bin-0.1.4: src/Data/Bin/Pos.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE EmptyCase #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE Safe #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE UndecidableInstances #-}
module Data.Bin.Pos (
Pos (..), PosP,
-- * Top & Pop
top, pop,
-- * Showing
explicitShow,
explicitShowsPrec,
-- * Conversions
toNatural,
-- * Interesting
absurd,
boring,
-- * Weakening (succ)
weakenRight1,
-- * Universe
universe,
) where
import Prelude
(Bounded (..), Eq, Int, Integer, Ord (..), Show (..), ShowS, String, fmap, fromIntegral, map, showParen,
showString, ($), (.))
import Control.DeepSeq (NFData (..))
import Data.Bin (Bin (..), BinP (..))
import Data.BinP.PosP (PosP (..))
import Data.EqP (EqP (..))
import Data.GADT.Show (GShow (..))
import Data.OrdP (OrdP (..))
import Data.Typeable (Typeable)
import Numeric.Natural (Natural)
import qualified Data.BinP.PosP as PP
import qualified Data.Boring as Boring
import qualified Data.Type.Bin as B
import qualified Data.Type.BinP as BP
import qualified Test.QuickCheck as QC
import Data.Type.Bin
-- $setup
-- >>> import Prelude (map, putStrLn, Ord (..), Bounded (..), ($), (.), print)
-- >>> import Data.Foldable (traverse_)
-- >>> import Data.Type.Bin
-- >>> import Data.EqP (eqp)
-- >>> import Data.OrdP (comparep)
-- >>> :set -XTypeApplications
-------------------------------------------------------------------------------
-- Data
-------------------------------------------------------------------------------
-- | 'Pos' is to 'Bin' is what 'Fin' is to 'Nat'.
--
-- The name is picked, as the lack of better alternatives.
--
data Pos (b :: Bin) where
Pos :: PosP b -> Pos ('BP b)
deriving (Typeable)
deriving instance Eq (Pos b)
deriving instance Ord (Pos b)
-------------------------------------------------------------------------------
-- Instances
-------------------------------------------------------------------------------
instance Show (Pos b) where
showsPrec d = showsPrec d . toNatural
-- | @since 0.1.3
instance GShow Pos where
gshowsPrec = showsPrec
-- |
--
-- >>> eqp (top :: Pos Bin4) (top :: Pos Bin6)
-- True
--
-- >>> let xs = universe @Bin4; ys = universe @Bin6 in traverse_ print [ [ eqp x y | y <- ys ] | x <- xs ]
-- [True,False,False,False,False,False]
-- [False,True,False,False,False,False]
-- [False,False,True,False,False,False]
-- [False,False,False,True,False,False]
--
-- @since 0.1.3
instance EqP Pos where
eqp (Pos x) (Pos y) = eqp x y
-- |
--
-- >>> let xs = universe @Bin4; ys = universe @Bin6 in traverse_ print [ [ comparep x y | y <- ys ] | x <- xs ]
-- [EQ,LT,LT,LT,LT,LT]
-- [GT,EQ,LT,LT,LT,LT]
-- [GT,GT,EQ,LT,LT,LT]
-- [GT,GT,GT,EQ,LT,LT]
--
-- @since 0.1.3
--
instance OrdP Pos where
comparep (Pos x) (Pos y) = comparep x y
-- |
--
-- >>> minBound < (maxBound :: Pos Bin5)
-- True
instance (SBinPI n, b ~ 'BP n) => Bounded (Pos b) where
minBound = Pos minBound
maxBound = Pos maxBound
-- | @since 0.1.2
instance NFData (Pos b) where
rnf (Pos p) = rnf p
-------------------------------------------------------------------------------
-- QuickCheck
-------------------------------------------------------------------------------
instance (SBinPI n, b ~ 'BP n) => QC.Arbitrary (Pos b) where
arbitrary = fmap Pos QC.arbitrary
instance QC.CoArbitrary (Pos b) where
coarbitrary = QC.coarbitrary . (fromIntegral :: Natural -> Integer) . toNatural
instance (SBinPI n, b ~ 'BP n) => QC.Function (Pos b) where
function = QC.functionMap (\(Pos p) -> p) Pos
-------------------------------------------------------------------------------
-- Showing
-------------------------------------------------------------------------------
explicitShow :: Pos b -> String
explicitShow b = explicitShowsPrec 0 b ""
explicitShowsPrec :: Int -> Pos b ->ShowS
explicitShowsPrec d (Pos b)
= showParen (d > 10)
$ showString "Pos "
. PP.explicitShowsPrec 11 b
-------------------------------------------------------------------------------
-- Conversions
-------------------------------------------------------------------------------
-- | Convert 'Pos' to 'Natural'
--
-- >>> map toNatural (universe :: [Pos Bin7])
-- [0,1,2,3,4,5,6]
toNatural :: Pos b -> Natural
toNatural (Pos p) = PP.toNatural p
-------------------------------------------------------------------------------
-- Interesting
-------------------------------------------------------------------------------
-- | @'Pos' 'BZ'@ is not inhabited.
absurd :: Pos 'BZ -> b
absurd x = case x of {}
-- | Counting to one is boring
--
-- >>> boring
-- 0
boring :: Pos ('BP 'BE)
boring = minBound
-------------------------------------------------------------------------------
-- min and max, tricky, we need Pred.
-------------------------------------------------------------------------------
-- TBW
-------------------------------------------------------------------------------
-- top & pop
-------------------------------------------------------------------------------
-- | 'top' and 'pop' serve as 'FZ' and 'FS', with types specified so
-- type-inference works backwards from the result.
--
-- >>> top :: Pos Bin4
-- 0
--
-- >>> pop (pop top) :: Pos Bin4
-- 2
--
-- >>> pop (pop top) :: Pos Bin9
-- 2
--
top :: SBinPI b => Pos ('BP b)
top = minBound
-- | See 'top'.
pop :: (SBinPI a, Pred b ~ 'BP a, BP.Succ a ~ b) => Pos ('BP a) -> Pos ('BP b)
pop = weakenRight1
-------------------------------------------------------------------------------
-- Append and Split
-------------------------------------------------------------------------------
-- | Like 'FS' for 'Fin'.
--
-- Some tests:
--
-- >>> map weakenRight1 $ (universe :: [Pos Bin2])
-- [1,2]
--
-- >>> map weakenRight1 $ (universe :: [Pos Bin3])
-- [1,2,3]
--
-- >>> map weakenRight1 $ (universe :: [Pos Bin4])
-- [1,2,3,4]
--
-- >>> map weakenRight1 $ (universe :: [Pos Bin5])
-- [1,2,3,4,5]
--
-- >>> map weakenRight1 $ (universe :: [Pos Bin6])
-- [1,2,3,4,5,6]
--
weakenRight1 :: SBinPI b => Pos ('BP b) -> Pos (Succ'' b)
weakenRight1 (Pos b) = Pos (PP.weakenRight1 b)
-------------------------------------------------------------------------------
-- Boring
-------------------------------------------------------------------------------
-- | @since 0.1.2
instance b ~ 'BP 'BE => Boring.Boring (Pos b) where
boring = boring
-- | @since 0.1.2
instance b ~ 'BZ => Boring.Absurd (Pos b) where
absurd = absurd
-------------------------------------------------------------------------------
-- Universe
-------------------------------------------------------------------------------
-- | Universe, i.e. all @[Pos b]@
--
-- >>> universe :: [Pos Bin9]
-- [0,1,2,3,4,5,6,7,8]
--
-- >>> traverse_ (putStrLn . explicitShow) (universe :: [Pos Bin5])
-- Pos (PosP (Here WE))
-- Pos (PosP (There1 (There0 (AtEnd 0b00))))
-- Pos (PosP (There1 (There0 (AtEnd 0b01))))
-- Pos (PosP (There1 (There0 (AtEnd 0b10))))
-- Pos (PosP (There1 (There0 (AtEnd 0b11))))
--
universe :: forall b. B.SBinI b => [Pos b]
universe = case B.sbin :: SBin b of
B.SBZ -> []
B.SBP -> map Pos PP.universe