module Quox.Syntax.DimEq import public Quox.Syntax.Var import public Quox.Syntax.Dim import public Quox.Syntax.Subst import public Quox.Context import Data.Maybe import Data.Nat import Data.DPair import Data.Fun.Graph import Decidable.Decidable import Decidable.Equality %default total public export DimEq' : Nat -> Type DimEq' = Context (Maybe . Dim) public export data DimEq : Nat -> Type where ZeroIsOne : DimEq d C : (eqs : DimEq' d) -> DimEq d %name DimEq eqs export zeroEq : DimEq 0 zeroEq = C [<] export new' : {d : Nat} -> DimEq' d new' {d = 0} = [<] new' {d = S d} = new' :< Nothing export %inline new : {d : Nat} -> DimEq d new = C new' private %inline shiftMay : Maybe (Dim from) -> Shift from to -> Maybe (Dim to) shiftMay p by = map (// by) p export %inline get' : DimEq' d -> Var d -> Maybe (Dim d) get' = getWith shiftMay private %inline getShift' : Shift len out -> DimEq' len -> Var len -> Maybe (Dim out) getShift' = getShiftWith shiftMay export %inline get : DimEq' d -> Dim d -> Dim d get _ (K e) = K e get eqs (B i) = fromMaybe (B i) $ get' eqs i export %inline equal : DimEq d -> (p, q : Dim d) -> Bool equal ZeroIsOne p q = True equal (C eqs) p q = get eqs p == get eqs q infixl 5 : Maybe (Dim d) -> DimEq (S d) ZeroIsOne : Dim d -> Maybe (Dim d) -> Maybe (Dim d) ifVar i p = map $ \q => if isYes $ q `decEq` B i then p else q private %inline checkConst : (e, f : DimConst) -> (eqs : Lazy (DimEq' d)) -> DimEq d checkConst Zero Zero eqs = C eqs checkConst One One eqs = C eqs checkConst _ _ _ = ZeroIsOne export setConst : Var d -> DimConst -> DimEq' d -> DimEq d setConst VZ e (eqs :< Nothing) = C $ eqs :< Just (K e) setConst VZ e (eqs :< Just (K f)) = checkConst e f $ eqs :< Just (K f) setConst VZ e (eqs :< Just (B i)) = setConst i e eqs : i `LT` j -> DimEq' d -> DimEq d setVar' VZ (VS i) LTZ (eqs :< Nothing) = C $ eqs :< Just (B i) setVar' VZ (VS i) LTZ (eqs :< Just (K e)) = setConst i e eqs : DimEq' d -> DimEq d setVar i j eqs with (compareP i j) _ | IsLT lt = setVar' i j lt eqs setVar i i eqs | IsEQ = C eqs _ | IsGT gt = setVar' j i gt eqs export %inline set : (p, q : Dim d) -> DimEq d -> DimEq d set _ _ ZeroIsOne = ZeroIsOne set (K e) (K f) (C eqs) = checkConst e f eqs set (K e) (B i) (C eqs) = setConst i e eqs set (B i) (K e) (C eqs) = setConst i e eqs set (B i) (B j) (C eqs) = setVar i j eqs public export %inline Split : Nat -> Type Split d = (DimEq' d, DSubst (S d) d) export %inline split1 : DimConst -> DimEq' (S d) -> Maybe (Split d) split1 e eqs = case setConst VZ e eqs of ZeroIsOne => Nothing C (eqs :< _) => Just (eqs, K e ::: id) export %inline split : DimEq' (S d) -> List (Split d) split eqs = toList (split1 Zero eqs) <+> toList (split1 One eqs) export splits' : DimEq' d -> List (DSubst d 0) splits' [<] = [id] splits' eqs@(_ :< _) = [th . ph | (eqs', th) <- split eqs, ph <- splits' eqs'] export %inline splits : DimEq d -> List (DSubst d 0) splits ZeroIsOne = [] splits (C eqs) = splits' eqs private 0 newGetShift : (d : Nat) -> (i : Var d) -> (by : Shift d d') -> getShift' by (new' {d}) i = Nothing newGetShift (S d) VZ by = Refl newGetShift (S d) (VS i) by = newGetShift d i (drop1 by) export 0 newGet' : (d : Nat) -> (i : Var d) -> get' (new' {d}) i = Nothing newGet' d i = newGetShift d i SZ export 0 newGet : (d : Nat) -> (p : Dim d) -> get (new' {d}) p = p newGet d (K e) = Refl newGet d (B i) = rewrite newGet' d i in Refl export 0 setSelf : (p : Dim d) -> (eqs : DimEq d) -> set p p eqs = eqs setSelf p ZeroIsOne = Refl setSelf (K Zero) (C eqs) = Refl setSelf (K One) (C eqs) = Refl setSelf (B i) (C eqs) = rewrite comparePSelf i in Refl -- [todo] "well formed" dimeqs -- [todo] operations maintain well-formedness -- [todo] if 'Wf eqs' then 'equal eqs' is an equivalence -- [todo] 'set' never breaks existing equalities