packages feed

Fin-0.1.1.0: Data/Fin.hs

{-# LANGUAGE TypeApplications #-}

module Data.Fin where

import Prelude hiding (iterate)
import Control.Applicative
import Data.Ap
import Data.CList (CList (..))
import Data.Foldable
import Data.Function (on)
import Data.Functor.Compose
import Data.Maybe
import Data.Natural.Class
import Data.Peano (Peano)
import qualified Data.Peano as P
import Data.Semigroup (Endo (..))
import qualified Numeric.Natural as N
import qualified Text.ParserCombinators.ReadP as Read
import Text.ParserCombinators.ReadPrec (readP_to_Prec)
import Text.Read (Read (..))

data Fin :: Peano -> * where
    Zero :: Fin (P.Succ n)
    Succ :: Fin n -> Fin (P.Succ n)

deriving instance Eq (Fin n)
deriving instance Ord (Fin n)
deriving instance Show (Fin n)

instance Read (Fin P.Zero) where readPrec = empty
instance Read (Fin n) => Read (Fin (P.Succ n)) where
    readPrec = Succ <$ string "Succ" <*> readPrec <|> Zero <$ string "Zero"
      where string = readP_to_Prec . pure . Read.string

instance Bounded (Fin (P.Succ P.Zero)) where
    minBound = Zero
    maxBound = Zero

instance Bounded (Fin n) => Bounded (Fin (P.Succ n)) where
    minBound = Zero
    maxBound = Succ maxBound

instance Enum (Fin P.Zero) where
    toEnum _ = error "toEnum @(Fin Zero)"
    fromEnum = \ case
    succ = \ case
    pred = \ case

instance (Natural n, Enum (Fin n)) => Enum (Fin (P.Succ n)) where
    toEnum 0 = Zero
    toEnum n = Succ (toEnum (pred n))
    fromEnum Zero = 0
    fromEnum (Succ n) = succ (fromEnum n)
    enumFrom Zero = Zero : (Succ <$> toList enum)
    enumFrom (Succ n) = (tail . enumFrom . inj₁) n

enum :: Natural n => CList n (Fin n)
enum = ap $ natural (Ap Nil) (Ap (Zero :. (Succ <$> enum)))

instance Num (Fin P.Zero) where
    (+) = \ case
    (*) = \ case
    abs = id
    negate = \ case
    signum = \ case
    fromInteger _ = error "fromInteger @(Fin Zero)"

instance (Natural n, Num (Fin n)) => Num (Fin (P.Succ n)) where
    a + b = toFin $ ((+) @N.Natural `on` fromFin) a b
    a - b = toFin $ ((-) @  Integer `on` fromFin) a b
    a * b = toFin $ ((*) @N.Natural `on` fromFin) a b

    abs = id
    signum = lift₁ . appEndo . getCompose $
             natural @n (Compose . Endo $ \ case) (Compose . Endo $ pure Zero)

    fromInteger = toFin

inj₁ :: Fin n -> Fin (P.Succ n)
inj₁ Zero = Zero
inj₁ (Succ n) = Succ (inj₁ n)

lift₁ :: (Fin m -> Fin n) -> Fin (P.Succ m) -> Fin (P.Succ n)
lift₁ _ Zero = Zero
lift₁ f (Succ n) = Succ (f n)

fromFin :: Integral a => Fin n -> a
fromFin Zero = 0
fromFin (Succ n) = succ (fromFin n)

toFin :: ∀ n a . (Natural n, Integral a) => a -> Fin (P.Succ n)
toFin = fromJust . toFinMay . (`mod` getConst (iterate @n (+1) 1))

toFinMay :: (Natural n, Integral a) => a -> Maybe (Fin (P.Succ n))
toFinMay = getCompose . getCompose . getCompose $
           natural (Compose . Compose . Compose $ \ case 0 -> Just Zero
                                                         _ -> Nothing)
                   (Compose . Compose . Compose $ \ case 0 -> Just Zero
                                                         n -> Succ <$> toFinMay (n-1))