||| An expression language with mechanical proof of type-safety.
|||
||| `Tupled` is an expression language supporting Let-bindings, and
||| Tuples.  Standard constructions are used to represent the language
||| as an EDSL, together with proof of progress taken from PLFA Part
||| 2.
|||
||| This module compliments Section 3 of the Functional Pearl, but does
||| not appear due to similarities with `Razor.Recorded`.
|||
module Razor.Tupled

import Razor.Common

%default total

namespace Types

  public export
  data Ty = TyInt
          | TyChar
          | TyTuple (Vect (S n) Ty)

namespace Terms

  mutual
    ||| Fields in a Tuple.
    public export
    data Fields : (g     : List Ty)
               -> (types : Vect (S n) Ty)
               -> Type
      where
        Singleton : {ty    : Ty}
                 -> (field : Tupled g ty)
                          -> Fields g (ty::Nil)

        Extend : {ty    : Ty}
              -> {tys   : Vect (S n) Ty}
              -> (field : Tupled g ty)
              -> (rest  : Fields g      tys)
                       -> Fields g (ty::tys)

    public export
    data Tupled : List Ty -> Ty -> Type where
         -- Let-Bindings & Variables
          Var : Elem ty g -> Tupled g ty

          Let : (this     : Tupled        g  expr)
             -> (beInThis : Tupled (expr::g) body)
                         -> Tupled        g  body

          -- Base Values
          I : Int  -> Tupled g TyInt
          C : Char -> Tupled g TyChar

          -- Tuples & Accessors
          MkTuple : {types : Vect (S n) Ty}
                 -> (values : Fields g          types)
                           -> Tupled g (TyTuple types)

          Proj : {types : Vect (S n) Ty}
              -> (i     : Fin (S n))
              -> (tuple : Tupled g (TyTuple types))
              -> (idx   : AtIndex i types ty)
                       -> Tupled g ty

  public export
  index : {types  : Vect (S n) Ty}
       -> (fields : Fields  g types)
       -> (idx    : AtIndex i types ty)
                 -> Tupled  g       ty
  index (Singleton field) Here = field
  index (Extend field rest) Here = field

  index (Singleton _) (There Here) impossible
  index (Singleton _) (There (There later)) impossible

  index (Extend field rest) (There later)
    = index rest later

namespace Renaming

  public export
  weaken : (Contains  old           type -> Contains  new           type)
        -> (Contains (old += type') type -> Contains (new += type') type)

  weaken func Here         = Here
  weaken func (There rest) = There (func rest)

  mutual
    namespace Fields
      public export
      rename : (forall type  . Contains old type -> Contains new type)
            -> (forall types . Fields old types -> Fields new types)
      rename f (Singleton first)  = Singleton (rename f first)
      rename f (Extend next rest) = Extend (rename f next) (rename f rest)

    public export
    rename : (forall type . Contains old type -> Contains new type)
          -> (forall type . Tupled   old type -> Tupled   new type)
    -- Let-Bindings & Variables
    rename f (Var x) = Var (f x)
    rename f (Let this beInThis) = Let (rename f this)
                                       (rename (weaken f) beInThis)

    -- Base Values
    rename f (I x) = I x
    rename f (C x) = C x

    -- Tuples & Accessors
    rename f (MkTuple values)   = MkTuple (rename f values)
    rename f (Proj i tuple idx) = Proj i (rename f tuple) idx

namespace Substitution

  public export
  weakens : (forall type . Contains  old           type -> Tupled  new           type)
         -> (forall type . Contains (old += type') type -> Tupled (new += type') type)
  weakens f Here         = Var Here
  weakens f (There rest) = rename There (f rest)

  namespace General
    mutual
      namespace Fields
        public export
        subst : (f     : forall type . Contains old type -> Tupled new type)
             -> (forall types . Fields old types -> Fields new types)
        subst f (Singleton first) = Singleton (subst f first)
        subst f (Extend next rest) = Extend (subst f next) (subst f rest)

      public export
      subst : (forall type . Contains old type -> Tupled new type)
           -> (forall type . Tupled   old type -> Tupled new type)
      -- Let-Bindings & Variables
      subst f (Var x) = f x
      subst f (Let this beInThis) = Let (subst f this)
                                        (subst (weakens f) beInThis)

      -- Base Values
      subst f (I x) = I x
      subst f (C x) = C x

      -- Tuples & Accesors
      subst f (MkTuple values)   = MkTuple (subst f values)
      subst f (Proj i tuple idx) = Proj i (subst f tuple) idx

  namespace Single
    public export
    apply : (this   : Tupled ctxt typeB)
         -> (idx    : Contains (ctxt += typeB) typeA)
                   -> Tupled ctxt typeA
    apply this Here         = this
    apply this (There rest) = Var rest

    public export
    subst : (this   : Tupled  ctxt           typeB)
         -> (inThis : Tupled (ctxt += typeB) typeA)
                   -> Tupled  ctxt           typeA
    subst {ctxt} {typeA} {typeB} this inThis = General.subst (apply this) inThis

namespace Values
  mutual
    namespace Fields
      public export
      data Value : (fields : Fields g types) -> Type
        where
          SingletonV : (field : Value v) -> Value (Singleton v)
          ExtendV : {field  : Tupled ctxt type}
                 -> {fields : Fields ctxt types}
                 -> (next : Value field)
                 -> (rest : Value fields)
                         -> Value (Extend field fields)

    public export
    data Value : Tupled ctxt type -> Type where

      -- Base Values
      IV : Value (I i)
      CV : Value (C c)

      -- Tuples
      MkTupleV : Value fields
              -> Value (MkTuple fields)

namespace Reduction

  mutual
    namespace Fields
      public export
      data Redux : (this, that : Fields g types) -> Type where
          -- Single field.
          SimplifySingleton : (prf : Redux this that)
                                  -> Redux (Singleton this) (Singleton that)

          SimplifyExtend : {this, that : Tupled ctxt type}
                        -> {rest : Fields ctxt types}
                        -> (prf : Redux this that)
                        -> Redux (Extend this rest) (Extend that rest)

          SimplifyExtendV : {this, that : Fields ctxt types}
                         -> (value      : Values.Value v)
                         -> (rest       : Fields.Redux this that)
                                       -> Redux (Extend v this) (Extend v that)

    public export
    data Redux : (this, that : Tupled ctxt type) -> Type where
      -- Let Bindings
      SimplifyLetValue : Redux this that
                      -> Redux (Let this body)
                               (Let that body)
      ReduceLetBody : Value value
                   -> Redux (Let value body)
                            (subst value body)

      SimplifyTupleR : Redux this that
                    -> Redux (MkTuple this) (MkTuple that)

      -- Accessors
      SimplifyProj : {0  labels : Vect (S n) Ty}
                  -> {   this, that : Tupled g (TyTuple labels)}
                  -> {0  idx        : AtIndex i labels ty}
                  -> Redux this that
                  -> Redux (Proj i this idx) (Proj i that idx)

      ReduceProj : {types  : Vect (S n) Ty}
                -> {fields : Fields g types}
                -> {idx    : AtIndex i types type}
                -> (values : Value fields)
                    -> Redux (Proj i (MkTuple fields) idx)
                             (index fields idx)

namespace Progress

  public export
  data Progress : (term : Tupled Nil type) -> Type where
      Done : Value term -> Progress term
      Step : {this,that : Tupled Nil type} -> (step : Redux this that) -> Progress this

  namespace Fields
    public export
    data Progress : (fields : Fields Nil types) -> Type where
      Done : (value : Value fields) -> Progress fields
      Step : {this,that : Fields Nil types} -> (step  : Redux this that) -> Progress this

  mutual
    namespace Fields
      public export
      progress : (fields : Fields Nil types) -> Progress fields
      progress (Singleton first) with (progress first)
        progress (Singleton first) | (Done x) = Done (SingletonV x)
        progress (Singleton first) | (Step prf) = Step (SimplifySingleton prf)

      progress (Extend next rest) with (progress next)
        progress (Extend next rest) | (Done x) with (progress rest)
          progress (Extend next rest) | (Done x) | (Done xs)  = Done (ExtendV x xs)
          progress (Extend next rest) | (Done x) | (Step prf) = Step (SimplifyExtendV x prf)

        progress (Extend next rest) | (Step prf) = Step (SimplifyExtend prf)

    public export
    progress : (term : Tupled Nil type) -> Progress term
    progress (Var Here) impossible

    -- Let-Bindings & Variables
    progress (Var _) impossible
    progress (Let this beInThis) with (progress this)
      progress (Let this beInThis) | (Done x) = Step (ReduceLetBody x)
      progress (Let this beInThis) | (Step prf) = Step (SimplifyLetValue prf)

    -- Base Values
    progress (I x) = Done IV
    progress (C x) = Done CV

    -- Tuples & Accessors
    progress (MkTuple values) with (progress values)
      progress (MkTuple values) | (Done v)   = Done (MkTupleV v)
      progress (MkTuple values) | (Step prf) = Step (SimplifyTupleR prf)

    progress (Proj i tuple idx) with (progress tuple)
      progress (Proj i (MkTuple fields) idx) | (Done (MkTupleV x)) = Step (ReduceProj x)
      progress (Proj i tuple idx) | (Step prf) = Step (SimplifyProj prf)

namespace Evaluation

  public export
  data Reduces : (this : Tupled ctxt type)
              -> (that : Tupled ctxt type)
              -> Type
    where
      Refl  : {this : Tupled ctxt type}
                   -> Reduces this this
      Trans : {this, that, end : Tupled ctxt type}
           -> Redux this that
           -> Reduces that end
           -> Reduces this end

  public export
  data Finished : (term : Tupled ctxt type)
                       -> Type
    where
      Normalised : {term : Tupled ctxt type} -> Value term -> Finished term
      OOF : Finished term

  public export
  data Steps : (term : Tupled Nil type) -> Type where
    MkSteps : {this, that : Tupled Nil type}
           -> (steps  : Inf (Reduces this that))
           -> (result : Finished that)
           -> Steps this

  public export
  data Fuel = Empty | More (Lazy Fuel)

  public export
  covering
  forever : Fuel
  forever = More forever

  public export
  compute : (fuel : Fuel)
         -> (term : Tupled Nil type)
         -> Steps term
  compute Empty term = MkSteps Refl OOF
  compute (More fuel) term with (progress term)
    compute (More fuel) term | (Done value) = MkSteps Refl (Normalised value)
    compute (More fuel) term | (Step step {that}) with (compute fuel that)
      compute (More fuel) term | (Step step {that = that}) | (MkSteps steps result)
        = MkSteps (Trans step steps) result

public export
covering
run : (this : Tupled Nil type)
           -> Maybe (Subset (Tupled Nil type) (Reduces this))
run this with (compute forever this)
  run this | (MkSteps steps (Normalised {term} x)) = Just (Element term steps)
  run this | (MkSteps steps OOF) = Nothing

namespace Examples
    public export
    abc : Tupled Nil (TyTuple [TyInt, TyChar, TyChar])
    abc = MkTuple (Extend (I 1) (Extend (C 'c') (Singleton (C '2'))))

    public export
    a : Tupled Nil (TyInt)
    a = Proj FZ abc Here

    public export
    b : Tupled Nil (TyChar)
    b = Proj (FS FZ) abc (There Here)


module Parser

import Lightyear
import Control.Monad.Identity
import Lexer
import Syntax

public export
Parser : Type -> Type
Parser = ParserT (List Lexeme) Identity

is : (Monad m, Stream tok str, Eq tok)
     => tok
     -> ParserT str m tok
is l = satisfy (== l)

implementation Stream Lexeme (List Lexeme) where
  uncons []        = Nothing
  uncons (x :: xs) = Just (x, xs)

E : Type
E = Expr

infixOp : Lexeme -> (a -> a -> a) -> Parser (a -> a -> a)
infixOp l ctor = do
  _ <- satisfy (== l)
  pure ctor

addOp : Parser (E -> E -> E)
addOp = infixOp PLUS Plus <|> infixOp MINUS Minus

mulOp : Parser (E -> E -> E)
mulOp = infixOp TIMES Times

boolOp : Parser (E -> E -> E)
boolOp = infixOp EQUAL Equal <|> infixOp LESS Less

tyOp : Parser (Ty -> Ty -> Ty)
tyOp = infixOp TARROW TArrow

-- Actually no "operator" here. Function application is just juxtaposition.
applyOp : Parser (E -> E -> E)
applyOp = pure Apply

isInt : Lexeme -> Bool
isInt (INT n) = True
isInt _       = False

int : Parser Expr
int = do
  (INT i) <- satisfy isInt
  pure (EInt i)

neg : Parser Expr
neg = do
  _ <- is MINUS
  (EInt i) <- int
  pure $ EInt (-i)

lparen : Parser Lexeme
lparen = is LPAREN

rparen : Parser Lexeme
rparen = is RPAREN

semicolon2 : Parser Lexeme
semicolon2 = is SEMICOLON2

true : Parser Expr
true = is TRUE *> pure (EBool True)

false : Parser Expr
false = is FALSE *> pure (EBool False)

isVar : Lexeme -> Bool
isVar (VAR _) = True
isVar _         = False

nm : Parser Name
nm = do
  (VAR nm) <- satisfy isVar
  pure $ nm

var : Parser Expr
var = do
  name <- nm
  pure $ Var name

mutual
  def : Parser TopLevelCmd
  def = do
    _ <- is LET
    name <- nm
    _ <- is EQUAL
    e <- expr
    pure $ TopDef name e

  expr : Parser Expr
  expr = boolean

  boolean : Parser Expr
  boolean = arith `chainl1` boolOp

  arith : Parser Expr
  arith = term `chainl1` addOp

  term : Parser Expr
  term = factor `chainl1` mulOp

  ifExpr : Parser Expr
  ifExpr = do
    _ <- is IF
    c <- expr
    _ <- is THEN
    t <- expr
    _ <- is ELSE
    f <- expr
    pure $ If c t f

  ty : Parser Ty
  ty = tyFactor `chainr1` tyOp

  tyFactor : Parser Ty
  tyFactor = (is TBOOL *> pure TBool)
         <|> (is TINT  *> pure TInt)
         <|> (lparen *>| ty <*| rparen)

  fun : Parser Expr
  fun = do
    _ <- is FUN
    funName <- nm
    _ <- lparen
    parName <- nm
    _ <- is COLON
    parTy <- ty
    _ <- rparen
    _ <- is COLON
    resTy <- ty
    _ <- is IS
    body <- expr
    pure $ Fun funName parName parTy resTy body

  factor : Parser Expr
  factor = (nonApp `chainl1` applyOp)
       <|> ifExpr
       <|> fun

  nonApp : Parser Expr
  nonApp = var
       <|> true
       <|> false
       <|> int
       <|> (lparen *>| expr <*| rparen)

export
toplevel : Parser (List TopLevelCmd)
toplevel = do
  cmd <- TopExpr <$> expr <|> def
  esOpt <- opt (semicolon2 *> toplevel)
  _ <- opt semicolon2
  pure $ cmd :: case esOpt of
    Just es => es
    None    => []

{-
toplevel ::=
  | EOF
  | def EOF
  | def ";;" EOF
  | expr EOF
  | expr ";; EOF
  | def ";;" toplevel
  | expr ";; toplevel

def ::= LET VAR EQUAL expr

expr ::=
  | non_app
  | app
  | arith
  | boolean
  | IF expr THEN expr ELSE expr
  | FUN VAR "(" VAR ":" ty ")" ":" ty IS expr

app ::=
  | app non_app -- huh?
  | non_app non_app

non_app ::=
  | VAR
  | TRUE
  | FALSE
  | INT
  | "(" expr ")"

arith ::=
  | MINUS INT
  | expr PLUS expr
  | expr MINUS expr
  | expr TIMES expr

boolean ::=
  | expr EQUAL expr
  | expr LESS expr

ty ::=
  | TBOOL
  | TINT
  | ty "->" ty
  | "(" ty ")"
-}


public export
data InfList : Type -> Type where
  (::) : (value : elem) -> Inf (InfList elem) -> InfList elem

Functor InfList where
    map func (value :: xs) = func value :: Delay (map func xs)

%name InfList xs, ys, zs


ff : (a -> b) -> Not (Not a) -> Not (Not b)
ff ab nna nb = nna $ nb . ab


import Data.Primitives.Views
import System

%default total

data InfIO: Type where
  Do: IO a -> (a -> Inf InfIO) -> InfIO

(>>=) : IO a -> (a -> Inf InfIO) -> InfIO
(>>=) = Do

data Fuel = Dry | More (Lazy Fuel)

partial
forever: Fuel
forever = More forever

run: Fuel -> InfIO -> IO()
run Dry         y                = putStrLn "Out of fuel"
run (More fuel) (Do action cont) =
  do
    res <- action
    run fuel (cont res)

quiz : Stream Int -> (score: Nat) -> InfIO
quiz (num1 :: num2 :: nums) score =
  do
    putStrLn ("Score so far: " ++ show score)
    putStr   (show num1 ++ " * " ++ show num2 ++ "? ")
    answer <- getLine
    if cast answer == num1 * num2
      then do putStrLn "Correct!"
              quiz nums (score + 1)
      else do putStrLn ("Wrong, the answer is " ++ show (num1 * num2))
              quiz nums score

randoms : Int -> Stream Int
randoms seed =
  let seed' = 1664525 * seed + 1013904223 in
      (seed' `shiftR` 2) :: randoms seed'

arithInputs : Int -> Stream Int
arithInputs seed = map bound (randoms seed)
  where
    bound : Int -> Int
    bound num with (divides num 12)
      bound ((12 * div) + rem) | (DivBy prf) = rem + 1

partial
main : IO()
main =
  do
    seed <- time
    run forever (quiz (arithInputs (fromInteger seed)) 0)


-- exercises in "Type-Driven Development with Idris"
-- chapter 6

-- check that all functions are total
%default total

data Format = Number Format
            | Doubble Format
            | Str Format
            | Chhar Format
            | Lit String Format
            | End
%name Format fmt, fmt1, fmt2, fmt3

PrintfType : Format -> Type
PrintfType (Number fmt) = (i : Int) -> PrintfType fmt
PrintfType (Doubble fmt) = (d : Double) -> PrintfType fmt
PrintfType (Str fmt) = (str : String) -> PrintfType fmt
PrintfType (Chhar fmt) = (car : Char) -> PrintfType fmt
PrintfType (Lit str fmt) = PrintfType fmt
PrintfType End = String

printFmt : (fmt : Format) -> (acc : String) -> PrintfType fmt
printFmt (Number fmt) acc = \i => printFmt fmt (acc ++ show i)
printFmt (Doubble fmt) acc = \d => printFmt fmt (acc ++ show d)
printFmt (Str fmt) acc = \str => printFmt fmt (acc ++ str)
printFmt (Chhar fmt) acc = \char => printFmt fmt (acc ++ show char)
printFmt (Lit str fmt) acc = printFmt fmt (acc ++ str)
printFmt End acc = acc

toFormat : (xs : List Char) -> Format
toFormat [] = End
toFormat ('%' :: 'd' :: chars) = Number (toFormat chars)
toFormat ('%' :: 'f' :: chars) = Doubble (toFormat chars)
toFormat ('%' :: 's' :: chars) = Str (toFormat chars)
toFormat ('%' :: 'c' :: chars) = Chhar (toFormat chars)
toFormat ('%' :: chars) = Lit "%" (toFormat chars)
toFormat (c :: chars) = case toFormat chars of
                          Lit str chars' => Lit (strCons c str) chars'
                          fmt => Lit (strCons c "") fmt

printf : (fmt : String) -> PrintfType (toFormat (unpack fmt))
printf fmt = printFmt _ ""
-- > :t printf "%s = %d"
-- printf "%s = %d" : String -> Int -> String
-- > :t printf "%c %f"
-- printf "%c %f" : Char -> Double -> String
-- > printf "%c %f" 'X' 24
-- "'X' 24.0" : String


module ProofColDivSeqPostulate

import ProofColDivSeqBase

%default total
-- %language ElabReflection
%access export

%hide Language.Reflection.P

--            from ProofColDivSeqBase
-- ########################################
-- 無限降下法（の変形） Isabelleで証明した
postulate infiniteDescent :
  ((n:Nat) -> P (S n) 2 -> (m ** (LTE (S m) (S n), P m 2)))
    -> All Limited $ allDivSeq Z 2
      -> All Limited $ allDivSeq (n+n+n) 2

-- BaseLog0.txtより、保証される
postulate base0 : All Limited $ allDivSeq Z 2
-- ########################################

--            from ProofColDivSeqLvDown
-- ########################################
any2Sub : {pp : a -> Type} -> (x : a) -> (xs, ys : List a)
  -> Either (Any pp xs) (Any pp ys) -> Either (Any pp (x :: xs)) (Any pp ys)
any2Sub x []       ys (Left anL)  = absurd anL
any2Sub x (x2::xs) ys (Left anL)  = Left (There anL)
any2Sub x xs       ys (Right anR) = Right anR

-- これが肝
any2 : {pp : a -> Type} -> (xs, ys : List a)
  -> Any pp (xs ++ ys) -> Either (Any pp xs) (Any pp ys)
any2 []      ys an         = Right an
any2 (x::xs) ys (Here he)  = Left (Here he)
any2 (x::xs) ys (There th) =
  let foo = any2 xs ys th in any2Sub x xs ys foo

any3 : {pp : a -> Type} -> (xs1, xs2, xs3 : List a)
  -> Either (Any pp xs1) (Any pp (xs2++xs3))
    -> Either (Any pp xs1) (Either (Any pp xs2) (Any pp xs3))
any3 xs1 xs2 xs3 (Left prfL)  = Left prfL
any3 xs1 xs2 xs3 (Right prfR) = Right (any2 xs2 xs3 prfR)

any4 : {pp : a -> Type} -> (xs1, xs2, xs3, xs4 : List a)
  -> Either (Any pp xs1) (Either (Any pp xs2) (Any pp (xs3++xs4)))
    -> Either (Any pp xs1) (Either (Any pp xs2) (Either (Any pp xs3) (Any pp xs4)))
any4 xs1 xs2 xs3 xs4 (Left prfL)           = Left prfL
any4 xs1 xs2 xs3 xs4 (Right (Left prfRL))  = Right (Left prfRL)
any4 xs1 xs2 xs3 xs4 (Right (Right prfRR)) = Right (Right (any2 xs3 xs4 prfRR))

any5 : {pp : a -> Type} -> (xs1, xs2, xs3, xs4, xs5 : List a)
  -> Either (Any pp xs1) (Either (Any pp xs2) (Either (Any pp xs3) (Any pp (xs4++xs5))))
    -> Either (Any pp xs1) (Either (Any pp xs2) (Either (Any pp xs3) (Either (Any pp xs4) (Any pp xs5))))
any5 xs1 xs2 xs3 xs4 xs5 (Left prfL)                    = Left prfL
any5 xs1 xs2 xs3 xs4 xs5 (Right (Left prfRL))           = Right (Left prfRL)
any5 xs1 xs2 xs3 xs4 xs5 (Right (Right (Left prfRRL)))  = Right (Right (Left prfRRL))
any5 xs1 xs2 xs3 xs4 xs5 (Right (Right (Right prfRRR))) = Right (Right (Right (any2 xs4 xs5 prfRRR)))

any6 : {pp : a -> Type} -> (xs1, xs2, xs3, xs4, xs5, xs6 : List a)
  -> Either (Any pp xs1) (Either (Any pp xs2) (Either (Any pp xs3) (Either (Any pp xs4) (Any pp (xs5++xs6)))))
    -> Either (Any pp xs1) (Either (Any pp xs2) (Either (Any pp xs3) (Either (Any pp xs4) (Either (Any pp xs5) (Any pp xs6)))))
any6 xs1 xs2 xs3 xs4 xs5 xs6 (Left prfL)                             = Left prfL
any6 xs1 xs2 xs3 xs4 xs5 xs6 (Right (Left prfRL))                    = Right (Left prfRL)
any6 xs1 xs2 xs3 xs4 xs5 xs6 (Right (Right (Left prfRRL)))           = Right (Right (Left prfRRL))
any6 xs1 xs2 xs3 xs4 xs5 xs6 (Right (Right (Right (Left prfRRRL))))  = Right (Right (Right (Left prfRRRL)))
any6 xs1 xs2 xs3 xs4 xs5 xs6 (Right (Right (Right (Right prfRRRR)))) = Right (Right (Right (Right (any2 xs5 xs6 prfRRRR))))

any7 : {pp : a -> Type} -> (xs1, xs2, xs3, xs4, xs5, xs6, xs7 : List a)
  -> Either (Any pp xs1) (Either (Any pp xs2) (Either (Any pp xs3) (Either (Any pp xs4) (Either (Any pp xs5) (Any pp (xs6++xs7))))))
    -> Either (Any pp xs1) (Either (Any pp xs2) (Either (Any pp xs3) (Either (Any pp xs4) (Either (Any pp xs5) (Either (Any pp xs6) (Any pp xs7))))))
any7 xs1 xs2 xs3 xs4 xs5 xs6 xs7 (Left prfL)                                      = Left prfL
any7 xs1 xs2 xs3 xs4 xs5 xs6 xs7 (Right (Left prfRL))                             = Right (Left prfRL)
any7 xs1 xs2 xs3 xs4 xs5 xs6 xs7 (Right (Right (Left prfRRL)))                    = Right (Right (Left prfRRL))
any7 xs1 xs2 xs3 xs4 xs5 xs6 xs7 (Right (Right (Right (Left prfRRRL))))           = Right (Right (Right (Left prfRRRL)))
any7 xs1 xs2 xs3 xs4 xs5 xs6 xs7 (Right (Right (Right (Right (Left prfRRRRL)))))  = Right (Right (Right (Right (Left prfRRRRL))))
any7 xs1 xs2 xs3 xs4 xs5 xs6 xs7 (Right (Right (Right (Right (Right prfRRRRR))))) = Right (Right (Right (Right (Right (any2 xs6 xs7 prfRRRRR)))))

any8 : {pp : a -> Type} -> (xs1, xs2, xs3, xs4, xs5, xs6, xs7, xs8 : List a)
  -> Either (Any pp xs1) (Either (Any pp xs2) (Either (Any pp xs3) (Either (Any pp xs4) (Either (Any pp xs5) (Either (Any pp xs6) (Any pp (xs7++xs8)))))))
    -> Either (Any pp xs1) (Either (Any pp xs2) (Either (Any pp xs3) (Either (Any pp xs4) (Either (Any pp xs5) (Either (Any pp xs6) (Either (Any pp xs7) (Any pp xs8)))))))
any8 xs1 xs2 xs3 xs4 xs5 xs6 xs7 xs8 (Left prfL)                                               = Left prfL
any8 xs1 xs2 xs3 xs4 xs5 xs6 xs7 xs8 (Right (Left prfRL))                                      = Right (Left prfRL)
any8 xs1 xs2 xs3 xs4 xs5 xs6 xs7 xs8 (Right (Right (Left prfRRL)))                             = Right (Right (Left prfRRL))
any8 xs1 xs2 xs3 xs4 xs5 xs6 xs7 xs8 (Right (Right (Right (Left prfRRRL))))                    = Right (Right (Right (Left prfRRRL)))
any8 xs1 xs2 xs3 xs4 xs5 xs6 xs7 xs8 (Right (Right (Right (Right (Left prfRRRRL)))))           = Right (Right (Right (Right (Left prfRRRRL))))
any8 xs1 xs2 xs3 xs4 xs5 xs6 xs7 xs8 (Right (Right (Right (Right (Right (Left prfRRRRRL))))))  = Right (Right (Right (Right (Right (Left prfRRRRRL)))))
any8 xs1 xs2 xs3 xs4 xs5 xs6 xs7 xs8 (Right (Right (Right (Right (Right (Right prfRRRRRR)))))) = Right (Right (Right (Right (Right (Right (any2 xs7 xs8 prfRRRRRR))))))

anyFinal : {pp : a -> Type} -> (xs1, xs2, xs3, xs4, xs5, xs6, xs7, xs8 : List a)
  -> Any pp (xs1 ++ xs2 ++ xs3 ++ xs4 ++ xs5 ++ xs6 ++ xs7 ++ xs8)
    -> Either (Any pp xs1) (Either (Any pp xs2) (Either (Any pp xs3) (Either (Any pp xs4) (Either (Any pp xs5) (Either (Any pp xs6) (Either (Any pp xs7) (Any pp xs8)))))))
anyFinal xs1 xs2 xs3 xs4 xs5 xs6 xs7 xs8 prf =
  let prf2 = any2 xs1 (xs2 ++ xs3 ++ xs4 ++ xs5 ++ xs6 ++ xs7 ++ xs8) prf in
  let prf3 = any3 xs1 xs2 (xs3 ++ xs4 ++ xs5 ++ xs6 ++ xs7 ++ xs8) prf2 in
  let prf4 = any4 xs1 xs2 xs3 (xs4 ++ xs5 ++ xs6 ++ xs7 ++ xs8) prf3 in
  let prf5 = any5 xs1 xs2 xs3 xs4 (xs5 ++ xs6 ++ xs7 ++ xs8) prf4 in
  let prf6 = any6 xs1 xs2 xs3 xs4 xs5 (xs6 ++ xs7 ++ xs8) prf5 in
  let prf7 = any7 xs1 xs2 xs3 xs4 xs5 xs6 (xs7 ++ xs8) prf6 in
  let prf8 = any8 xs1 xs2 xs3 xs4 xs5 xs6 xs7 xs8 prf7 in prf8

-- 前方を削っているのは、（有限or無限を判定する）末尾に影響を与えないから
postulate dspCut : Any (Not . Limited) (map ([a,b] `dsp`) xs)
  -> Any (Not . Limited) xs
postulate dsp2Cut : Any (Not . Limited) (map ([a] `dsp2`) xs)
  -> Any (Not . Limited) xs

changeA' : (x, lv:Nat)
  -> Any (Not . Limited)
      (if (modNatNZ (x+7) 4 SIsNotZ) == 0
        then map ([6,-4] `dsp`) $ allDivSeq (divNatNZ ((x+7)*3) 4 SIsNotZ) (S lv)
        else [])
    -> Any (Not . Limited) (allDivSeq (divNatNZ ((x+7)*3) 4 SIsNotZ) (S lv))
changeA' x lv prf with ((modNatNZ (x+7) 4 SIsNotZ) == 0) proof p
  changeA' x lv prf | False = absurd prf
  changeA' x lv prf | True  = dspCut prf
changeA : (x, lv:Nat) -> Any (Not . Limited) (allDivSeqA x (S lv))
  -> Any (Not . Limited) (allDivSeq (divNatNZ ((x+7)*3) 4 SIsNotZ) (S lv))
changeA x lv prf = changeA' x lv $ replace (definiA x lv) prf

changeA0' : (x : Nat)
  -> Any (Not . Limited)
      (if (modNatNZ (x+7) 4 SIsNotZ) == 0 && (modNatNZ (modNatNZ (x+7) 4 SIsNotZ) 2 SIsNotZ) == 1
        then [[6,-4] `dsp` (Just (divSeq (divNatNZ ((x+7)*3) 4 SIsNotZ)))]
        else [])
    -> Any (Not . Limited) [Just (divSeq (divNatNZ ((x+7)*3) 4 SIsNotZ))]
changeA0' x prf with ((modNatNZ (x+7) 4 SIsNotZ) == 0 && (modNatNZ (modNatNZ (x+7) 4 SIsNotZ) 2 SIsNotZ) == 1) proof p
  changeA0' x prf | False = absurd prf
  changeA0' x prf | True  = dspCut prf
changeA0 : (x:Nat) -> Any (Not . Limited) (allDivSeqA x 0)
  -> Any (Not . Limited) [Just (divSeq (divNatNZ ((x+7)*3) 4 SIsNotZ))]
changeA0 x prf = changeA0' x $ replace (definiA0 x) prf

changeB : (x, lv:Nat) -> Any (Not . Limited) (allDivSeqB x (S lv))
  -> Any (Not . Limited) (allDivSeq (x*6+3) (S lv))
changeB x lv prf = dspCut $ replace (definiB x lv) prf

changeB0' : (x : Nat)
  -> Any (Not . Limited)
      (if (modNatNZ (x*6+3) 2 SIsNotZ) == 1
        then [[1,-2] `dsp` (Just (divSeq (x*6+3)))]
        else [])
    -> Any (Not . Limited) [Just (divSeq (x*6+3))]
changeB0' x prf with ((modNatNZ (x*6+3) 2 SIsNotZ) == 1) proof p
  changeB0' x prf | False = absurd prf
  changeB0' x prf | True  = dspCut prf
changeB0 : (x:Nat) -> Any (Not . Limited) (allDivSeqB x 0)
  -> Any (Not . Limited) [Just (divSeq (x*6+3))]
changeB0 x prf = changeB0' x $ replace (definiB0 x) prf

changeC : (x, lv:Nat) -> Any (Not . Limited) (allDivSeqC x (S lv))
  -> Any (Not . Limited) (allDivSeq (x*3+6) (S lv))
changeC x lv prf = dspCut $ replace (definiC x lv) prf

changeC0' : (x : Nat)
  -> Any (Not . Limited)
      (if (modNatNZ (x*3+6) 2 SIsNotZ) == 1
        then [[4,-4] `dsp` (Just (divSeq (x*3+6)))]
        else [])
    -> Any (Not . Limited) [Just (divSeq (x*3+6))]
changeC0' x prf with ((modNatNZ (x*3+6) 2 SIsNotZ) == 1) proof p
  changeC0' x prf | False = absurd prf
  changeC0' x prf | True  = dspCut prf
changeC0 : (x:Nat) -> Any (Not . Limited) (allDivSeqC x 0)
  -> Any (Not . Limited) [Just (divSeq (x*3+6))]
changeC0 x prf = changeC0' x $ replace (definiC0 x) prf

changeD' : (x, lv:Nat)
  -> Any (Not . Limited)
      (if (modNatNZ (x+1) 2 SIsNotZ) == 0
        then map ([3,-2] `dsp`) $ allDivSeq (divNatNZ ((x+1)*3) 2 SIsNotZ) (S lv)
        else [])
    -> Any (Not . Limited) (allDivSeq (divNatNZ ((x+1)*3) 2 SIsNotZ) (S lv))
changeD' x lv prf with ((modNatNZ (x+1) 2 SIsNotZ) == 0) proof p
  changeD' x lv prf | False = absurd prf
  changeD' x lv prf | True  = dspCut prf
changeD : (x, lv:Nat) -> Any (Not . Limited) (allDivSeqD x (S lv))
  -> Any (Not . Limited) (allDivSeq (divNatNZ ((x+1)*3) 2 SIsNotZ) (S lv))
changeD x lv prf = changeD' x lv $ replace (definiD x lv) prf

changeD0' : (x : Nat)
  -> Any (Not . Limited)
      (if (modNatNZ (x+1) 2 SIsNotZ) == 0 && (modNatNZ (modNatNZ (x+1) 2 SIsNotZ) 2 SIsNotZ) == 1
        then [[3,-2] `dsp` (Just (divSeq (divNatNZ ((x+1)*3) 2 SIsNotZ)))]
        else [])
    -> Any (Not . Limited) [Just (divSeq (divNatNZ ((x+1)*3) 2 SIsNotZ))]
changeD0' x prf with ((modNatNZ (x+1) 2 SIsNotZ) == 0 && (modNatNZ (modNatNZ (x+1) 2 SIsNotZ) 2 SIsNotZ) == 1) proof p
  changeD0' x prf | False = absurd prf
  changeD0' x prf | True  = dspCut prf
changeD0 : (x:Nat) -> Any (Not . Limited) (allDivSeqD x 0)
  -> Any (Not . Limited) [Just (divSeq (divNatNZ ((x+1)*3) 2 SIsNotZ))]
changeD0 x prf = changeD0' x $ replace (definiD0 x) prf

changeE : (x, lv:Nat) -> Any (Not . Limited) (allDivSeqE x (S lv))
  -> Any (Not . Limited) (allDivSeq (x*12+9) (S lv))
changeE x lv prf = dspCut $ replace (definiE x lv) prf

changeE0' : (x : Nat)
  -> Any (Not . Limited)
      (if (modNatNZ (x*12+9) 2 SIsNotZ) == 1
        then [[2,-4] `dsp` (Just (divSeq (x*12+9)))]
        else [])
    -> Any (Not . Limited) [Just (divSeq (x*12+9))]
changeE0' x prf with ((modNatNZ (x*12+9) 2 SIsNotZ) == 1) proof p
  changeE0' x prf | False = absurd prf
  changeE0' x prf | True  = dspCut prf
changeE0 : (x:Nat) -> Any (Not . Limited) (allDivSeqE x 0)
  -> Any (Not . Limited) [Just (divSeq (x*12+9))]
changeE0 x prf = changeE0' x $ replace (definiE0 x) prf

changeF' : (x, lv:Nat)
  -> Any (Not . Limited)
      (if (modNatNZ (x+3) 8 SIsNotZ) == 0
        then map ([5,-2] `dsp`) $ allDivSeq (divNatNZ ((x+3)*3) 8 SIsNotZ) (S lv)
        else [])
    -> Any (Not . Limited) (allDivSeq (divNatNZ ((x+3)*3) 8 SIsNotZ) (S lv))
changeF' x lv prf with ((modNatNZ (x+3) 8 SIsNotZ) == 0) proof p
  changeF' x lv prf | False = absurd prf
  changeF' x lv prf | True  = dspCut prf
changeF : (x, lv:Nat) -> Any (Not . Limited) (allDivSeqF x (S lv))
  -> Any (Not . Limited) (allDivSeq (divNatNZ ((x+3)*3) 8 SIsNotZ) (S lv))
changeF x lv prf = changeF' x lv $ replace (definiF x lv) prf

changeF0' : (x : Nat)
  -> Any (Not . Limited)
      (if (modNatNZ (x+3) 8 SIsNotZ) == 0 && (modNatNZ (modNatNZ (x+3) 8 SIsNotZ) 2 SIsNotZ) == 1
        then [[5,-2] `dsp` (Just (divSeq (divNatNZ ((x+3)*3) 8 SIsNotZ)))]
        else [])
    -> Any (Not . Limited) [Just (divSeq (divNatNZ ((x+3)*3) 8 SIsNotZ))]
changeF0' x prf with ((modNatNZ (x+3) 8 SIsNotZ) == 0 && (modNatNZ (modNatNZ (x+3) 8 SIsNotZ) 2 SIsNotZ) == 1) proof p
  changeF0' x prf | False = absurd prf
  changeF0' x prf | True  = dspCut prf
changeF0 : (x:Nat) -> Any (Not . Limited) (allDivSeqF x 0)
  -> Any (Not . Limited) [Just (divSeq (divNatNZ ((x+3)*3) 8 SIsNotZ))]
changeF0 x prf = changeF0' x $ replace (definiF0 x) prf

changeG' : (x, lv:Nat)
  -> Any (Not . Limited)
      (if (modNatNZ (x `minus` 21) 64 SIsNotZ) == 0 && x > 21
        then map ([6] `dsp2`) $ allDivSeq (divNatNZ (x `minus` 21) 64 SIsNotZ) (S lv)
        else [])
    -> Any (Not . Limited) (allDivSeq (divNatNZ (x `minus` 21) 64 SIsNotZ) (S lv))
changeG' x lv prf with ((modNatNZ (x `minus` 21) 64 SIsNotZ) == 0 && x > 21) proof p
  changeG' x lv prf | False = absurd prf
  changeG' x lv prf | True  = dsp2Cut prf
changeG : (x, lv:Nat) -> Any (Not . Limited) (allDivSeqG x (S lv))
  -> Any (Not . Limited) (allDivSeq (divNatNZ (x `minus` 21) 64 SIsNotZ) (S lv))
changeG x lv prf = changeG' x lv $ replace (definiG x lv) prf

changeG0' : (x : Nat)
  -> Any (Not . Limited)
      (if (modNatNZ (x `minus` 21) 64 SIsNotZ) == 0 && x > 21 && (modNatNZ (modNatNZ (x `minus` 21) 64 SIsNotZ) 2 SIsNotZ) == 1
        then [[6] `dsp2` (Just (divSeq (divNatNZ (x `minus` 21) 64 SIsNotZ)))]
        else [])
    -> Any (Not . Limited) [Just (divSeq (divNatNZ (x `minus` 21) 64 SIsNotZ))]
changeG0' x prf with ((modNatNZ (x `minus` 21) 64 SIsNotZ) == 0 && x > 21 && (modNatNZ (modNatNZ (x `minus` 21) 64 SIsNotZ) 2 SIsNotZ) == 1) proof p
  changeG0' x prf | False = absurd prf
  changeG0' x prf | True  = dsp2Cut prf
changeG0 : (x:Nat) -> Any (Not . Limited) (allDivSeqG x 0)
  -> Any (Not . Limited) [Just (divSeq (divNatNZ (x `minus` 21) 64 SIsNotZ))]
changeG0 x prf = changeG0' x $ replace (definiG0 x) prf

postulate defini3 : (x, lv:Nat)
  -> Either (Any (Not . Limited) $ allDivSeq x lv)
    (Either (Any (Not . Limited) $ allDivSeq (divNatNZ ((x+7)*3) 4 SIsNotZ) lv)
      (Either (Any (Not . Limited) $ allDivSeq (x*6+3) lv)
        (Either (Any (Not . Limited) $ allDivSeq (x*3+6) lv)
          (Either (Any (Not . Limited) $ allDivSeq (divNatNZ ((x+1)*3) 2 SIsNotZ) lv)
            (Either (Any (Not . Limited) $ allDivSeq (x*12+9) lv)
              (Either (Any (Not . Limited) $ allDivSeq (divNatNZ ((x+3)*3) 8 SIsNotZ) lv)
                      (Any (Not . Limited) $ allDivSeq (divNatNZ (x `minus` 21) 64 SIsNotZ) lv)))))))
    -> (Any (Not . Limited) $ allDivSeq x (S lv))
postulate defini0 : (x:Nat)
  -> Either (Any (Not . Limited) $ [Just (divSeq x)])
    (Either (Any (Not . Limited) $ [Just (divSeq (divNatNZ ((x+7)*3) 4 SIsNotZ))])
      (Either (Any (Not . Limited) $ [Just (divSeq (x*6+3))])
        (Either (Any (Not . Limited) $ [Just (divSeq (x*3+6))])
          (Either (Any (Not . Limited) $ [Just (divSeq (divNatNZ ((x+1)*3) 2 SIsNotZ))])
            (Either (Any (Not . Limited) $ [Just (divSeq (x*12+9))])
              (Either (Any (Not . Limited) $ [Just (divSeq (divNatNZ ((x+3)*3) 8 SIsNotZ))])
                      (Any (Not . Limited) $ [Just (divSeq (divNatNZ (x `minus` 21) 64 SIsNotZ))])))))))
    -> (Any (Not . Limited) $ allDivSeq x 0)

-- ProofColDivSeqLvDown.idrでlvDown'を証明したからOK
postulate lvDown : (n, lv:Nat) -> P n lv -> P n (pred lv)
-- ########################################

--            from sub0xxxxx
-- ########################################
-- 01 3(6x+1) --B[1,-2]--> 3x
postulate b18x3To3x' :
  (k:Nat) -> P (S (plus (plus (plus k k) (plus k k)) (plus k k))) 1 -> P k 2

-- 02 3(18x+4) --A[6,-4]->E[2,-4]--> 3(2x)
postulate ae54x12To6x' :
  (l:Nat) -> P (S (S (plus (plus (plus (plus (plus l l) l) (plus (plus l l) l))
                              (S (plus (plus (plus l l) l) (plus (plus l l) l))))
                        (S (plus (plus (plus l l) l) (plus (plus l l) l)))))) 1
    -> P (plus l l) 3

-- 03 3(18x+10) --A[6,-4]->C[4,-4]--> 3(8x+3)
postulate ac54x30To24x9' :
  (l:Nat) -> P (S (S (S (plus (plus (plus (plus (plus l l) l) (S (plus (plus l l) l))) (S (S (plus (plus (plus l l) l) (S (plus (plus l l) l))))))
                         (S (S (plus (plus (plus l l) l) (S (plus (plus l l) l))))))))) 1
    -> P (S (S (S (plus (plus (plus l l) (plus l l)) (plus (plus l l) (plus l l)))))) 3

-- 04 3(18x+16) --A[6,-4]->B[1,-2]--> 3(4x+3)
postulate ab54x30To12x9' :
  (l:Nat) -> P (S (S (S (S (plus (plus (plus (plus (plus l l) l) (S (S (plus (plus l l) l))))
                                  (S (S (S (plus (plus (plus l l) l) (S (S (plus (plus l l) l))))))))
                            (S (S (S (plus (plus (plus l l) l) (S (S (plus (plus l l) l)))))))))))) 1
    -> P (S (S (S (plus (plus l l) (plus l l))))) 3

-- 二重否定除去
postulate dne : ((a -> Void) -> Void) = a
-- 対偶
postulate contraposition2 :
  {A, B : Nat -> Type} -> (x : Nat) -> (Not $ B x -> Not $ A x) -> (A x -> B x)
-- 05 3(3x+2) --C[4,-4]--> 3x
postulate contraC9x6To3x' :
  (j:Nat) -> Not $ P j 2 -> Not $ P (S (S (plus (plus j j) j))) 1
{-
contraC9x6To3x' j =
  rewrite definiP j                           2 in
  rewrite definiP (S (S (plus (plus j j) j))) 1 in
  rewrite dne {a = All Limited (allDivSeq (plus (plus j j) j) 2)} in
  rewrite dne {a = All Limited (allDivSeq (S (S (plus (plus (plus (plus j j) j) (S (S (plus (plus j j) j)))) (S (S (plus (plus j j) j)))))) 1)} in ?rhs
-}
c9x6To3x' :
  (j:Nat) -> P (S (S (plus (plus j j) j))) 1 -> P j 2
c9x6To3x' j = contraposition2 j $ contraC9x6To3x' j

-- 06 3(12x+3) --E[2,-4]--> 3x
postulate e36x9To3x' :
  (l:Nat) -> P (S (S (S (plus (plus (plus (plus l l) (plus l l)) (plus (plus l l) (plus l l))) (plus (plus l l) (plus l l)))))) 1 -> P l 2

-- 07 3(36x+9) --F[5,-2]->C[4,-4]--> 3(32x+7)
postulate fc108x27To96x21' :
  (o:Nat) -> P (S (S (S (S (plus (plus (plus (plus (plus (plus o o) o) (plus (plus o o) o)) (S (plus (plus (plus o o) o) (plus (plus o o) o))))
                                  (S (plus (plus (plus (plus o o) o) (plus (plus o o) o)) (S (plus (plus (plus o o) o) (plus (plus o o) o))))))
                            (S (plus (plus (plus (plus o o) o) (plus (plus o o) o)) (S (plus (plus (plus o o) o) (plus (plus o o) o)))))))))) 1
    -> P (S (S (S (S (S (S (S (plus (plus (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))) (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))))
                                                            (plus (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))) (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o)))))))))))) 3

-- 08 3(36x+21) --F[5,-2]->B[1,-2]--> 3(16x+9)
postulate fb108x63To48x27' :
  (o:Nat) -> P (S (S (S (S (S (plus (plus (plus (plus (plus (plus o o) o) (S (plus (plus o o) o)))
                                           (S (S (plus (plus (plus o o) o) (S (plus (plus o o) o))))))
                                     (S (S (plus (plus (plus (plus o o) o) (S (plus (plus o o) o))) (S (S (plus (plus (plus o o) o) (S (plus (plus o o) o)))))))))
                               (S (S (plus (plus (plus (plus o o) o) (S (plus (plus o o) o))) (S (S (plus (plus (plus o o) o) (S (plus (plus o o) o)))))))))))))) 1
    -> P (S (S (S (S (S (S (S (S (S (plus (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))) (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))))))))))))) 3

-- 09 3(36x+33) --F[5,-2]->E[2,-4]--> 3(8x+7)
postulate fe108x99To24x21' :
  (o:Nat) -> P (S (S (S (S (S (S (plus (plus (plus (plus (plus (plus o o) o) (S (S (plus (plus o o) o))))
                                              (S (S (S (plus (plus (plus o o) o) (S (S (plus (plus o o) o))))))))
                                        (S (S (S (plus (plus (plus (plus o o) o) (S (S (plus (plus o o) o))))
                                                       (S (S (S (plus (plus (plus o o) o) (S (S (plus (plus o o) o))))))))))))
                                  (S (S (S (plus (plus (plus (plus o o) o) (S (S (plus (plus o o) o))))
                                                 (S (S (S (plus (plus (plus o o) o) (S (S (plus (plus o o) o)))))))))))))))))) 1
    -> P (S (S (S (S (S (S (S (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o)))))))))) 3

-- 10 3(36x+6) --F[5,-2]->E[2,-4]--> 3(8x+1)
postulate fe108x18To24x3' :
  (o:Nat) ->  P (S (S (S (S (plus (plus (plus (plus (plus (plus o o) o) (plus (plus o o) o)) (plus (plus (plus o o) o) (plus (plus o o) o)))
                                  (S (plus (plus (plus (plus o o) o) (plus (plus o o) o)) (plus (plus (plus o o) o) (plus (plus o o) o)))))
                            (S (plus (plus (plus (plus o o) o) (plus (plus o o) o)) (plus (plus (plus o o) o) (plus (plus o o) o))))))))) 1
    -> P (S (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o)))) 3

-- 11 3(36x+18) --F[5,-2]->C[4,-4]--> 3(32x+15)
postulate fc108x54To96x45' :
  (o:Nat) -> P (S (S (S (S (S (plus (plus (plus (plus (plus (plus o o) o) (S (plus (plus o o) o)))
                                           (S (plus (plus (plus o o) o) (S (plus (plus o o) o)))))
                                     (S (S (plus (plus (plus (plus o o) o) (S (plus (plus o o) o))) (S (plus (plus (plus o o) o) (S (plus (plus o o) o))))))))
                               (S (S (plus (plus (plus (plus o o) o) (S (plus (plus o o) o))) (S (plus (plus (plus o o) o) (S (plus (plus o o) o))))))))))))) 1
    -> P (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (plus (plus (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))) (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))))
                                                            (plus (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))) (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o)))))))))))))))))))) 3

-- 12 3(36x+30) --F[5,-2]->B[1,-2]--> 3(16x+13)
postulate fb108x90To48x39' :
  (o:Nat) -> P (S (S (S (S (S (S (plus (plus (plus (plus (plus (plus o o) o) (S (S (plus (plus o o) o))))
                                              (S (S (plus (plus (plus o o) o) (S (S (plus (plus o o) o)))))))
                                        (S (S (S (plus (plus (plus (plus o o) o) (S (S (plus (plus o o) o)))) (S (S (plus (plus (plus o o) o) (S (S (plus (plus o o) o)))))))))))  (S (S (S (plus (plus (plus (plus o o) o) (S (S (plus (plus o o) o))))
                                                 (S (S (plus (plus (plus o o) o) (S (S (plus (plus o o) o))))))))))))))))) 1
    -> P (S (S (S (S (S (S (S (S (S (S (S (S (S (plus (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))) (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))))))))))))))))) 3

-- 13 3(36x+12) --F[5,-2]->B[1,-2]--> 3(16x+5)
postulate fb108x36To48x15' :
  (o:Nat) -> P (S (S (S (S (S (plus (plus (plus (plus (plus (plus o o) o) (plus (plus o o) o)) (S (plus (plus (plus o o) o) (plus (plus o o) o))))
                                     (S (S (plus (plus (plus (plus o o) o) (plus (plus o o) o)) (S (plus (plus (plus o o) o) (plus (plus o o) o)))))))
                               (S (S (plus (plus (plus (plus o o) o) (plus (plus o o) o)) (S (plus (plus (plus o o) o) (plus (plus o o) o)))))))))))) 1
    -> P (S (S (S (S (S (plus (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))) (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))))))))) 3

-- 14 3(36x+24) --F[5,-2]->E[2,-4]--> 3(8x+5)
postulate fe108x72To24x15' :
  (o:Nat) ->  P (S (S (S (S (S (S (plus (plus (plus (plus (plus (plus o o) o) (S (plus (plus o o) o)))
                                              (S (S (plus (plus (plus o o) o) (S (plus (plus o o) o))))))
                                        (S (S (S (plus (plus (plus (plus o o) o) (S (plus (plus o o) o))) (S (S (plus (plus (plus o o) o) (S (plus (plus o o) o))))))))))
                                  (S (S (S (plus (plus (plus (plus o o) o) (S (plus (plus o o) o))) (S (S (plus (plus (plus o o) o) (S (plus (plus o o) o)))))))))))))))) 1
    -> P (S (S (S (S (S (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o)))))))) 3

-- 15 3(36x+36) --F[5,-2]->C[4,-4]--> 3(32x+31)
postulate fc108x108To96x93' :
  (o:Nat) -> P (S (S (S (S (S (S (S (plus (plus (plus (plus (plus (plus o o) o) (S (S (plus (plus o o) o))))
                                                 (S (S (S (plus (plus (plus o o) o) (S (S (plus (plus o o) o))))))))
                                           (S (S (S (S (plus (plus (plus (plus o o) o) (S (S (plus (plus o o) o))))
                                                             (S (S (S (plus (plus (plus o o) o) (S (S (plus (plus o o) o)))))))))))))
                                     (S (S (S (S (plus (plus (plus (plus o o) o) (S (S (plus (plus o o) o))))
                                                       (S (S (S (plus (plus (plus o o) o) (S (S (plus (plus o o) o)))))))))))))))))))) 1
    -> P (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (plus (plus (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))) (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))))
                                                            (plus (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))) (plus (plus (plus o o) (plus o o)) (plus (plus o o)
            (plus o o)))))))))))))))))))))))))))))))))))) 3
-- ########################################

--            from ProofColDivSeqMain
-- ########################################
all2Sub : {pp : a -> Type} -> (xs, ys : List a)
  -> All pp ((x::xs) ++ ys) -> (pp x, All pp (xs ++ ys))
all2Sub xs ys (Cons p ps) = (p, ps)

-- これが肝
all2 : {pp : a -> Type} -> (xs, ys : List a)
  -> All pp (xs ++ ys) -> All pp xs
all2 []      ys _   = NilA
all2 (x::xs) ys prf =
  let (prf2, prf3) = all2Sub xs ys prf
  in Cons prf2 (all2 xs ys prf3)
-- ########################################


data Name : Type where
  MN : Int -> Name

data IsVar : Name -> List Name -> Type where
  First : IsVar n (n :: ns)
  Later : IsVar n ns -> IsVar n (m :: ns)

data Expr : List Name -> Type where
  CLocal : (prf : IsVar x vars) -> Expr vars

data FunDecl : Type where
  MkFunDecl : (vars : List Name) -> Expr vars -> FunDecl

funDeclWorks : List FunDecl
funDeclWorks = [MkFunDecl [MN 0] (CLocal First)]

foo : Expr [MN 0]
foo = CLocal First

dpairFails : List (vars : List Name ** Expr vars)
dpairFails = [([MN 0] ** CLocal First)]

dpairWithExtraInfoWorks : List (vars : List Name ** Expr vars)
dpairWithExtraInfoWorks = [([MN 0] ** CLocal {x=MN 0} (First {ns=[]}))]

dpairWithExtraInfoBad : List (vars : List Name ** Expr vars)
dpairWithExtraInfoBad = [([MN 0] ** CLocal {x=MN 0} (First {ns=[MN 0]}))]


import Data.Vect
import Decidable.Equality

myExactLength : {m : _} -> (len : Nat) -> (input : Vect m a) -> Maybe (Vect len a)
myExactLength len input = case decEq m len of
                                 Yes Refl => Just input
                                 No contra => Nothing


module Kan
import Control.Monad.Identity
import Data.Morphisms

data Ran : (f : Type -> Type) -> (g : Type -> Type) -> (a : Type) -> Type where
  R : ({b : Type} -> (a -> f b) -> g b) -> Ran f g a

data Lan : (f : Type -> Type) -> (g : Type -> Type) -> (a : Type) -> Type where
  L : (f b -> a) -> g b -> Lan f g a

Functor (Ran f g) where
  map f ran = R (\k => let (R f') = ran in f' (k . f))

Functor (Lan f g) where
  map f (L g h) = L (f . g) h

Yoneda : (Type -> Type) -> Type -> Type
Yoneda f a = Ran Identity f a

CoYoneda : (Type -> Type) -> Type -> Type
CoYoneda f a = Lan Identity f a

Codensity : (Type -> Type) -> Type -> Type
Codensity m a = Ran m m a

Reducer : Type -> Type
Reducer a = Ran Endomorphism Endomorphism a


foo : Int -> Int
foo x = case isLT of
            Yes => x*2
            No => x*4
    where
       data MyLT = Yes | No

       isLT : MyLT
       isLT = if x < 20 then Yes else No

even : Nat -> Bool
even Z = True
even (S k) = odd k where
  odd : Nat -> Bool
  odd Z = False
  odd (S k) = even k

test : List Nat
test = [c (S 1), c Z, d (S Z)]
  where c : Nat -> Nat
        c x = 42 + x

        d : Nat -> Nat
        d y = c (y + 1 + z y)
              where z : Nat -> Nat
                    z w = y + w


-- -------------------------------------------------------------- [ Common.idr ]
-- Module      : Common
-- Description : Common things for advanced crypto
-- Copyright   : (c) Jan de Muijnck-Hughes
-- License     : see LICENSE
-- --------------------------------------------------------------------- [ EOF ]
module Crypto.Adv.Common

namespace BPolicy
    data BPolicy : Type where
      Leaf : (value : a) -> BPolicy
      Or   : (l : BPolicy) -> (r : BPolicy) -> BPolicy
      And  : (l : BPolicy) -> (r : BPolicy) -> BPolicy

namespace TPolicy
  data TPolicy : (threshold : Nat) -> (count : Nat) -> Type where
    TLeaf      : (value : a) -> TPolicy 1 1
    NKPolicy   : (k : Nat) -> (children : Vect n (TPolicy t m)) -> TPolicy k n
    OneKPolicy : (children : Vect n (TPolicy t m)) -> TPolicy 1 n
    KKPolicy   : (children : Vect n (TPolicy t m)) -> TPolicy n n

foobar : BPolicy
foobar = Or (And
              (Or (Leaf "a:xxx0") (Leaf "a:xx0x"))
              (Leaf "a:x0xx"))
            (Leaf "a:0xxx")

foobar' : TPolicy 1 2
foobar' = OneKPolicy [KKPolicy
                      [OneKPolicy
                          [TLeaf "a:xx0", TLeaf "a:xx0x"],
                       TLeaf "a:x0xx"],
                      TLeaf "a:0xxx"]
-- 1of2 ( 2of2 ( 1of2 ( a:xxx0 , a:xx0x ) , a:x0xx ) , a:0xxx )


module Capabilities

-- Inspired by http://okmij.org/ftp/Computation/lightweight-static-guarantees.html

interface Sortable (f : Type -> Type) where
  Sorted : (a : Type) -> (a -> a -> Ordering) -> Type

  sortBy : (cmp : a -> a -> Ordering) -> f a -> Sorted a cmp
  mergeBy : (cmp : a -> a -> Ordering) -> Sorted a cmp  -> Sorted a cmp -> Sorted a cmp
  get : Sorted a cmp -> f a

Sortable List where
  Sorted a cmp = List a

  sortBy cmp xs = List.sortBy cmp xs
  mergeBy cmp xs ys = List.mergeBy cmp xs ys
  get xs = xs

example : Sortable f => (cmp : a -> a -> Ordering) -> f a -> f a -> f a
example @{sortable} cmp xs ys =
  let sxs = sortBy cmp xs in
  let sys = sortBy cmp ys in
  let sms = mergeBy @{sortable} cmp sxs sys in
  get sms


module Replica.Other.Free

%default total

public export
data Ap : (Type -> Type) -> Type -> Type where
  Pure : a -> Ap f a
  MkAp : f a -> Ap f (a -> b) -> Ap f b

export
Functor (Ap f) where
  map func (Pure x) = Pure (func x)
  map func (MkAp x y) = MkAp x (map (func .) y)

export
Applicative (Ap f) where
  pure = Pure
  (<*>) (Pure y) x = map y x
  (<*>) e@(MkAp y z) x = MkAp y $ assert_smaller e (map flip z) <*> x

export
runAp : Applicative g => (forall x. f x -> g x) -> Ap f c -> g c
runAp func (Pure x) = pure x
runAp func (MkAp x y) = runAp func y <*> func x

export
runApM : Monoid m => (forall x. f x -> m) -> Ap f c -> m
runApM func (Pure x) = neutral
runApM func (MkAp x y) = runApM func y <+> func x

export
liftAp : f a -> Ap f a
liftAp x = MkAp x (Pure id)


||| This module is about *aliases* for pins that belong to some hardware protocol (bus)
module Arduino.Protocols

import Arduino.Boards

import Data.Vect
import Data.Vect.Quantifiers

%default total
%access public export

-----------------
--- Protocols ---
-----------------

-- TODO to think whether can pins used in different protocols intersect with each other?
-- TODO to think why digital, PWM, DAC and ADC are not just yet another protocols?

record HardwareProtocol where
  constructor MkProtocol
  protocolName : String
  protocolPinsCount : Nat

interface HasDigitalPins board => HasProtocol (0 prot : HardwareProtocol) (0 board : Board) where
  ProtocolPinGroupsCount : Nat

  ProtocolPins : Fin ProtocolPinGroupsCount -> Vect (protocolPinsCount prot) Pin

  AllProtocolPins : Vect (ProtocolPinGroupsCount * protocolPinsCount prot) Pin
  AllProtocolPins = ?flattenSomehow -- map ProtocolPins (allFinsTo ProtocolPinGroupsCount)
    where
      allFinsTo : (n : Nat) -> Vect n (Fin n)
      allFinsTo Z     = []
      allFinsTo (S n) = last :: map weaken (allFinsTo n)

  AllProtocolPinsDigital : All (CanBeDigital {board}) AllProtocolPins
  AllProtocolPinsDifferent : AllDiffer AllProtocolPins

--- UART ---

UART : HardwareProtocol
UART = MkProtocol "UART" 2

TX : HasProtocol UART board => Fin (ProtocolPinGroupsCount {prot=UART} {board}) -> Pin
RX : HasProtocol UART board => Fin (ProtocolPinGroupsCount {prot=UART} {board}) -> Pin

TX {board} = Data.Vect.index 0 . ProtocolPins {prot=UART} {board}
RX {board} = Data.Vect.index 1 . ProtocolPins {prot=UART} {board}

--- I2C ---

I2C : HardwareProtocol
I2C = MkProtocol "I2C" 2

SDA : HasProtocol I2C board => Fin (ProtocolPinGroupsCount {prot=I2C} {board}) -> Pin
SCL : HasProtocol I2C board => Fin (ProtocolPinGroupsCount {prot=I2C} {board}) -> Pin

SDA {board} = Data.Vect.index 0 . ProtocolPins {prot=I2C} {board}
SCL {board} = Data.Vect.index 1 . ProtocolPins {prot=I2C} {board}

--- SPI ---

SPI : HardwareProtocol
SPI = MkProtocol "SPI" 4

MISO : HasProtocol SPI board => Fin (ProtocolPinGroupsCount {prot=SPI} {board}) -> Pin
MOSI : HasProtocol SPI board => Fin (ProtocolPinGroupsCount {prot=SPI} {board}) -> Pin
SCK  : HasProtocol SPI board => Fin (ProtocolPinGroupsCount {prot=SPI} {board}) -> Pin
SS   : HasProtocol SPI board => Fin (ProtocolPinGroupsCount {prot=SPI} {board}) -> Pin

MISO {board} = Data.Vect.index 0 . ProtocolPins {prot=SPI} {board}
MOSI {board} = Data.Vect.index 1 . ProtocolPins {prot=SPI} {board}
SCK  {board} = Data.Vect.index 2 . ProtocolPins {prot=SPI} {board}
SS   {board} = Data.Vect.index 3 . ProtocolPins {prot=SPI} {board}


import Data.Vect

distrL : a -> List b -> List (a, b)
distrL a = map (a,)

distrR : b -> List a -> List (a, b)
distrR b = map (, b)

-- closeVect : List (n ** Vect n Nat)
-- closeVect = map (** flip replicate 3) [0..10]

insert : List (Nat,Nat,Nat,Nat)
insert = map (\ f => f (the Nat 0) (the Nat 1))
         [(,,2,3), (2,,,3), (2,3,,)]


import Data.Vect
||| Modeled from https://eb.host.cs.st-andrews.ac.uk/drafts/impldtp.pdf page 10

data ANTContext : {n: Nat} -> Type

data Binding : ANTContext -> Type

||| All contexts that are definable using this structure are valid.
|||
||| They can have variables added into them as specified by Binding
data ANTContext : {n: Nat} -> Type where
  Empty : ANTContext {n=0}
  AddBinding : (ctx : ANTContext {n=n})
                -> (b : Binding ctx)
                -> ANTContext {n=S n}

data ANTExp : ANTContext -> Type

||| This simple returns TypeN
||| Since data constructors can't recursively reference each other, this
||| breaks the loop
mkTypeN : (g : ANTContext) -> Nat -> ANTExp g

typeForExp : {g : ANTContext} -> ANTExp g -> ANTExp g

||| Context B encompass context A if any expression that is implied
||| by A can also be implied by B. This means that no matter what vars
||| are added to a context, it still implies *at least* the same
||| expressions as it did before. B is a descendent of A
|||
||| In other words, an *inner* context can reference anything from
||| an *outer* context, and all context applied in the outer also
||| apply to the inner.
|||
||| Ex: let x = (15 : Int) in (let y = 3 : Int) in x + y)
|||                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- outer context (A), x is defined
|||      inner context (B), x and y are defined -- ^^^^^
data ContextEncompasses : (des : ANTContext) -> (anc : ANTContext)
     -> Type where
  Self : {self : ANTContext} -> ContextEncompasses self self
  Child : (b : Binding anc) -> {prf : ContextEncompasses des anc}
         -> ContextEncompasses des (AddBinding anc b)

||| Represents a variable into the context. Note this is not the same thing
||| as creating a Lam/Pi/Let Expression. It is rather the resulting
||| context to be used *within* the body of a lam/pi/let expression that
||| has been created. Ex:
|||
||| Let x = (16 : Int) in (....)
|||                        ^^^^-- binding should be added to context for this part
|||
||| See ANTExp.Lam to create a Lam, etc.
|||
data Binding : (ctx : ANTContext) -> Type where
  |||  G |- S' : Type(i)
  |||  ------------------
  |||  G;\x:S' |- valid
  LamBinding : (S' : ANTExp ctx)
            -> (typeForExp S' = (mkTypeN ctx i)) -> Binding ctx

  |||  G |- S' : Type(i)
  |||  ------------------
  |||  G;forall x:S' |- valid
  PiBinding : (S' : ANTExp ctx)
            -> (typeForExp S' = (mkTypeN ctx i)) -> Binding ctx

  |||  G |- S' : Type(i)    G |- s : S'
  |||  --------------------------------
  |||  G;let x = s : S' |- valid
  LetBinding : (S' : ANTExp ctx)
            -> (typeForExp S' = (mkTypeN ctx i))
            -> (s : ANTExp ctx)
            -> (typeForExp s = S')
            -> Binding ctx

--isEquivalentExp : ANTExp des -> ANTExp anc -> ContextEncompasses des anc -> Type

alphaEq : ANTExp g -> ANTExp g -> Type

||| This is a *valid* expression of the context. An expression can
||| only be created if a context implies it by running one or more of
||| its base rules
|||
||| Basically if a rule ends in:
|||    ....
|||    -------
|||    G |- x
||| then this represents that "G |- x" is true, according to the rules
data ANTExp : ANTContext -> Type where

  ||| This is our own made up rule. It's not in the design doc, but it
  ||| inherently implied:
  |||
  |||  G |- X
  |||  ---------
  |||  G;a |- X
  |||
  ||| It means that if you add a new variable, a to G, everything that
  ||| G implied before is still valid
  ChildContextImpliesSame :
    (parent : ANTContext) -> (b : Binding parent) -> ANTExp parent -> ANTExp (AddBinding parent b)

  |||       G |- valid
  |||  Type ------------
  |||       G |- Type(n) : Type(n+1)
  ||| Part 1: Every context implies Type(n) exists
  ||| Part 2 of this, that TypeN has a Type(n+1) is in typeForExpr
  TypeN : (g : ANTContext) -> (n : Nat) -> ANTExp g

  |||       (\x:S) in G
  |||  Var1 ------------
  |||       G |- x : S
  |||
  |||       (forall x:S) in G
  |||  Var2 ------------
  |||       G |- x : S
  |||
  |||       (let x = s : S) in G
  |||  Var3 ------------
  |||       G |- x : S
  ||| Part 1: A valid var lookup
  ||| Part 2 of this, that x has a type of S, is in typeForExpr
  Var : (g : ANTContext {n} ) -> (n : Fin n) -> ANTExp g

  |||         G;forall x:S' |- T : Type(m)    G |- S' : Type(n)
  ||| Forall  ------------------------------------------------- (Exists(p).m <= p, n <= p)
  |||         G |- (x : S') -> T : Type(p)
  |||
  ||| Note that T is in (G;forall x:S'), but even so, (x : S') -> T is
  ||| in G The idea being if you can create a context from G that has
  ||| the additional "forall x:S'" in it and it implies that T exists,
  ||| then you can say that "(x : S') -> T" aka "forall x:S'.T" is implied
  ||| in G
  -- TODO 2 this only represents that (x : S') -> T is created, it doesn't specify
  --  the type as Type(p).. should we specify type in typeForExp??? seems so
  Pi : { n : Nat }
    -> (g : ANTContext)
    -> (S' : ANTExp g)
    -> (prf : typeForExp S' = (mkTypeN g n))
    -> (T : ANTExp (AddBinding g (PiBinding S' prf)))
    -> ANTExp g

  |||     G;\x:S' |- e : T   G |- (x : S') -> T : Type(n)
  ||| Lam ---------------------------------------------
  |||     G |- \x:S'.e : (x : S') -> T
  |||
  ||| Note, not in rule, but implied (S' : Type(n))... must be because
  ||| that's the only way to create G;\x:S', see Binding
  |||
  ||| Given a context, an arg type within that context, and an expression
  ||| that uses that var, returns a Lam.
  Lam : (g : ANTContext {n=i})
    -> (S' : ANTExp g)
    -> (prf : typeForExp S' = (mkTypeN g i)) -- necessary because LamBinding needs it
    -> (T : ANTExp g)
    -> (e : ANTExp (AddBinding g (LamBinding S' prf)))
    -> alphaEq (typeForExp e) (ChildContextImpliesSame g (LamBinding S' prf) T)
    -> ANTExp g

alphaEq (ChildContextImpliesSame parent b x) (ChildContextImpliesSame parent b y) = alphaEq x y
alphaEq (ChildContextImpliesSame parent b x) y@(TypeN (AddBinding parent b) n) = alphaEq x y
alphaEq (ChildContextImpliesSame parent b x) (Var (AddBinding parent b) n2) = Void --TODO this is wrong, because if child is a var, then it could be referencing the same
alphaEq (ChildContextImpliesSame parent b x) (Pi (AddBinding parent b) S' prf T) = Void
alphaEq (ChildContextImpliesSame parent b x) (Lam (AddBinding parent b) S' prf T e y) = Void
alphaEq x@(TypeN g n) y = x = y
alphaEq x@(Var g n2) y = x = y --TODO 2 this is wrong, because we have to take into account a parent context referencing same var as a child, ChildContextImpliesSame
alphaEq x@(Pi g S' prf T) y = x = y
alphaEq x@(Lam g S' prf T e x) y = x = y

mkTypeN g k = TypeN g k

{-

-- lemmaVarStillExists : (g : ANTContext) -> (e : ANTExp g)
--                     -> (e2 : ANTExp (AddVar g binder

lemmaLamTypeOfTypeIsTypeN : (n : Nat) -> (argType : ANTExp g) -> (i : Nat)
                    -> typeForExp argType = mkTypeN g i

lemmaPiTypeOfTypeIsTypeN : (n : Nat) -> (argType : ANTExp g) -> (i : Nat)
  -> typeForExp argType = mkTypeN g i

typeForExp (TypeN g n) = TypeN g (S n)
--typeForExp (Lam g argType exp) = ?typeForExp_rhs_2
typeForExp (Pi g S' prf T) = TypeN g ?chooseNForPi
  where
    chooseNForPi : {g : ANTContext} -> ANTExp g -> ANTExp g -> Nat
    chooseNForPi x y = ?chooseNForPi_rhs

{-
||| All contexts that are definable using this structure are valid.
|||
||| They can have variables added into them.  Variables come in 3
||| times, Lambda, Forall, and Let
|||
||| TODO 3 - Not sure why we need seperate types, idris impl decl says
||| "We record the binding ||| form in the context, as well as the
||| type, because type checking relies on evaluation. In particular,
||| let bound names may reduce."
--TODO 2.5 do we need "n"?
data ANTContext :  {n : Nat} -> Type where
  |||
  |||  --------
  |||  |- valid
  Empty : ANTContext {n}

  |||  G |- S' : Type(i)
  |||  ------------------
  |||  G;\x:S' |- valid
  |||
  |||  G |- S' : Type(i)
  |||  ------------------
  |||  G;forall x:S' |- valid
  |||
  ||| Adds a variable into the context. Note this is not the same
  ||| thing as creating a Lam Expression. It is rather the resulting
  ||| context to be used *within* the body of a lam expression that
  ||| has been created
  |||
  ||| See ANTExp.Lam to create a Lam, etc.
  AddVar : {i : Nat} -> {n : Nat} -> (ctx : ANTContext {n=n})
    -> (binder : Binder)
    -> (S' : ANTExp ctx)
    -> (typeForExp S' = (mkTypeN ctx i))
    -> ANTContext {n=n} -- HACK {n=S n}

    |||  G |- S : Type(i)
  |||  ------------------
  |||  G;forall x:S |- valid
  AddForallVar : {S : Expr} -> {i : Nat} -> (ctx : ANTContext)
    -> ImpliesExpr ctx (TypedExp S (TypeN i))
    -> ANTContext {S n}

  |||  G |- S : Type(i)   G |- s : S
  |||  --------------------------------
  |||  G;let x => s : S |- valid
  AddForallVar : {s : Expr} -> {S : Expr} -> {i : Nat} -> (ctx : ANTContext)
    -> ImpliesExpr ctx (TypedExp S (TypeN i))
    -> ImpliesExpr ctx (TypedExp s S)
    -> ANTContext {S n}

data ImpliesExpr : ANTContext -> TypedExp -> Type

||| Context
||| @n total number of vars
data ANTContext : {n : Nat} -> Type
data Expr : ANTContext -> Type
data ANTTypedExp : {ctx : ANTContext} -> (exp : Expr ctx) -> (typ : Expr ctx) -> Type where

||| Modeled from https://eb.host.cs.st-andrews.ac.uk/drafts/impldtp.pdf page 10
data ANTContext : {n : Nat} -> Type where
  |||
  |||  --------
  |||  |- valid
  Empty : ANTContext {n=0}

  |||  G |- S : Type(i)
  |||  ------------------
  |||  G;\x:S |- valid
  AddVar : (ctx : ANTContext {n}) -> Expr ctx -> ANTContext {n=(S n)}

getExprLemma1 : ANTContext -> Expr ctx -> Expr (AddVar ctx unrelatedVar)

||| TODO 2 getExpr doesn't prove it's getting the right expression
||| it could return anything. We need a view
getExpr : {n : Nat} -> (ctx : ANTContext {n}) -> Fin n -> ANTTypedExp ctx

betaEq : Expr ctx -> Expr ctx -> Type
betaEq = ?x

-- I Int | BI Integer | Fl Double | Ch Char | Str String

||| This represents when a context implies something. It is laid out
||| as the basic rules of the type theory system
||| Modeled from https://eb.host.cs.st-andrews.ac.uk/drafts/impldtp.pdf page 11
data Expr : ANTContext -> Type where
  |||  G |- valid
  |||  ------------
  |||  G |- Int : Type(0)
  ||| Part 1: the type "Int" can be implied from any context
  IntT : {ctx : ANTContext} -> Expr ctx

  |||  G |- valid
  |||  ------------
  |||  G |- Type(n) : Type(n+1)
  ||| Part 1: Every context implies Type(n) exists
  ||| Part 2 of this, that TypeN has a Type(n+1) is in data ANTType
  TypeN : (n : Nat) -> (g : ANTContext) -> Expr g

  |||  G |- valid
  |||  ------------
  |||  G |- i : Int
  IntC : {g : ANTContext} -> (i : Int) -> Expr g

  |||     G |- f : (x : S) -> T   G |- s : S
  ||| App ----------------------------------
  |||     G |- f s : T[s/x]
  App : (ctx : ANTContext {n})
    -> Ex
    -> (sInd : Fin n)
    -> betaEq (getExpr ctx fInd) (getExpr ctx sInd)
    -> Expr ctx
  |||     G;\x:S |- e : T   G |- (x : S) -> T : Type(n)
  ||| Lam ---------------------------------------------
  |||     G |- \x:S.e : (x : S) -> T
  ||| Given a context, an arg type within that context, and an expression
  ||| that uses that var, returns a Lam (variable name not specified
  ||| since we are using DeBrujin indexes)
  Lam : (ctx : ANTContext) -> (argType : Expr ctx)
    -> Expr (AddVar ctx argType) -> Expr ctx

||| This is an expression with an associated type.
|||
||| Note that the type is just a plain expression, even though, every
||| expression must have a type. This is because we can't have a typed
||| expression refer to itself in it's declaration, or it becomes an
||| infinite loop.
data ANTTypedExp : {ctx : ANTContext} -> (exp : Expr ctx) -> (typ : Expr ctx) -> Type where
  |||  G |- valid
  |||  ------------
  |||  G |- Type(n) : Type(n+1)
  ||| Part 2 TypeN has a Type(n+1) is in data ANTType
  TypeSNofN : (n : Nat) -> (g : ANTContext) -> ANTTypedExp (TypeN n g) (TypeN (S n) g)

  |||  G |- valid
  |||  ------------
  |||  G |- Int : Type(0)
  ||| Part 2 The type of Int is Type(0)
  Type0OfIntT : (g : ANTContext) -> ANTTypedExp IntT (TypeN 0 g)

  |||  G |- valid
  |||  ------------
  |||  G |- Int : Type(0)
  ||| Part 2 The type of IntC is IntT
  IntTOfInt : (i : Int) -> (g : ANTContext) -> ANTTypedExp (IntC i) IntT

getExprLemma1 x y = ?getExprLemma1_rhs

getExpr {n = Z} Empty FZ impossible
getExpr {n = Z} Empty (FS _) impossible
getExpr {n = (S k)} (AddVar ctx val) FZ = getExprLemma1 ctx val
getExpr {n = (S k)} (AddVar ctx val) (FS x) = getExprLemma1 ctx (getExpr ctx x)
-}

{-

||| Constants, c ::= Type i  (type universes)
|||                | i (integer literal)
|||                | str (string literal)
data ANTConst : Type where
  CType : Nat -> ANTConst
  CInt : Int -> ANTConst
  CStr : String -> ANTConst

||| Binders, b ::= \x : t (abstraction)
|||              | let x => t : t (let binding)
|||              | forall x : t (function space)
data ANTBinder : Type where
  BAbs : {n : Nat} -> ANTBinder
  BLet : {n : Nat} -> ANTBinder
  BForall : {n : Nat} -> ANTBinder

||| Terms, t ::= c (constant)
|||            | x (variable)
|||            | b. t (binding)
|||            | t t (application)
|||            | T (type constructor)
|||            | D (data constructor)
data ANTTerm : Type where
  TConst : ANTConst -> ANTTerm
  ||| each var has a name that must be unique
  TVar : String -> ANTTerm
  TBinding : ANTBinder -> ANTTerm
  TApp : ANTTerm -> ANTTerm -> ANTTerm
  TTypeCons : ANTTerm -- TODO 2 how to represent this?
  TDataCons : ANTTerm -- TODO 2 how to represent this?

data ContextualExpr : ANTTerm -> Type where
  TConst :

data ANTContext :  {n : Nat} -> Type where
  |||
  |||  --------
  |||  |- valid
  Empty : ANTContext {n}

  |||  G |- S : Type(i)
  |||  ------------------
  |||  G;\x:S |- valid
  AddLambdaVar : (n : Nat) -> (i : Nat) -> (varName : String)
    -> (ctx : ANTContext {n})
    -> (S : ContextualExpr ctx (TypeN i))
    -> ANTContext {n=S n}

-}
-}


module BatchedQueue

import Queue

%default total
%access private

export
data BatchedQueue a = BQ (List a) (List a)

check : List a -> List a -> BatchedQueue a
check [] r = BQ (reverse r) []
check f  r = BQ f           r

export
Queue BatchedQueue where
  empty = BQ [] []
  isEmpty (BQ f r) = isNil f

  snoc (BQ f r) x = check f (x :: r)

  head (BQ []       _) = idris_crash "empty queue"
  head (BQ (x :: f) r) = x

  tail (BQ []       _) = idris_crash "empty queue"
  tail (BQ (x :: f) r) = check f r


import Data.Vect

PIN : Type
PIN = Vect 4 Char

data ATMState = Ready | CardInserted | Session

data PINCheck = CorrectPIN | IncorrectPIN

data HasCard : ATMState -> Type where
    HasCI : HasCard CardInserted
    HasSession : HasCard Session

data ATMCmd : (ty : Type) -> ATMState -> (ty -> ATMState) -> Type where
    InsertCard : ATMCmd () Ready (const CardInserted)
    EjectCard : {auto prf : HasCard state} -> ATMCmd () state (const Ready)
    GetPIN : ATMCmd PIN CardInserted (const CardInserted)

    CheckPIN : PIN -> ATMCmd PINCheck CardInserted
                        (\check =>
                            case check of
                                CorrectPIN => Session
                                IncorrectPIN => CardInserted)
    GetAmount : ATMCmd Nat state (const state)

    Dispense : (amount : Nat) -> ATMCmd () Session (const Session)

    Message : String -> ATMCmd () state (const state)
    Pure : (res : ty) -> ATMCmd ty (stateFn res) stateFn
    (>>=) : ATMCmd a state1 state2Fn ->
            ((res : a) -> ATMCmd b (state2Fn res) state3Fn) ->
            ATMCmd b state1 state3Fn

atm : ATMCmd () Ready (const Ready)
atm =
    do
        InsertCard
        pin <- GetPIN
        cash <- GetAmount
        pinOK <- CheckPIN pin
        case pinOK of
            CorrectPIN =>
                do
                    Dispense cash
                    EjectCard
                    Message "Please remove your card and cash"
            IncorrectPIN =>
                do
                    Message "Incorrect PIN"
                    EjectCard

-- badATM : ATMCmd () state (const Ready)
-- badATM = EjectCard

testPIN : PIN
testPIN = ['1', '2', '3', '4']

readVect : (n : Nat) -> IO (Vect n Char)
readVect Z =
    do
        discard <- getLine
        pure []
readVect (S k) =
    do
        ch <- getChar
        chs <- readVect k
        pure (ch :: chs)

runATM : ATMCmd res inState outStateFn -> IO res
runATM InsertCard =
    do
        putStrLn "Please insert your card (press enter)"
        x <- getLine
        pure ()
runATM EjectCard  = putStrLn "Card ejected"
runATM GetPIN =
    do
        putStr "Enter PIN: "
        readVect 4
runATM (CheckPIN pin) =
    if pin == testPIN
        then pure CorrectPIN
        else pure IncorrectPIN
runATM GetAmount =
    do
        putStr "How much would you like? "
        x <- getLine
        pure (cast x)
runATM (Dispense amount) = putStrLn ("Here is " ++ show amount)
runATM (Message msg) = putStrLn msg
runATM (Pure res) = pure res
runATM (x >>= f) =
    do
        x' <- runATM x
        runATM (f x')


module Data.Void

export
absurdity : Uninhabited t => (0 _ : t) -> s
absurdity x = void (uninhabited x)

export
contradiction : Uninhabited t => (0 _ : x -> t) -> (x -> s)
contradiction since x = absurdity (since x)


-- Testing that 'with' works under parameters that are implicits
parameters {0 A : Type} (pred : A -> Bool)
  foo : A -> Bool
  foo x = case pred x of
    True  => False
    False => True

  bar : (x : A) -> not (pred x) = foo x
  bar  a with (pred a)
   bar a | True  = Refl
   bar a | False = Refl


module Algebra.Solver.Ring.SolvableRing

import Algebra.Ring

%default total

||| When normalizing arithmetic expressions, we must
||| make sure that factors that evaluate to zero must
||| be removed from the sum of products.
|||
||| For instance, the following example only works,
||| if the term `0 * x * y` gets removed before comparing
||| the normalized sums:
|||
||| ```idris example
||| 0 binom3 : {x,y : Bits8} -> (x + y) * (x - y) === x * x - y * y
||| binom3 = solve [x,y] ((x .+. y) * (x .-. y)) (x .*. x - y .*. y)
||| ```
|||
||| Because we cannot directly use a (primitive) pattern match
||| without having a concrete type, we need this interface.
||| (We *could* use `DecEq`, but this is not publicly exported
||| for the primitives; probably for good reasons since it is
||| implemented using `believe_me`).
public export
interface Ring a => SolvableRing a where

  ||| Checks if a value is propositionally equal to zero.
  isZero : (v : a) -> Maybe (v === 0)

public export
SolvableRing Bits8 where
  isZero 0 = Just Refl
  isZero _ = Nothing

public export
SolvableRing Bits16 where
  isZero 0 = Just Refl
  isZero _ = Nothing

public export
SolvableRing Bits32 where
  isZero 0 = Just Refl
  isZero _ = Nothing

public export
SolvableRing Bits64 where
  isZero 0 = Just Refl
  isZero _ = Nothing

public export
SolvableRing Int8 where
  isZero 0 = Just Refl
  isZero _ = Nothing

public export
SolvableRing Int16 where
  isZero 0 = Just Refl
  isZero _ = Nothing

public export
SolvableRing Int32 where
  isZero 0 = Just Refl
  isZero _ = Nothing

public export
SolvableRing Int64 where
  isZero 0 = Just Refl
  isZero _ = Nothing

public export
SolvableRing Int where
  isZero 0 = Just Refl
  isZero _ = Nothing

public export
SolvableRing Integer where
  isZero 0 = Just Refl
  isZero _ = Nothing


module Libraries.Data.Graph

import Libraries.Data.SortedMap
import Libraries.Data.SortedSet

import Data.List1

-- Mechanically transcribed from
-- https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm#The_algorithm_in_pseudocode
private
record TarjanVertex where
  constructor TV
  index   : Int
  lowlink : Int
  inStack : Bool

private
record TarjanState cuid where
  constructor TS
  vertices           : SortedMap cuid TarjanVertex
  stack              : List cuid
  nextIndex          : Int
  components         : List (List1 cuid)
  impossibleHappened : Bool  -- we should get at least some indication of broken assumptions

initial : Ord cuid => TarjanState cuid
initial = TS empty [] 0 [] False

||| Find strongly connected components in the given graph.
|||
||| Input: map from vertex X to all vertices Y such that there is edge X->Y
||| Output: list of strongly connected components, ordered by output degree descending
export
tarjan : Ord cuid => SortedMap cuid (SortedSet cuid) -> List (List1 cuid)
tarjan {cuid} deps = loop initial (SortedMap.keys deps)
  where
    strongConnect : TarjanState cuid -> cuid -> TarjanState cuid
    strongConnect ts v =
        let ts'' = case SortedMap.lookup v deps of
              Nothing => ts'  -- no edges
              Just edgeSet => loop ts' (SortedSet.toList edgeSet)
          in case SortedMap.lookup v ts''.vertices of
              Nothing => { impossibleHappened := True } ts''
              Just vtv =>
                if vtv.index == vtv.lowlink
                  then createComponent ts'' v []
                  else ts''
      where
        createComponent : TarjanState cuid -> cuid -> List cuid -> TarjanState cuid
        createComponent ts v acc =
          case ts.stack of
            [] => { impossibleHappened := True } ts
            w :: ws =>
              let ts' : TarjanState cuid = {
                      vertices $= SortedMap.adjust w { inStack := False },
                      stack := ws
                    } ts
                in if w == v
                  then { components $= ((v ::: acc) ::) } ts'  -- that's it
                  else createComponent ts' v (w :: acc)

        loop : TarjanState cuid -> List cuid -> TarjanState cuid
        loop ts [] = ts
        loop ts (w :: ws) =
          loop (
            case SortedMap.lookup w ts.vertices of
              Nothing => let ts' = strongConnect ts w in
                case SortedMap.lookup w ts'.vertices of
                  Nothing => { impossibleHappened := True } ts'
                  Just wtv => { vertices $= SortedMap.adjust v { lowlink $= min wtv.lowlink } } ts'

              Just wtv => case wtv.inStack of
                False => ts  -- nothing to do
                True => { vertices $= SortedMap.adjust v { lowlink $= min wtv.index } } ts
          ) ws

        ts' : TarjanState cuid
        ts' = {
            vertices  $= SortedMap.insert v (TV ts.nextIndex ts.nextIndex True),
            stack     $= (v ::),
            nextIndex $= (1+)
          } ts

    loop : TarjanState cuid -> List cuid -> List (List1 cuid)
    loop ts [] =
      if ts.impossibleHappened
        then []
        else ts.components
    loop ts (v :: vs) =
      case SortedMap.lookup v ts.vertices of
        Just _ => loop ts vs  -- done, skip
        Nothing => loop (strongConnect ts v) vs


import Data.List

data Vect : Nat -> Type -> Type where
     Nil : Vect Z a
     (::) : a -> Vect k a -> Vect (S k) a

length : Vect n a -> Nat
length [] = Z
length (x :: xs) = S (length xs)

wrong : Nat -> Nat
wrong x = length x


import Data.Vect
import Data.Vect.Elem

oneInVector : Elem 1 [1,2,3]
oneInVector = Here

maryInVector : Elem "Mary" ["Peter", "Paul", "Mary"]
maryInVector = There (There Here)

fourNotInVector : Elem 4 [1,2,3] -> Void
fourNotInVector (There (There (There Here))) impossible
fourNotInVector (There (There (There (There _)))) impossible

peteNotInVector : Elem "Pete" ["John", "Paul", "George", "Ringo"] -> Void
peteNotInVector (There (There (There (There Here)))) impossible
peteNotInVector (There (There (There (There (There _))))) impossible


module Main

data Free : (f : Type -> Type) -> (a : Type) -> Type where
  Pure : a -> Free f a
  Bind : f (Free f a) -> Free f a

Functor f => Functor (Free f) where
  map f m = assert_total $ case m of
    Pure x => Pure (f x)
    Bind x => Bind (map (map f) x)

Functor f => Applicative (Free f) where
  pure = Pure

  m <*> x = assert_total $ case m of
    Pure f => map f x
    Bind f => Bind (map (<*> x) f)

Functor f => Monad (Free f) where
  m >>= f = assert_total $ case m of
    Pure x => f x
    Bind x => Bind (map (>>= f) x)

liftFree : Functor f => f a -> Free f a
liftFree = assert_total $ Bind . map Pure

foldFree : (Monad m, Functor f) => ({ a : Type } -> f a -> m a) -> Free f b -> m b
foldFree f m = assert_total $ case m of
  Pure x => pure x
  Bind x => f x >>= foldFree f

data FileSystemF next
  = ReadFile (String -> next)

FileSystem : Type -> Type
FileSystem = Free FileSystemF

Functor FileSystemF where
  map f (ReadFile nextFn) = ReadFile (f . nextFn)

readFileF : FileSystem String
readFileF = liftFree $ ReadFile id

interpret2 : FileSystemF a -> IO a
interpret2 (ReadFile nextFn) = do
  contents <- pure "ola"
  pure (nextFn contents)

main : IO ()
main =
  do
    c <- foldFree interpret2 readFileF
    putStrLn' c


module On2

on2 : forall f, t. (t a -> t b -> c) -> (forall z. f z -> t z) -> f a -> f b -> c
on2 g h = \x, y => h x `g` h y


module InvParComb

%default total

data IPC : (listtype : Type) -> (valtype : Type) -> Type where
  MkIPC : (parse : (List listtype -> Maybe (valtype, List listtype))) ->
          (compose : (valtype -> Maybe (List listtype))) ->
          IPC listtype valtype

ipc_parse : IPC a b -> List a -> Maybe (b, List a)
ipc_parse (MkIPC p c) = p

ipc_compose : IPC a b -> b -> Maybe (List a)
ipc_compose (MkIPC p c) = c

-- Functor, kind of
ipc_fmap : (fp : a -> b) -> (fc : b -> a) -> IPC c a -> IPC c b
ipc_fmap fp fc (MkIPC parse compose) = MkIPC pf cf
  where
    pf l = do
      (x, rest) <- parse l
      return (fp x, rest)
    cf x = compose $ fc x

-- Almost applicative
ipc_pure : (Eq a, Eq b) => a -> IPC a a
ipc_pure v = MkIPC pf cf
  where
    pf l = Just (v, l)
    cf x = if x == v then Just [v] else Nothing

ipc_empty : IPC a b
ipc_empty = MkIPC (\x => Nothing) (\x => Just $ List.Nil)

ipc_item : IPC a a
ipc_item = MkIPC pf cf
  where
    pf [] = Nothing
    pf (x :: xs) = Just (x, xs)
    cf x = Just [x]

ipc_fail : IPC a a
ipc_fail = MkIPC (const Nothing) (const Nothing)

-- Alternative
mplus : Maybe a -> Lazy (Maybe a) -> Maybe a
mplus Nothing m2 = m2
mplus j m2 = j

ipc_alt : IPC a b -> IPC a b -> IPC a b
ipc_alt (MkIPC p1 c1) (MkIPC p2 c2) = MkIPC pf cf
  where
    pf l = mplus (p1 l) (p2 l)
    cf x = mplus (c1 x) (c2 x)

-- Tuple
ipc_tup : IPC a b -> IPC a c -> IPC a (b, c)
ipc_tup (MkIPC p1 c1) (MkIPC p2 c2) = MkIPC pf cf
  where
   pf l = do
     (x, rest) <- p1 l
     (y, ret) <- p2 rest
     return ((x, y), ret)
   cf (x, y) = do
     l1 <- c1 x
     l2 <- c2 y
     return (l1 ++ l2)

-- Vector
ipc_nilv : IPC a (Vect 0 b)
ipc_nilv = MkIPC pf cf
  where
    pf l = Just $ ([], l)
    cf x = Just []

ipc_consv : IPC a b -> IPC a (Vect n b) -> IPC a (Vect (S n) b)
ipc_consv (MkIPC p1 c1) (MkIPC p2 c2) = MkIPC pf cf
  where
    pf l = do
      (x, rest) <- p1 l
      (xs, ret) <- p2 rest
      return (x :: xs, ret)
    cf (x :: xs) = do
      l1 <- c1 x
      l2 <- c2 xs
      return (l1 ++ l2)

ipc_rep : (n : Nat) -> IPC a b -> IPC a (Vect n b)
ipc_rep Z p = ipc_nilv
ipc_rep (S k) p = ipc_consv p (ipc_rep k p)

-- List
ipc_nil : (Eq a, Eq b) => IPC a (List b)
ipc_nil = MkIPC pf cf
  where
    pf l = Just ([], l)
    cf [] = Just []
    cf l = Nothing

ipc_cons : (Eq a, Eq b) => IPC a b -> Lazy (IPC a (List b)) -> IPC a (List b)
ipc_cons (MkIPC p1 c1) (MkIPC p2 c2) = MkIPC pf cf
  where
    pf l = do
      (x, rest) <- p1 l
      (xs, ret) <- p2 rest
      return (x :: xs, ret)
    cf [] = Nothing
    cf (x :: xs) = do
      l1 <- c1 x
      l2 <- c2 xs
      return (l1 ++ l2)


-- Like SimpleRealizedFunction but uses data instead of record

module SimpleDataRealizedFunction
%default total

public export
data SimpleDataRealizedFunction : (t : Type) -> (cost : Integer) -> (quality : Double)
                                  -> Type where
  MkSimpleDataRealizedFunction : (f : t) -> (cost : Integer) -> (quality : Double)
                                 -> SimpleDataRealizedFunction t cost quality

-- Perform the composition between 2 simple realized functions.  The
-- resulting realized function is formed as follows:
--
-- 1. Composed lifted functions
-- 2. Costs are added
-- 3. Composed quality is the min of the component qualities
public export
compose : {a : Type} -> {b : Type} -> {c : Type} ->
          (SimpleDataRealizedFunction (b -> c) g_cost g_q) ->
          (SimpleDataRealizedFunction (a -> b) f_cost f_q) ->
          (SimpleDataRealizedFunction (a -> c) (f_cost + g_cost) (min f_q g_q))
compose (MkSimpleDataRealizedFunction f f_cost f_q) (MkSimpleDataRealizedFunction g g_cost g_q) =
         MkSimpleDataRealizedFunction (f . g) (g_cost + f_cost) (min g_q f_q)

-- Perform function application over realized functions.  Maybe we'd
-- want to used some funded data, as defined in FndType.
public export
apply : (SimpleDataRealizedFunction (a -> b) cost quality) -> a -> b
apply (MkSimpleDataRealizedFunction f cost quality) = f


module Syntax

-- Gettin used to idris's syntax and type system
--
-- Those that are same as haskell are omitted, complicated syntax (like with)
-- are played in other files

-- all top-level functions/variables' types must be explicitly declared
foo : String
foo = "hello"

-- String in idris are not List Char, but rather packed Text as in haskell
bar : List Char
bar = unpack foo

-- Nat is unbound integer within [0,inf), inductively defined
data Nat' : Type where
  Z' : Nat'
  S' : Nat' -> Nat'

-- One can either use the GADT style to define a data, or simply:
data Nat'' = Z'' | S'' Nat''

replicate1 : Nat -> List Nat
replicate1 Z = []
-- replicate1 0 = Nil       -- the same
replicate1 (S k) = 1 :: replicate1 k
-- here the value 1's type is automatically set to Nat because it knows it's
-- constructing a list of Nat

-- [] is the syntatic sugar for Nil. [1,2,3] is for 1::2::3::Nil

-- idris allows overloading functions over argument types, as long as there is
-- they are defined in different namespaces,
--
-- the compiler will figure out which definition to use automatically when
-- calling with arguments with different types

-- finite set captures the idea of matching Nat that are less than or equal to a
-- given Nat.
data Fin' : Nat -> Type where
  FZ' : Fin' (S k)
  FS' : Fin' k -> Fin' (S k)

-- typeclass in haskell ~= interfaces in idris, the instance implementation
-- syntax is slightly different:

Show Nat' where
  show Z' = "Z"
  show (S' n) = "S (" ++ show n ++ ")"

-- idris doesn't have things like Constraint at the kind level. Interfaces (or
-- typeclass in haskell) are implemented as partial functions. i.e. unsatisfied
-- types are not implemented.
--
-- example with Show : Type -> Type, where Show Nat : Type is implemented and
-- Show (Nat -> Nat) : Type is not implemented (doesn't have a proof).

-- more sensible Functor/Applicative/Monad interfaces
--
-- Functor has `map' instead of `fmap' as its implementation
--
-- Applicative has `pure' and `<*>', same as Haskell
--
-- Monad has `>>=', `return' is not needed, since we already got `pure'

-- do notation works as in haskell. Monad comprehension as well. Monad
-- comprehension requires (Monad m, Alternative m) (for use of guard).
-- Alternative is the same as in haskell.

-- Multiple implementations for the same type with a interface is allowed.
--
-- e.g. Nat can be a Semigroup in two ways: additive and multiplicative
--
-- This is achieved by named impl, where a name is used when defining

[SemiNatMult] Semigroup Nat where
  (<+>) m n = m * n

[SemiNatAdd] Semigroup Nat where
  (<+>) m n = m + n

-- dependent implementation can be achieved with the 'using' syntax
[MonoidNatMult] Monoid Nat using SemiNatMult where
  neutral = 1
[MonoidNatAdd]  Monoid Nat using SemiNatAdd  where
  neutral = 0

-- !-notation makes writing monadic code easier:
--
--

m_add : Maybe Int -> Maybe Int -> Maybe Int
-- without !-notation
-- m_add x y = do x' <- x
--                y' <- y
--                pure (x + y)
-- with !-notation
m_add x y = pure (!x + !y)

-- ! can be thought of like a prefix function with this type:
--
-- (!) : m a -> a

-- Another convenient syntax is the idiom bracket. idiom bracket makes writing
-- Applicative code cleaner:

m_add' : Maybe Int -> Maybe Int -> Maybe Int
-- without idiom bracket:
-- m_add' x y = pure (+) <*> x <*> y
-- with idiom bracket:
m_add' x y = [| x + y |]

-- idris values are by default strict, Lazy values must be declared explicitly.
--
-- Lazy : Type -> Type

my_if : Bool -> Lazy a -> Lazy a -> a
my_if b x y = if b then x else y

-- when we acquire a value of 'a' where we have a 'Lazy a', it will
-- automatically be evaluated.
--
-- similarly, when we acquire a 'Lazy a' where we have an 'a'-typed expr, the
-- expr will be converted into a unresolved lazy value of type 'Lazy a'

-- function definitions are evaluated in the order they are defined therefore
-- you cannot write plain functions depending on each other directly (you can
-- define recursive function with no problem, though). 'mutual' syntax can be
-- used in this situation, in a 'mutual' block, first the type declarations are
-- added, then the function definitions.

fact' : Nat -> Nat
fact' Z = 1
fact' (S n) = (S n) * fact' n

mutual
  even : Nat -> Bool
  even Z = True
  even (S k) = odd k

  odd : Nat -> Bool
  odd Z = False
  odd (S k) = even k

-- infinte data structure are called co-data types.
--
-- codata-types will gets translated into same as a kind of 'Lazy' data types
namespace StreamCodata
  codata Stream' : Type -> Type where
    (::) : (e : a) -> Stream' a -> Stream' a

-- is the same as

namespace StreamInfData
  data Stream'' : Type -> Type where
    (::) : (e : a) -> Inf (Stream'' a) -> Stream'' a

-- since (::) cannot be defined twice under the same namespace, so here we
-- define them under two namespaces. They can be used without distinguishing
-- explicitly since they differ by the type of the second argument.

-- Inf is mostly the same as Lazy except some nauce differences on their effects
-- on totality.

-- just as a canonical example of typical dependent-type language, there's a
-- built-in Vect data structure is the list parametrized with its length.
-- Defined as below:

data Vect' : (n : Nat) -> a -> Type where
  Nil  : Vect' 0 a
  (::) : a -> Vect' n a -> Vect' (S n) a

-- Tuple syntax (a,b), as both type and value, is a syntatic sugar again.
-- (a, b) as a type is Pair a b; (a, b) as a value is MkPair a b

-- There's another interesting data structure called DPair (Dependent Pair). Defined as:

data DPair' : (a : Type) -> (P : a -> Type) -> Type where
  MkDPair' : {P : a -> Type} -> (x : a) -> P x -> DPair' a P

-- where the second element is a value whose type depends on the value of the
-- first argument. In type theory this type is referred to as `Sigma types'.
--
-- There's, again, a syntatic sugar for DPair: (a:A ** p) stands for MkDPair a p

vec : (n : Nat ** Vect' n Int)
vec = (_ ** [1,2])
-- we can write `_' in place of values which the typechecker can infer
-- automatically.

vec' : DPair Nat (\n => Vect' n Int)
vec' = MkDPair _ [3,4]
-- the same, desugared version.