Main.hs 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. {-
  2. - Copyright (C) 2019 Koz Ross <koz.ross@retro-freedom.nz>
  3. -
  4. - This program is free software: you can redistribute it and/or modify
  5. - it under the terms of the GNU General Public License as published by
  6. - the Free Software Foundation, either version 3 of the License, or
  7. - (at your option) any later version.
  8. -
  9. - This program is distributed in the hope that it will be useful,
  10. - but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. - GNU General Public License for more details.
  13. -
  14. - You should have received a copy of the GNU General Public License
  15. - along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. -}
  17. {-# LANGUAGE AllowAmbiguousTypes #-}
  18. {-# LANGUAGE BangPatterns #-}
  19. {-# LANGUAGE DeriveAnyClass #-}
  20. {-# LANGUAGE DeriveGeneric #-}
  21. {-# LANGUAGE DerivingVia #-}
  22. {-# LANGUAGE MagicHash #-}
  23. {-# LANGUAGE OverloadedStrings #-}
  24. {-# LANGUAGE ScopedTypeVariables #-}
  25. {-# LANGUAGE TypeApplications #-}
  26. {-# LANGUAGE TypeInType #-}
  27. module Main where
  28. -- base
  29. import Data.Int (Int8)
  30. import Data.Kind (Type)
  31. import Data.Word (Word8, Word16, Word64)
  32. import Foreign.Storable (Storable)
  33. import GHC.Exts (proxy#)
  34. import GHC.Generics (Generic)
  35. import GHC.TypeNats (Nat, KnownNat, natVal')
  36. -- binary
  37. import Data.Binary (Binary)
  38. -- deepseq
  39. import Control.DeepSeq (NFData)
  40. -- finitary
  41. import Data.Finitary (Finitary(..))
  42. -- finitary-derive
  43. import Data.Finitary.Finiteness (Finiteness(..))
  44. import Data.Finitary.PackBytes (PackBytes)
  45. import Data.Finitary.PackWords (PackWords)
  46. import Data.Finitary.PackInto (PackInto)
  47. import qualified Data.Finitary.PackBits as Safe
  48. import qualified Data.Finitary.PackBits.Unsafe as Unsafe
  49. import qualified Data.Finitary.PackBytes as PackBytes
  50. import qualified Data.Finitary.PackWords as PackWords
  51. -- finite-typelits
  52. import Data.Finite (Finite)
  53. -- hashable
  54. import Data.Hashable (Hashable(..))
  55. -- hedgehog
  56. import Hedgehog
  57. import qualified Hedgehog.Gen as G
  58. import qualified Hedgehog.Range as R
  59. -- hedgehog-classes
  60. import Hedgehog.Classes
  61. -- vector
  62. import Data.Vector.Unboxed (Unbox)
  63. --------------------------------------------------------------------------------
  64. data Small = A | B | C Int8 | D | E Bool
  65. deriving (Eq, Show, Generic, Finitary)
  66. deriving (Ord, Bounded, NFData, Hashable, Binary) via (Finiteness Small)
  67. data Medium = Bar | Baz Word8 Small | Quux Word16
  68. deriving (Eq, Show, Generic, Finitary)
  69. deriving (Ord, Bounded, NFData, Hashable, Binary) via (Finiteness Medium)
  70. data Big = Big1 Word64 Word64 | Big2 Int Medium Small Medium
  71. deriving (Eq, Show, Generic, Finitary)
  72. deriving (Ord, Bounded, NFData, Hashable, Binary) via (Finiteness Big)
  73. genSmall :: MonadGen m => m Small
  74. genSmall = do
  75. c <- G.element @_ @Word [ 0, 1, 2, 3, 4 ]
  76. case c of
  77. 0 -> pure A
  78. 1 -> pure B
  79. 2 -> do
  80. i <- G.int8 (R.linear 0 maxBound)
  81. pure ( C i )
  82. 3 -> pure D
  83. _ -> do
  84. b <- G.bool
  85. pure ( E b )
  86. genMedium :: MonadGen m => m Medium
  87. genMedium = do
  88. c <- G.element @_ @Word [ 0, 1, 2 ]
  89. case c of
  90. 0 -> pure Bar
  91. 1 -> do
  92. w1 <- G.word8 (R.linear 0 maxBound)
  93. s2 <- genSmall
  94. pure ( Baz w1 s2 )
  95. _ -> do
  96. w1 <- G.word16 (R.linear 0 maxBound)
  97. pure ( Quux w1 )
  98. genBig :: MonadGen m => m Big
  99. genBig = do
  100. c <- G.element @_ @Word [ 0, 1 ]
  101. case c of
  102. 0 -> do
  103. w1 <- G.word64 (R.linear 0 maxBound)
  104. w2 <- G.word64 (R.linear 0 maxBound)
  105. pure ( Big1 w1 w2 )
  106. _ -> do
  107. i1 <- G.int (R.linear 0 maxBound)
  108. med2 <- genMedium
  109. small3 <- genSmall
  110. med4 <- genMedium
  111. pure ( Big2 i1 med2 small3 med4 )
  112. -- Generators
  113. choose :: forall (a :: Type) m . (MonadGen m, Finitary a) => m a
  114. choose = fromFinite <$> chooseFinite
  115. chooseFinite :: forall (n :: Nat) m . (KnownNat n, MonadGen m) => m (Finite n)
  116. chooseFinite = fromIntegral <$> G.integral (R.linear 0 limit)
  117. where limit = subtract @Integer 1 . fromIntegral $ natVal' @n proxy#
  118. finitenessLaws :: (Show a, Binary a, Ord a) => Gen a -> [Laws]
  119. finitenessLaws p = [binaryLaws p, ordLaws p]
  120. packLaws :: (Eq a, Show a, Storable a) => Gen a -> [Laws]
  121. packLaws p = [storableLaws p]
  122. vectorLaws :: (Eq a, Show a, Unbox a) => Gen a -> [Laws]
  123. vectorLaws p = [muvectorLaws p]
  124. ordIsMonotonic :: forall (a :: Type) (t :: Type -> Type) .
  125. (Finitary a, Show a, Ord a, Ord (t a)) =>
  126. (a -> t a) -> Property
  127. ordIsMonotonic f = property $ do x <- forAll $ choose @a
  128. y <- forAll $ choose @a
  129. (x < y) === (f x < f y)
  130. roundTrips :: forall (a :: Type) (t :: Type -> Type) .
  131. (Finitary a, Show a, Ord a) =>
  132. Gen a -> (a -> t a) -> (t a -> a) -> Property
  133. roundTrips gen pack unpack = property $ do
  134. a <- forAll $ gen
  135. case pack a of
  136. !packed -> case unpack packed of
  137. roundTripped -> a === roundTripped
  138. finitenessTests :: [(String,[Laws])]
  139. finitenessTests =
  140. [ ("Small Finiteness", finitenessLaws @Small choose)
  141. , ("Medium Finiteness", finitenessLaws @Medium choose)
  142. , ("Big Finiteness", finitenessLaws @Big choose)
  143. ]
  144. packTests :: [(String,[Laws])]
  145. packTests =
  146. [ ("Small PackBytes" , packLaws @(PackBytes Small ) choose)
  147. , ("Small PackWords" , packLaws @(PackWords Small ) choose)
  148. , ("Medium PackBytes" , packLaws @(PackBytes Medium ) choose)
  149. , ("Medium PackWords" , packLaws @(PackWords Medium ) choose)
  150. , ("Big PackBytes" , packLaws @(PackBytes Big ) choose)
  151. , ("Big PackWords" , packLaws @(PackWords Big ) choose)
  152. , ("Small packed into Word64", packLaws @(PackInto Small Word64) choose)
  153. ]
  154. vectorTests :: [(String,[Laws])]
  155. vectorTests =
  156. [ ("Small PackBits" , vectorLaws @(Safe.PackBits Small ) choose)
  157. , ("Small unsafe PackBits" , vectorLaws @(Unsafe.PackBits Small ) choose)
  158. , ("Small PackBytes" , vectorLaws @(PackBytes Small ) choose)
  159. , ("Small PackWords" , vectorLaws @(PackWords Small ) choose)
  160. , ("Medium PackBits" , vectorLaws @(Safe.PackBits Medium) choose)
  161. , ("Medium unsafe PackBits", vectorLaws @(Unsafe.PackBits Medium) choose)
  162. , ("Medium PackBytes" , vectorLaws @(PackBytes Medium) choose)
  163. , ("Medium PackWords" , vectorLaws @(PackWords Medium) choose)
  164. , ("Big PackBits" , vectorLaws @(Safe.PackBits Big ) choose)
  165. , ("Big unsafe PackBits" , vectorLaws @(Unsafe.PackBits Big ) choose)
  166. , ("Big PackBytes" , vectorLaws @(PackBytes Big ) choose)
  167. , ("Big PackWords" , vectorLaws @(PackWords Big ) choose)
  168. ]
  169. monotonicTests :: Group
  170. monotonicTests = Group "Monotonicity"
  171. [ ("Small PackBits" , ordIsMonotonic @Small Safe.Packed)
  172. , ("Small unsafe PackBits" , ordIsMonotonic @Small Unsafe.Packed)
  173. , ("Small PackBytes" , ordIsMonotonic @Small PackBytes.Packed)
  174. , ("Small PackWords" , ordIsMonotonic @Small PackWords.Packed)
  175. , ("Medium PackBits" , ordIsMonotonic @Medium Safe.Packed)
  176. , ("Medium unsafe PackBits", ordIsMonotonic @Medium Unsafe.Packed)
  177. , ("Medium PackBytes" , ordIsMonotonic @Medium PackBytes.Packed)
  178. , ("Medium PackWords" , ordIsMonotonic @Medium PackWords.Packed)
  179. , ("Big PackBits" , ordIsMonotonic @Big Safe.Packed)
  180. , ("Big unsafe PackBits" , ordIsMonotonic @Big Unsafe.Packed)
  181. , ("Big PackBytes" , ordIsMonotonic @Big PackBytes.Packed)
  182. , ("Big PackWords" , ordIsMonotonic @Big PackWords.Packed)
  183. ]
  184. roundTripTests :: Group
  185. roundTripTests = Group "Round-tripping"
  186. [ ("Small PackBits" , roundTrips @Small genSmall Safe.Packed ( \ ( Safe.Packed x ) -> x ) )
  187. , ("Small unsafe PackBits" , roundTrips @Small genSmall Unsafe.Packed ( \ ( Unsafe.Packed x ) -> x ) )
  188. , ("Small PackBytes" , roundTrips @Small genSmall PackBytes.Packed ( \ ( PackBytes.Packed x ) -> x ) )
  189. , ("Small PackWords" , roundTrips @Small genSmall PackWords.Packed ( \ ( PackWords.Packed x ) -> x ) )
  190. , ("Medium PackBits" , roundTrips @Medium genMedium Safe.Packed ( \ ( Safe.Packed x ) -> x ) )
  191. , ("Medium unsafe PackBits", roundTrips @Medium genMedium Unsafe.Packed ( \ ( Unsafe.Packed x ) -> x ) )
  192. , ("Medium PackBytes" , roundTrips @Medium genMedium PackBytes.Packed ( \ ( PackBytes.Packed x ) -> x ) )
  193. , ("Medium PackWords" , roundTrips @Medium genMedium PackWords.Packed ( \ ( PackWords.Packed x ) -> x ) )
  194. , ("Big PackBits" , roundTrips @Big genBig Safe.Packed ( \ ( Safe.Packed x ) -> x ) )
  195. , ("Big unsafe PackBits" , roundTrips @Big genBig Unsafe.Packed ( \ ( Unsafe.Packed x ) -> x ) )
  196. , ("Big PackBytes" , roundTrips @Big genBig PackBytes.Packed ( \ ( PackBytes.Packed x ) -> x ) )
  197. , ("Big PackWords" , roundTrips @Big genBig PackWords.Packed ( \ ( PackWords.Packed x ) -> x ) )
  198. ]
  199. checkTest :: Either [(String,[Laws])] Group -> IO Bool
  200. checkTest ( Left laws ) = lawsCheckMany laws
  201. checkTest ( Right group ) = checkParallel group
  202. main :: IO Bool
  203. main = and <$> traverse checkTest [ Left finitenessTests, Left packTests, Left vectorTests, Right monotonicTests, Right roundTripTests ]