Skip to content

Remove quickcheck dependency #7

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Bugfixes:

Other improvements:

- Remove the __quickcheck__ dependency from the package. Add __gen__ dependency and `Gen` modules. Add the __quickcheck__ dependency to the test `spago-dev.dhall`. (#7 by @jamesdbrock)

## [v2.0.0](https://github.com/purescript-contrib/purescript-int64/releases/tag/v1.0.0) - 2022-06-02

Expand Down
1 change: 1 addition & 0 deletions spago-dev.dhall
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ conf //
[ "spec"
, "aff"
, "quickcheck-laws"
, "quickcheck"
, "assert"
, "arrays"
, "foldable-traversable"
Expand Down
2 changes: 1 addition & 1 deletion spago.dhall
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
, "nullable"
, "functions"
, "maybe"
, "quickcheck"
, "gen"
, "unsafe-coerce"
]
, packages = ./packages.dhall
Expand Down
39 changes: 39 additions & 0 deletions src/Data/Int64.Gen.purs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
module Data.Int64.Gen
( chooseInt64
) where

import Prelude

import Control.Monad.Gen (class MonadGen, chooseInt)
import Data.Int64 (Int64, fromLowHighBits, toSigned, toUnsigned)
import Data.UInt64 (UInt64, rem)

topsigned :: UInt64
topsigned = toUnsigned top

-- | map
-- | signed bottom → zero
-- | zero → signed top + 1
-- | signed top -> unsigned top
mapToUInt64 :: Int64 -> UInt64
mapToUInt64 x = if x >= zero then toUnsigned x + topsigned + one else toUnsigned (top + x) + one

-- | map
-- | zero → signed bottom
-- | signed top + 1 → zero
-- | unsigned top → signed top
mapToInt64 :: UInt64 -> Int64
mapToInt64 x = if x > topsigned then toSigned (x - topsigned - one) else toSigned x - top - one

chooseInt64 :: forall m. MonadGen m => Int64 -> Int64 -> m Int64
chooseInt64 a b = do
random <- fromLowHighBits <$> chooseInt bottom top <*> chooseInt bottom top
-- We cannot subtract an Int64 from a greater Int64. But we can subtract a
-- UInt64 from a greater UInt64. So map the random Int64 to a UInt64,
-- constrain it between a and b, then map it back.
let
randomu = mapToUInt64 random
au = mapToUInt64 a
bu = mapToUInt64 b
pure $ mapToInt64 $ (randomu `rem` (bu - au)) + au

6 changes: 4 additions & 2 deletions src/Data/Int64.purs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ import Data.Int (Parity, Radix, decimal)
import Data.Int64.Internal as Internal
import Data.Maybe (Maybe)
import Data.UInt64 (UInt64)
import Test.QuickCheck (class Arbitrary)
import Unsafe.Coerce (unsafeCoerce)

-- | Signed two’s-complement 64-bit integer.
Expand All @@ -98,8 +97,11 @@ derive newtype instance Bounded Int64
derive newtype instance Semiring Int64
derive newtype instance Ring Int64
derive newtype instance CommutativeRing Int64

-- | The `EuclideanRing` instance provides a `mod` operator which
-- | is only lawful if the *divisor* is in the `Int` range,
-- | *-2³¹ ≤ divisor ≤ 2³¹⁻¹*.
derive newtype instance EuclideanRing Int64
derive newtype instance Arbitrary Int64

-- | Creates an `Int64` from an `Int` value.
fromInt :: Int -> Int64
Expand Down
9 changes: 4 additions & 5 deletions src/Data/Int64/Internal.purs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,6 @@ import Data.Maybe (Maybe(..))
import Data.Nullable (Nullable)
import Data.Nullable as Nullable
import Data.Ord (abs)
import Test.QuickCheck.Arbitrary (class Arbitrary, arbitrary)

foreign import data Signedness :: Type

Expand Down Expand Up @@ -187,7 +186,8 @@ instance euclideanRingLong'Signed :: EuclideanRing (Long' Signed) where
degree = Int.floor <<< toNumber <<< abs
div l1 l2 =
(l1 - (l1 `mod` l2)) `quot` l2

-- https://github.com/purescript/purescript-prelude/blob/f4cad0ae8106185c9ab407f43cf9abf05c256af4/src/Data/EuclideanRing.js#L14
-- https://en.m.wikipedia.org/wiki/Modulo_operation
mod l1 l2 =
let
l2' = abs l2
Expand All @@ -199,9 +199,6 @@ instance euclideanRingLong'Unsigned :: EuclideanRing (Long' Unsigned) where
div = quot
mod = rem

instance arbitraryLong' :: SInfo s => Arbitrary (Long' s) where
arbitrary = fromLowHighBits <$> arbitrary <*> arbitrary

-- Constructors

signedLongFromInt :: Int -> Long' Signed
Expand Down Expand Up @@ -488,6 +485,8 @@ foreign import lessThan_ :: Long -> Fn1 Long Boolean
foreign import lessThanOrEqual_ :: Long -> Fn1 Long Boolean

-- | Returns this Long modulo the specified.
-- |
-- | The foreign implementation only works when the divisor is in the Int range.
foreign import modulo_ :: Long -> Fn1 Long Long

-- | Returns the product of this and the specified Long.
Expand Down
12 changes: 12 additions & 0 deletions src/Data/UInt64.Gen.purs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module Data.UInt64.Gen
( chooseUInt64
) where

import Prelude
import Control.Monad.Gen (class MonadGen, chooseInt)
import Data.UInt64 (UInt64, fromLowHighBits, rem)

chooseUInt64 :: forall m. MonadGen m => UInt64 -> UInt64 -> m UInt64
chooseUInt64 a b = do
random <- fromLowHighBits <$> chooseInt bottom top <*> chooseInt bottom top
pure $ (random `rem` (b - a)) + a
2 changes: 0 additions & 2 deletions src/Data/UInt64.purs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ import Prelude
import Data.Int (Parity, Radix, decimal)
import Data.Int64.Internal as Internal
import Data.Maybe (Maybe)
import Test.QuickCheck (class Arbitrary)

-- | Unsigned 64-bit integer.
newtype UInt64 = UInt64 (Internal.Long' Internal.Unsigned)
Expand All @@ -86,7 +85,6 @@ derive newtype instance Semiring UInt64
derive newtype instance Ring UInt64
derive newtype instance CommutativeRing UInt64
derive newtype instance EuclideanRing UInt64
derive newtype instance Arbitrary UInt64

-- | Creates a `UInt64` from an `Int` value.
fromInt :: Int -> Maybe UInt64
Expand Down
89 changes: 42 additions & 47 deletions test/Data/Long/InternalSpec.purs
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,22 @@ import Control.Monad.Gen (chooseInt)
import Data.Int (Parity(..), Radix, binary, decimal, hexadecimal, octal, radix)
import Data.Int64 (Int64)
import Data.Int64 as Int64
import Data.Int64.Gen (chooseInt64)
import Data.Int64.Internal (class SInfo, Long', SignProxy(..), Signed, Unsigned)
import Data.Int64.Internal as Internal
import Data.Maybe (Maybe(..), isJust, isNothing)
import Data.Number as Number
import Data.Ord (abs)
import Data.Traversable (traverse_)
import Data.UInt64 (UInt64)
import Data.UInt64 (UInt64)
import Data.UInt64 as UInt64
import Data.UInt64.Gen (chooseUInt64)
import Effect.Aff (Aff)
import Effect.Class (liftEffect)
import Test.QuickCheck (class Arbitrary, class Testable, arbitrary, quickCheck)
import Test.QuickCheck.Laws.Data (checkCommutativeRing, checkEq, checkEuclideanRing, checkOrd, checkRing, checkSemiring)
import Test.QuickCheck (class Arbitrary, class Testable, arbitrary, quickCheck, (<?>))
import Test.QuickCheck.Laws.Data (checkBoundedGen, checkCommutativeRingGen, checkEqGen, checkEuclideanRingGen, checkOrdGen, checkRingGen, checkSemiringGen)
import Test.Spec (Spec, describe, it)
import Test.Spec.Assertions (shouldEqual, shouldSatisfy)
import Type.Proxy (Proxy(..))

internalSpec :: Spec Unit
internalSpec = do
Expand All @@ -33,19 +33,23 @@ internalSpec = do
longSpec :: Spec Unit
longSpec = describe "Long" do
it "should follow laws" $ liftEffect do
checkEq prxSignedLong
checkOrd prxSignedLong
checkSemiring prxSignedLong
checkRing prxSignedLong
checkCommutativeRing prxSignedLong
-- since degree is only up to int, we can only check for IntInLong
checkEuclideanRing prxIntInSignedLong

checkEq prxUnsignedLong
checkOrd prxUnsignedLong
checkSemiring prxUnsignedLong
checkRing prxUnsignedLong
checkCommutativeRing prxUnsignedLong
checkEqGen $ chooseInt64 bottom top
checkOrdGen $ chooseInt64 bottom top
checkBoundedGen $ chooseInt64 bottom top
checkSemiringGen $ chooseInt64 bottom top
checkRingGen $ chooseInt64 bottom top
checkCommutativeRingGen $ chooseInt64 bottom top
-- `mod` is only lawful if the divisor is in 32-bit Int range.
checkEuclideanRingGen $ chooseInt64 (Int64.fromInt bottom) (Int64.fromInt top)

checkEqGen $ chooseUInt64 bottom top
checkOrdGen $ chooseUInt64 bottom top
checkBoundedGen $ chooseUInt64 bottom top
checkSemiringGen $ chooseUInt64 bottom top
checkRingGen $ chooseUInt64 bottom top
checkCommutativeRingGen $ chooseUInt64 bottom top
-- `mod` is only lawful if the divisor is in 32-bit Int range.
checkEuclideanRingGen $ chooseUInt64 zero (UInt64.unsafeFromInt top)

it "should be built from high and low bits" do
quickCheck' \high low ->
Expand All @@ -67,10 +71,10 @@ longSpec = describe "Long" do
Internal.unsignedLongFromInt (-1) `shouldSatisfy` isNothing

it "should convert to strings" $ do
quickCheck' \(Radix' r) l ->
quickCheck' \(Radix' r) (Int64' l) ->
readSigned r (Int64.toStringAs r l) == Just l

quickCheck' \(Radix' r) l ->
quickCheck' \(Radix' r) (UInt64' l) ->
readUnsigned r (UInt64.toStringAs r l) == Just l

it "should convert numbers" $ do
Expand Down Expand Up @@ -111,11 +115,16 @@ longSpec = describe "Long" do
]

it "should determine odd/even" do
quickCheck' \(l :: Int64) -> (Int64.parity l == Even) == (l `mod` (Int64.fromInt 2) == zero)
quickCheck' \(l :: UInt64) -> (UInt64.parity l == Even) == (l `mod` (UInt64.unsafeFromInt 2) == zero)
quickCheck' \(Int64' l) -> (Int64.parity l == Even) == (l `mod` (Int64.fromInt 2) == zero)
quickCheck' \(UInt64' l) -> (UInt64.parity l == Even) == (l `mod` (UInt64.unsafeFromInt 2) == zero)

it "Int64 should always have positive mods" do
-- `mod` is only lawful if the divisor is in 32-bit Int range.
quickCheck' \(Int64' l1) l2 -> (l1 `mod` Int64.fromInt l2) > zero <?> show l1 <> " `mod` " <> show l2

it "should always have positive mods" do
quickCheck' \(l1 :: Int64) l2 -> (_ > zero) $ l1 `mod` l2
it "UInt64 should always have positive mods" do
-- `mod` is only lawful if the divisor is in 32-bit Int range.
quickCheck' \(UInt64' l1) l2 -> (l1 `mod` UInt64.unsafeFromInt (abs l2)) > zero <?> show l1 <> " `mod` " <> show l2

it "should div, quot, mod, rem by 0 be 0" do
traverse_ (\f -> f (Internal.signedLongFromInt 2) zero `shouldEqual` zero)
Expand Down Expand Up @@ -191,36 +200,12 @@ readUnsigned = UInt64.fromStringAs
i2lS :: Int -> Int64
i2lS = Int64.fromInt

-- i2lU :: Int -> UInt64
-- i2lU = Internal.unsafeFromInt

prxSignedLong :: Proxy Int64
prxSignedLong = Proxy

prxUnsignedLong :: Proxy UInt64
prxUnsignedLong = Proxy

prxIntInSignedLong :: Proxy IntInSignedLong
prxIntInSignedLong = Proxy

signedProxy :: SignProxy Signed
signedProxy = SignProxy

unsignedProxy :: SignProxy Unsigned
unsignedProxy = SignProxy

-- Helper for Longs within the Int range
newtype IntInSignedLong = IntInSignedLong Int64

instance arbitraryIntInSignedLong :: Arbitrary IntInSignedLong where
arbitrary = IntInSignedLong <<< Int64.fromInt <$> arbitrary

derive newtype instance eqIntInSignedLong :: Eq IntInSignedLong
derive newtype instance semiringIntInSignedLong :: Semiring IntInSignedLong
derive newtype instance ringIntInSignedLong :: Ring IntInSignedLong
derive newtype instance commutativeRingIntInSignedLong :: CommutativeRing IntInSignedLong
derive newtype instance eucledianRingIntInSignedLong :: EuclideanRing IntInSignedLong

newtype Radix' = Radix' Radix

instance arbitraryRadix' :: Arbitrary Radix' where
Expand All @@ -229,5 +214,15 @@ instance arbitraryRadix' :: Arbitrary Radix' where
Just r -> pure (Radix' r)
Nothing -> arbitrary

newtype Int64' = Int64' Int64

instance Arbitrary Int64' where
arbitrary = Int64' <$> chooseInt64 bottom top

newtype UInt64' = UInt64' UInt64

instance Arbitrary UInt64' where
arbitrary = UInt64' <$> chooseUInt64 bottom top

quickCheck' :: forall a. Testable a => a -> Aff Unit
quickCheck' = liftEffect <<< quickCheck