{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveDataTypeable #-}
#if __GLASGOW_HASKELL__ < 710
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE StandaloneDeriving #-}
#endif
module Data.BinP (
BinP(..),
cata,
toNatural,
fromNatural,
toNat,
explicitShow,
explicitShowsPrec,
predMaybe,
binP1, binP2, binP3, binP4, binP5, binP6, binP7, binP8, binP9,
) where
import Control.DeepSeq (NFData (..))
import Data.Bits (Bits (..))
import Data.Data (Data)
import Data.Hashable (Hashable (..))
import Data.Monoid (mappend)
import Data.Nat (Nat (..))
import Data.Typeable (Typeable)
import GHC.Exception (ArithException (..), throw)
import Numeric.Natural (Natural)
import qualified Data.Nat as N
import qualified Test.QuickCheck as QC
data BinP
= BE
| B0 BinP
| B1 BinP
deriving (Eq, Typeable, Data)
#if __GLASGOW_HASKELL__ < 710
deriving instance Typeable 'BE
deriving instance Typeable 'B0
deriving instance Typeable 'B1
#endif
instance Ord BinP where
compare BE BE = EQ
compare BE _ = LT
compare _ BE = GT
compare (B0 a) (B0 b) = compare a b
compare (B1 a) (B1 b) = compare a b
compare (B0 a) (B1 b) = compare a b `mappend` LT
compare (B1 a) (B0 b) = compare a b `mappend` GT
instance Show BinP where
showsPrec d = showsPrec d . toNatural
instance Num BinP where
fromInteger = fromNatural . fromInteger
BE + b = succ b
b + BE = succ b
B0 a + B0 b = B0 (a + b)
B0 a + B1 b = B1 (a + b)
B1 a + B0 b = B1 (a + b)
B1 a + B1 b = B0 (succ (a + b))
BE * b = b
a * BE = a
B0 a * B0 b = B0 (B0 (a * b))
B1 a * B0 b = B0 (B0 (a * b)) + B0 b
B0 a * B1 b = B0 (B0 (a * b)) + B0 a
B1 a * B1 b = B1 (B0 (a * b)) + B0 a + B0 b
abs = id
signum _ = BE
negate _ = error "negate @Bin"
instance Real BinP where
toRational = toRational . toInteger
instance Integral BinP where
toInteger = toInteger . toNatural
quotRem _ _ = error "quotRem @Bin is not implemented"
instance Enum BinP where
succ BE = B0 BE
succ (B0 n) = B1 n
succ (B1 n) = B0 (succ n)
pred n = case predMaybe n of
Nothing -> throw Underflow
Just m -> m
toEnum n = case compare n 1 of
LT -> throw Underflow
EQ -> BE
GT -> case n `divMod` 2 of
(m, 0) -> B0 (toEnum m)
(m, _) -> B1 (toEnum m)
fromEnum BE = 1
fromEnum (B0 n) = 2 * fromEnum n
fromEnum (B1 n) = succ (2 * fromEnum n)
instance NFData BinP where
rnf BE = ()
rnf (B0 n) = rnf n
rnf (B1 n) = rnf n
instance Hashable BinP where
hashWithSalt = undefined
predMaybe :: BinP -> Maybe BinP
predMaybe BE = Nothing
predMaybe (B1 n) = Just (B0 n)
predMaybe (B0 n) = Just (mult2Plus1 (predMaybe n))
where
mult2Plus1 :: Maybe BinP -> BinP
mult2Plus1 = maybe BE B1
instance Bits BinP where
B0 a .|. B0 b = B0 (a .|. b)
B0 a .|. B1 b = B1 (a .|. b)
B1 a .|. B0 b = B1 (a .|. b)
B1 a .|. B1 b = B1 (a .|. b)
BE .|. B0 b = B1 b
BE .|. B1 b = B1 b
B0 b .|. BE = B1 b
B1 b .|. BE = B1 b
BE .|. BE = BE
bit n
| n <= 0 = BE
| otherwise = B0 (bit (pred n))
shiftL b n
| n <= 0 = b
| otherwise = shiftL (B0 b) (pred n)
rotateL = shiftL
popCount = go 1 where
go !acc BE = acc
go !acc (B0 b) = go acc b
go !acc (B1 b) = go (succ acc) b
testBit BE 0 = True
testBit (B0 _) 0 = False
testBit (B1 _) 0 = True
testBit BE _ = False
testBit (B0 b) n = testBit b (pred n)
testBit (B1 b) n = testBit b (pred n)
zeroBits = error "zeroBits @BinP is undefined"
clearBit _ _ = error "clearBit @BinP is undefined"
complementBit _ _ = error "complementBit @BinP is undefined"
xor _ _ = error "xor @BinP is undefined"
(.&.) _ _ = error "(.&.) @BinP is undefined"
shiftR _ = error "shiftR @BinP is undefined"
rotateR _ = error "shiftL @BinP is undefined"
complement _ = error "compelement @BinP is undefined"
bitSizeMaybe _ = Nothing
bitSize _ = error "bitSize @BinP is undefined"
isSigned _ = True
instance QC.Arbitrary BinP where
arbitrary = do
bs <- QC.arbitrary :: QC.Gen [Bool]
return (foldr (\b -> if b then B1 else B0) BE bs)
shrink BE = []
shrink (B1 b) = b : B0 b : map B1 (QC.shrink b)
shrink (B0 b) = b : map B0 (QC.shrink b)
instance QC.CoArbitrary BinP where
coarbitrary = QC.coarbitrary . sp where
sp :: BinP -> Maybe (Either BinP BinP)
sp BE = Nothing
sp (B0 b) = Just (Left b)
sp (B1 b) = Just (Right b)
instance QC.Function BinP where
function = QC.functionMap sp (maybe BE (either B0 B1)) where
sp :: BinP -> Maybe (Either BinP BinP)
sp BE = Nothing
sp (B0 b) = Just (Left b)
sp (B1 b) = Just (Right b)
explicitShow :: BinP -> String
explicitShow n = explicitShowsPrec 0 n ""
explicitShowsPrec :: Int -> BinP -> ShowS
explicitShowsPrec _ BE
= showString "BE"
explicitShowsPrec d (B0 n)
= showParen (d > 10)
$ showString "B0 "
. explicitShowsPrec 11 n
explicitShowsPrec d (B1 n)
= showParen (d > 10)
$ showString "B1 "
. explicitShowsPrec 11 n
toNatural :: BinP -> Natural
toNatural BE = 1
toNatural (B0 n) = 2 * toNatural n
toNatural (B1 n) = 2 * toNatural n + 1
fromNatural :: Natural -> BinP
fromNatural 0 = throw Underflow
fromNatural 1 = BE
fromNatural n = case n `divMod` 2 of
(m, 0) -> B0 (fromNatural m)
(m, _) -> B1 (fromNatural m)
cata
:: a
-> (a -> a)
-> (a -> a)
-> BinP
-> a
cata z o i = go where
go BE = z
go (B0 b) = o (go b)
go (B1 b) = i (go b)
toNat :: BinP -> Nat
toNat = cata (S Z) o i where
o :: Nat -> Nat
o = N.cata Z (S . S)
i :: Nat -> Nat
i = S . o
binP1, binP2, binP3, binP4, binP5, binP6, binP7, binP8, binP9 :: BinP
binP1 = BE
binP2 = B0 BE
binP3 = B1 BE
binP4 = B0 binP2
binP5 = B1 binP2
binP6 = B0 binP3
binP7 = B1 binP3
binP8 = B0 binP4
binP9 = B1 binP4