bin-0.1.3: src/Data/Bin/Pos.hs
{-# LANGUAGE CPP #-}
{-# 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.GADT.Show (GShow (..))
import Data.Typeable (Typeable)
import Numeric.Natural (Natural)
#if MIN_VERSION_some(1,0,5)
import Data.EqP (EqP (..))
import Data.OrdP (OrdP (..))
#endif
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
#if MIN_VERSION_some(1,0,5)
-- |
--
-- >>> 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
#endif
-- |
--
-- >>> 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