mirror of
https://github.com/idris-lang/Idris2.git
synced 2024-12-30 15:13:37 +03:00
179 lines
5.0 KiB
Idris
179 lines
5.0 KiB
Idris
module Data.Fin
|
|
|
|
import Data.List1
|
|
import public Data.Maybe
|
|
import Data.Nat
|
|
import Decidable.Equality.Core
|
|
|
|
%default total
|
|
|
|
||| Numbers strictly less than some bound. The name comes from "finite sets".
|
|
|||
|
|
||| It's probably not a good idea to use `Fin` for arithmetic, and they will be
|
|
||| exceedingly inefficient at run time.
|
|
||| @ n the upper bound
|
|
public export
|
|
data Fin : (n : Nat) -> Type where
|
|
FZ : Fin (S k)
|
|
FS : Fin k -> Fin (S k)
|
|
|
|
export
|
|
Uninhabited (Fin Z) where
|
|
uninhabited FZ impossible
|
|
uninhabited (FS f) impossible
|
|
|
|
export
|
|
Uninhabited (FZ = FS k) where
|
|
uninhabited Refl impossible
|
|
|
|
export
|
|
Uninhabited (FS k = FZ) where
|
|
uninhabited Refl impossible
|
|
|
|
export
|
|
fsInjective : FS m = FS n -> m = n
|
|
fsInjective Refl = Refl
|
|
|
|
export
|
|
Eq (Fin n) where
|
|
(==) FZ FZ = True
|
|
(==) (FS k) (FS k') = k == k'
|
|
(==) _ _ = False
|
|
|
|
||| Convert a Fin to a Nat
|
|
public export
|
|
finToNat : Fin n -> Nat
|
|
finToNat FZ = Z
|
|
finToNat (FS k) = S $ finToNat k
|
|
|
|
||| `finToNat` is injective
|
|
export
|
|
finToNatInjective : (fm : Fin k) -> (fn : Fin k) -> (finToNat fm) = (finToNat fn) -> fm = fn
|
|
finToNatInjective FZ FZ _ = Refl
|
|
finToNatInjective (FS _) FZ Refl impossible
|
|
finToNatInjective FZ (FS _) Refl impossible
|
|
finToNatInjective (FS m) (FS n) prf =
|
|
cong FS $ finToNatInjective m n $ succInjective (finToNat m) (finToNat n) prf
|
|
|
|
export
|
|
Cast (Fin n) Nat where
|
|
cast = finToNat
|
|
|
|
||| Convert a Fin to an Integer
|
|
public export
|
|
finToInteger : Fin n -> Integer
|
|
finToInteger FZ = 0
|
|
finToInteger (FS k) = 1 + finToInteger k
|
|
|
|
export
|
|
Cast (Fin n) Integer where
|
|
cast = finToInteger
|
|
|
|
||| Weaken the bound on a Fin by 1
|
|
public export
|
|
weaken : Fin n -> Fin (S n)
|
|
weaken FZ = FZ
|
|
weaken (FS k) = FS $ weaken k
|
|
|
|
||| Weaken the bound on a Fin by some amount
|
|
public export
|
|
weakenN : (0 n : Nat) -> Fin m -> Fin (m + n)
|
|
weakenN n FZ = FZ
|
|
weakenN n (FS f) = FS $ weakenN n f
|
|
|
|
||| Weaken the bound on a Fin using a constructive comparison
|
|
public export
|
|
weakenLTE : Fin n -> LTE n m -> Fin m
|
|
weakenLTE FZ LTEZero impossible
|
|
weakenLTE (FS _) LTEZero impossible
|
|
weakenLTE FZ (LTESucc _) = FZ
|
|
weakenLTE (FS x) (LTESucc y) = FS $ weakenLTE x y
|
|
|
|
||| Attempt to tighten the bound on a Fin.
|
|
||| Return `Left` if the bound could not be tightened, or `Right` if it could.
|
|
export
|
|
strengthen : {n : _} -> Fin (S n) -> Either (Fin (S n)) (Fin n)
|
|
strengthen {n = S _} FZ = Right FZ
|
|
strengthen {n = S _} (FS i) with (strengthen i)
|
|
strengthen (FS _) | Left x = Left $ FS x
|
|
strengthen (FS _) | Right x = Right $ FS x
|
|
strengthen f = Left f
|
|
|
|
||| Add some natural number to a Fin, extending the bound accordingly
|
|
||| @ n the previous bound
|
|
||| @ m the number to increase the Fin by
|
|
public export
|
|
shift : (m : Nat) -> Fin n -> Fin (m + n)
|
|
shift Z f = f
|
|
shift (S m) f = FS $ shift m f
|
|
|
|
||| The largest element of some Fin type
|
|
public export
|
|
last : {n : _} -> Fin (S n)
|
|
last {n=Z} = FZ
|
|
last {n=S _} = FS last
|
|
|
|
||| All of the Fin elements
|
|
public export
|
|
allFins : (n : Nat) -> List1 (Fin (S n))
|
|
allFins Z = FZ ::: []
|
|
allFins (S n) = FZ ::: map FS (forget (allFins n))
|
|
|
|
export
|
|
Ord (Fin n) where
|
|
compare FZ FZ = EQ
|
|
compare FZ (FS _) = LT
|
|
compare (FS _) FZ = GT
|
|
compare (FS x) (FS y) = compare x y
|
|
|
|
public export
|
|
natToFin : Nat -> (n : Nat) -> Maybe (Fin n)
|
|
natToFin Z (S _) = Just FZ
|
|
natToFin (S k) (S j)
|
|
= case natToFin k j of
|
|
Just k' => Just (FS k')
|
|
Nothing => Nothing
|
|
natToFin _ _ = Nothing
|
|
|
|
||| Convert an `Integer` to a `Fin`, provided the integer is within bounds.
|
|
||| @n The upper bound of the Fin
|
|
public export
|
|
integerToFin : Integer -> (n : Nat) -> Maybe (Fin n)
|
|
integerToFin x Z = Nothing -- make sure 'n' is concrete, to save reduction!
|
|
integerToFin x n = if x >= 0 then natToFin (fromInteger x) n else Nothing
|
|
|
|
||| Allow overloading of Integer literals for Fin.
|
|
||| @ x the Integer that the user typed
|
|
||| @ prf an automatically-constructed proof that `x` is in bounds
|
|
public export
|
|
fromInteger : (x : Integer) -> {n : Nat} ->
|
|
{auto 0 prf : (IsJust (integerToFin x n))} ->
|
|
Fin n
|
|
fromInteger {n} x {prf} with (integerToFin x n)
|
|
fromInteger {n} x {prf = ItIsJust} | Just y = y
|
|
|
|
||| Convert an Integer to a Fin in the required bounds/
|
|
||| This is essentially a composition of `mod` and `fromInteger`
|
|
public export
|
|
restrict : (n : Nat) -> Integer -> Fin (S n)
|
|
restrict n val = let val' = assert_total (abs (mod val (cast (S n)))) in
|
|
-- reasoning about primitives, so we need the
|
|
-- 'believe_me'. It's fine because val' must be
|
|
-- in the right range
|
|
fromInteger {n = S n} val'
|
|
{prf = believe_me {a=IsJust (Just val')} ItIsJust}
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- DecEq
|
|
--------------------------------------------------------------------------------
|
|
|
|
public export
|
|
DecEq (Fin n) where
|
|
decEq FZ FZ = Yes Refl
|
|
decEq FZ (FS f) = No absurd
|
|
decEq (FS f) FZ = No absurd
|
|
decEq (FS f) (FS f')
|
|
= case decEq f f' of
|
|
Yes p => Yes $ cong FS p
|
|
No p => No $ p . fsInjective
|