module Combinatorics.Battleship.Count.Counter (
C,
Composed,
zero,
one,
add,
sum,
toInteger,
propAdd,
) where
import Control.Monad (liftM2, )
import qualified Data.List as List
import Data.Bits (shiftL, )
import Data.Word (Word8, Word32, Word64, )
import Foreign.Storable
(Storable, sizeOf, alignment,
poke, peek, pokeByteOff, peekByteOff, )
import qualified Test.QuickCheck as QC
import Prelude hiding (sum, toInteger, )
class C a where
zero, one :: a
add :: a -> a -> a
class (C a, Ord a) => Integ a where
toInteger :: a -> Integer
rangeSize :: a -> Integer
instance C Word8 where
zero = 0; one = 1
add = (+)
instance Integ Word8 where
toInteger = fromIntegral
rangeSize _ = shiftL 1 8
instance C Word32 where
zero = 0; one = 1
add = (+)
instance Integ Word32 where
toInteger = fromIntegral
rangeSize _ = shiftL 1 32
instance C Word64 where
zero = 0; one = 1
add = (+)
instance Integ Word64 where
toInteger = fromIntegral
rangeSize _ = shiftL 1 64
sum :: (C a) => [a] -> a
sum = List.foldl' add zero
data Composed hi lo = Composed !hi !lo
deriving (Eq, Ord)
instance (C hi, C lo, Ord lo) => C (Composed hi lo) where
zero = Composed zero zero
one = Composed zero one
add (Composed xh xl) (Composed yh yl) =
let zh = add xh yh; zl = add xl yl
in Composed (if zl < xl then add zh one else zh) zl
instance (Integ hi, Integ lo) => Integ (Composed hi lo) where
rangeSize ~(Composed hi lo) = rangeSize hi * rangeSize lo
toInteger (Composed hi lo) =
toInteger hi * rangeSize lo + toInteger lo
instance (Integ hi, Integ lo) => Show (Composed hi lo) where
show = show . toInteger
instance (Storable a, Storable b) => Storable (Composed a b) where
sizeOf ~(Composed a b) = sizeOf a + sizeOf b
alignment ~(Composed a b) = alignment a `lcm` alignment b
poke ptr (Composed a b) = do
pokeByteOff ptr 0 a
pokeByteOff ptr (sizeOf a) b
peek ptr = do
a <- peekByteOff ptr 0
b <- peekByteOff ptr (sizeOf a)
return $ Composed a b
instance (QC.Arbitrary a, QC.Arbitrary b) => QC.Arbitrary (Composed a b) where
arbitrary = liftM2 Composed QC.arbitrary QC.arbitrary
shrink (Composed hi lo) = map (uncurry Composed) $ QC.shrink (hi,lo)
propAdd ::
Composed (Composed Word64 Word32) (Composed Word32 Word32) ->
Composed (Composed Word64 Word32) (Composed Word32 Word32) ->
Bool
propAdd a b =
toInteger (add a b) == mod (toInteger a + toInteger b) (rangeSize a)