{- Ctrl-Alt-A: Add definition
   Ctrl-Alt-C: Case split
   Ctrl-Alt-D: Documentation
   Ctrl-Alt-L: Lift hole: Lifts a hole to the top level as a new function definition
   Ctrl-Alt-M: Match: Replaces a hole with a case expression that matches on an intermediate result
   Ctrl-Alt-R: Reloads and typechecks the current buffer
   Ctrl-Alt-S: Search
   Ctrl-Alt-T: Type-check: Displays the type under cursor -}
module Main

import Data.Vect

infixr 5 .+.

data Schema
  = SString
  | SInt
  | (.+.) Schema Schema

SchemaType : Schema -> Type
SchemaType SString = String
SchemaType SInt = Int
SchemaType (x .+. y) = (SchemaType x, SchemaType y)

record DataStore where
  constructor MkData
  schema : Schema
  size : Nat
  items : Vect size (SchemaType schema)

addToStore : (store : DataStore) -> SchemaType (schema store) -> DataStore
addToStore (MkData sch s i) n = MkData sch _ (addToData i)
  where
    addToData : Vect old (SchemaType sch) -> Vect (S old) (SchemaType sch)
    addToData [] = [n]
    addToData (i :: is) = i :: addToData is

data Command : Schema -> Type where
  SetSchema : (newschema : Schema) -> Command schema
  Add : SchemaType schema -> Command schema
  Get : Integer -> Command schema
  Quit : Command schema

parsePrefix : (sch : Schema) -> String -> Maybe (SchemaType sch, String)
parsePrefix SString x = getQuoted (unpack x)
  where
    getQuoted : List Char -> Maybe (String, String)
    getQuoted ('"' :: xs) = case span (/= '"') xs of
      (qouted, '"' :: rest) => Just (pack qouted, ltrim (pack rest))
      _                     => Nothing
    getQuoted _ = Nothing
parsePrefix SInt x = case span isDigit x of
  ("", rest) => Nothing
  (num, rest) => Just (cast num, ltrim rest)
parsePrefix (y .+. z) x = case parsePrefix y x of
  Nothing => Nothing
  Just (lval, x') => case parsePrefix z x' of
    Nothing => Nothing
    Just (rval, x'') => Just ((lval, rval), x'')

parseBySchema : (sch : Schema) -> (str : String) -> Maybe (SchemaType sch)
parseBySchema sch inp = case parsePrefix sch inp of
  Just (res, "") => Just res
  Just _ => Nothing
  Nothing => Nothing

parseSchema : List String -> Maybe Schema
parseSchema ("String" :: xs) = case xs of
  [] => Just SString
  _ => case parseSchema xs of
    Nothing  => Nothing
    Just xsh => Just (SString .+. xsh)
parseSchema ("Int" :: xs) = case xs of
  [] => Just SInt
  _ => case parseSchema xs of
    Nothing  => Nothing
    Just xsh => Just (SInt .+. xsh)
parseSchema _ = Nothing

parseCommand : (schema : Schema) -> (cmd : String) -> (args : String) -> Maybe (Command schema)
parseCommand sch "add" str    = case parseBySchema sch str of
                                  Nothing => Nothing
                                  Just ok => Just (Add ok)
parseCommand sch "get" val    = case all isDigit (unpack val) of
                                  False => Nothing
                                  True  => Just (Get (cast val))
parseCommand sch "quit" ""    = Just Quit
parseCommand sch "schema" rest = case parseSchema (words rest) of
  Nothing => Nothing
  Just sok => Just (SetSchema sok)
parseCommand _   _      _     = Nothing

setSchema : (store : DataStore) -> Schema -> Maybe DataStore
setSchema s sc = case size s of
  Z => Just (MkData sc _ [])
  S _ => Nothing

parse : (schema : Schema) -> (input : String) -> Maybe (Command schema)
parse sch input = case span (/= ' ') input of
  (cmd, args) => parseCommand sch cmd (ltrim args)

display : SchemaType schema -> String
display {schema = SString} item = item
display {schema = SInt} item = cast item
display {schema = (x .+. y)} (a, b) = display a ++ ", " ++ display b

getEntry : (pos : Integer) -> (store : DataStore) -> Maybe (String, DataStore)
getEntry pos store =
  let store_items = items store
  in case integerToFin pos (size store) of
    Nothing => Just ("Out of range\n", store)
    Just ix => Just (display (index ix store_items) ++ "\n", store)

getSize : (store : DataStore) -> Maybe (String, DataStore)
getSize store = Just ("Size of store: " ++ cast (size store), store)

processInput : DataStore -> String -> Maybe (String, DataStore)
processInput store inp = case parse (schema store) inp of
  Nothing           => Just ("Invalid command\n", store)
  Just (SetSchema sc) => case setSchema store sc of
                          Nothing => Just ("Can't update schema\n", store)
                          Just st => Just ("OK\n", st)
  Just (Add item)   => Just ("ID " ++ show (size store) ++ "\n", addToStore store item)
  Just (Get pos)    => getEntry pos store
  Just Quit         => Nothing

main : IO ()
main = replWith (MkData SString _ []) "Command: " processInput


module Incrementer

import public Numeric

%default total

||| Increment integer
public export
incrementer : Integer -> Integer
incrementer = (+1)


module Data.LazyList

public export
data LazyList : Type -> Type where
  Nil : LazyList a
  (::) : a -> Lazy (LazyList a) -> LazyList a

data NonEmpty : LazyList a -> Type where
  IsNonEmpty : NonEmpty (_ :: _)

export
Functor LazyList where
  map f [] = []
  map f (x :: xs) = f x :: map f xs

export
Show a => Show (LazyList a) where
  show [] = "[]"
  show (x :: xs) = show x ++ " :: " ++ show xs

public export
Semigroup (LazyList a) where
  [] <+> ys = ys
  (x :: xs) <+> ys = x :: (xs <+> ys)

public export
Monoid (LazyList a) where
  neutral = []

export
total
head : LazyList a -> Maybe a
head [] = Nothing
head (x :: _) = Just x

export
total
head' : (l : LazyList a) -> NonEmpty l => a
head' (x :: _) = x

export
partial
head'' : LazyList a -> a
head'' [] = idris_crash "head: LazyList was empty"
head'' (x :: _) = x

export
total
tail : LazyList a -> Maybe (LazyList a)
tail [] = Nothing
tail (_ :: xs) = Just xs

export
total
tail' : (l : LazyList a) -> NonEmpty l => LazyList a
tail' (_ :: xs) = xs

export
partial
tail'' : LazyList a -> LazyList a
tail'' [] = idris_crash "tail: LazyList was empty"
tail'' (_ :: xs) = xs

export
implementation  Foldable LazyList where
  foldr f z [] = z
  foldr f z (x :: xs) = f x (foldr f z xs)

export
||| Foldable's methods are strict due to needing (a -> b -> b). `map` and the
||| like can stay lazy probably because they have a constructors guarding their
||| recursion. We don't have that here and must instead be explicitly lazy in
||| our combining function.
foldr' : (a -> Lazy b -> Lazy b) -> b -> LazyList a -> b
foldr' f z [] = z
foldr' f z (x :: xs) = f x (Delay (foldr' f z xs))

export
toList : LazyList a -> List a
toList = foldr' (\x,xs => x :: force xs) []

export -- Not lazy
fromList : List a -> LazyList a
fromList = foldr (\x,xs => x :: xs) []

-- It's weird but the S Z case is necessary otherwise a list like
-- take 2 [1,2,idris_crash "foo"] will crash. I don't know why that should be,
-- given that xs is Lazy.
export
take : Nat -> LazyList a -> LazyList a
take Z _  = []
take _ [] = []
take (S Z) (x :: _) = x :: []
take (S k) (x :: xs) = x :: take k xs

export
take' : Nat -> Stream a -> LazyList a
take' Z (x :: xs) = []
take' (S k) (x :: xs) = x :: take' k xs

repeat : a -> LazyList a
repeat x = x :: repeat x

takeUntil : (a -> Bool) -> Stream a -> LazyList a
takeUntil f (x :: xs) = if f x then [x] else x :: takeUntil f xs

takeBefore : (a -> Bool) -> Stream a -> LazyList a
takeBefore f (x :: xs) = if f x then [] else x :: takeBefore f xs

export -- [2..7] = [2,3,4,5,6,7]
rangeFromTo : (Range a, Ord a) => a -> a -> LazyList a
rangeFromTo x y = if y > x then takeUntil (>= y) (rangeFrom x)
                           else []

export
||| yanked from Prelude.Types
unpack : String -> LazyList Char
unpack str = unpack' (prim__cast_IntegerInt (natToInteger (length str)) - 1) str []
  where
    unpack' : Int -> String -> LazyList Char -> LazyList Char
    unpack' pos str acc
        = if pos < 0
             then acc
             else assert_total $ unpack' (pos - 1) str (assert_total (prim__strIndex str pos)::acc)

export
zipWith : (a -> b -> c) -> LazyList a -> LazyList b -> LazyList c
zipWith f (x :: xs) (y :: ys) = f x y :: zipWith f xs ys
zipWith _ _ _ = []

export
%inline
zip : LazyList a -> LazyList b -> LazyList (a,b)
zip = zipWith MkPair


module Data.List.Alternating

import Data.Bifoldable
import Data.List

infixl 5 +>
infixr 5 <+

%default total

mutual
    namespace Odd
        ||| Non-empty list, starting and ending with an a, where adjacent elements alternate
        ||| between types a and b.
        ||| We can think of this type as:
        ||| - A fence, with the `a`s as fence-posts, and the `b`s as panels.
        ||| - A non-empty list of `a`s, separated by `b`s
        ||| - A list of `b`s, separated by, and surrounded by, `a`s
        ||| - The free extension of a monoid `a`, with variables in `b`
        public export
        data Odd a b = (::) a (Even b a)

    namespace Even
        ||| A list, starting with an a, and ending with a b; where adjacent elements
        ||| alternate between types a and b.
        ||| Equivalent to List (a, b)
        public export
        data Even a b = Nil | (::) a (Odd b a)

%name Odd xs, ys, zs
%name Even xs, ys, zs

mutual
    public export
    Eq a => Eq b => Eq (Odd a b) where
        x :: xs == y :: ys = x == y && assert_total (xs == ys)

    public export
    Eq a => Eq b => Eq (Even a b) where
        [] == [] = True
        x :: xs == y :: ys = x == y && xs == ys
        _ == _ = False

mutual
    public export
    Ord a => Ord b => Ord (Odd a b) where
        compare (x :: xs) (y ::ys)
           = case compare x y of
                  EQ => assert_total (compare xs ys)
                  c => c

    public export
    Ord a => Ord b => Ord (Even a b) where
        compare [] [] = EQ
        compare [] (x :: xs) = LT
        compare (x :: xs) [] = GT
        compare (x :: xs) (y ::ys)
           = case compare x y of
                  EQ => compare xs ys
                  c => c

mutual
    public export
    Bifunctor Odd where
        bimap f g (x :: xs) = (f x) :: assert_total (bimap g f xs)

    public export
    Bifunctor Even where
        bimap f g [] = []
        bimap f g (x :: xs) = (f x) :: (bimap g f xs)

mutual
    public export
    Bifoldable Odd where
        bifoldr f g acc (x :: xs) = f x (assert_total $ bifoldr g f acc xs)

        bifoldl f g acc (x :: xs) = assert_total $ bifoldl g f (f acc x) xs

    public export
    Bifoldable Even where
        bifoldr f g acc [] = acc
        bifoldr f g acc (x :: xs) = f x (bifoldr g f acc xs)

        bifoldl f g acc [] = acc
        bifoldl f g acc (x :: xs) = bifoldl g f (f acc x) xs

mutual
    public export
    Bitraversable Odd where
        bitraverse f g (x :: xs) = [| f x :: assert_total (bitraverse g f xs) |]

    public export
    Bitraversable Even where
        bitraverse f g [] = [| [] |]
        bitraverse f g (x :: xs) = [| f x :: bitraverse g f xs |]

namespace Snd
    public export
    Functor (Odd a) where
        map = mapSnd

namespace Fst
    public export
    [FstFunctor] Functor (\a => Odd a b) where
        map = mapFst

mutual
    namespace Odd
        public export
        (++) : Odd a b -> Odd b a -> Even a b
        (x :: xs) ++ ys = x :: xs ++ ys

    namespace EvenOdd
        public export
        (++) : Even a b -> Odd a b -> Odd a b
        [] ++ ys = ys
        (x :: xs) ++ ys = x :: xs ++ ys

mutual
    namespace Even
        public export
        (++) : Even a b -> Even a b -> Even a b
        [] ++ ys = ys
        (x :: xs) ++ ys = x :: xs ++ ys

    namespace OddEven
        public export
        (++) : Odd a b -> Even b a -> Odd a b
        (x :: xs) ++ ys = x :: xs ++ ys

||| The semigroup structure induced by treating Odd as the free extension of a
||| monoid `a`, with variables in `b`
public export
Semigroup a => Semigroup (Odd a b) where
    [x] <+> (y :: ys) = (x <+> y) :: ys
    (x :: y :: xs) <+> ys = x :: y :: xs <+> ys

namespace Odd
    public export
    (+>) : Semigroup a => Odd a b -> a -> Odd a b
    [x] +> z = [x <+> z]
    x :: y :: xs +> z = x :: y :: (xs +> z)

    public export
    (<+) : Semigroup a => a -> Odd a b -> Odd a b
    x <+ y :: ys = (x <+> y) :: ys

public export
Semigroup (Even a b) where
    (<+>) = (++)

public export
Monoid a => Monoid (Odd a b) where
    neutral = [neutral]

public export
Monoid (Even a b) where
    neutral = []

public export
Foldable (Odd a) where
    foldr = bifoldr (flip const)
    foldl = bifoldl const

public export
singleton : a -> Odd a b
singleton x = [x]

namespace Snd
    public export
    Monoid a => Applicative (Odd a) where
        pure x = [neutral, x, neutral]
        fs <*> xs = biconcatMap singleton (flip map xs) fs

public export
flatten : Odd (Odd a b) b -> Odd a b
flatten [x] = x
flatten (x :: y :: xs) = x ++ (y :: flatten xs)

namespace Fst
    public export
    [FstApplicative] Applicative (\a => Odd a b) using FstFunctor where
        pure x = [x]
        fs <*> xs = flatten $ bimap (flip mapFst xs) id fs

public export
Monoid a => Alternative (Odd a) where
    empty = [neutral]
    xs <|> ys = xs <+> ys

namespace Snd
    public export
    [SndMonad] Monoid a => Monad (Odd a) where
        x >>= f = assert_total $ biconcatMap singleton f x

    public export
    (>>=) : Monoid a => Odd a b -> (b -> Odd a c) -> Odd a c
    (>>=) = (>>=) @{SndMonad}

namespace Fst
    public export
    [FstMonad] Monad (\a => Odd a b) using FstApplicative where
        x >>= f = flatten $ mapFst f x
        join = flatten

    public export
    (>>=) : Odd a c -> (a -> Odd b c) -> Odd b c
    (>>=) = (>>=) @{FstMonad}

public export
Traversable (Odd a) where
    traverse = bitraverse pure

mutual
    namespace Odd
        public export
        odds : Odd a b -> List a
        odds (x :: xs) = x :: evens xs

    namespace Even
        public export
        evens : Even a b -> List b
        evens [] = []
        evens (x :: xs) = odds xs

mutual
    namespace Odd
        public export
        evens : Odd a b -> List b
        evens (x :: xs) = odds xs

    namespace Even
        public export
        odds : Even a b -> List a
        odds [] = []
        odds (x :: xs) = x :: evens xs

mutual
    namespace Odd
        public export
        forget : Odd a a -> List a
        forget (x :: xs) = x :: forget xs

    namespace Even
        public export
        forget : Even a a -> List a
        forget [] = []
        forget (x :: xs) = x :: forget xs

export
Show a => Show b => Show (Odd a b) where
    show xs = "[\{concat $ intersperse ", " $ forget $ bimap show show xs}]"

export
Show a => Show b => Show (Even a b) where
    show xs = "[\{concat $ intersperse ", " $ forget $ bimap show show xs}]"


module DepSec.Ref

import public DepSec.Labeled
import public DepSec.DIO
import Data.IORef

%access export

||| Secure reference
data SecRef : (l : labelType) -> (valueType : Type) -> Type where
  ||| TCB
  MkSecRef : (ref : IORef a) -> SecRef l a

||| Create a reference with a labeled intial value
||| @ flow evidence that l may flow to l'
||| @ flow' evidence that l' may flow to l''
||| @ value initial value
newRef : Poset labelType
      => {l, l', l'' : labelType}
      -> {auto flow : l `leq` l'}
      -> {auto flow' : l' `leq` l''}
      -> (value : Labeled l' a)
      -> DIO l (SecRef l'' a)
newRef (MkLabeled v)
  = lift $ newIORef v >>= pure . MkSecRef

||| Read a secure reference
||| @ flow evidence that l may flow to l'
||| @ ref reference to read
readRef : Poset labelType
       => {l, l' : labelType}
       -> {auto flow : l `leq` l'}
       -> (ref  : SecRef l' a)
       -> DIO l (Labeled l' a)
readRef (MkSecRef ioRef)
  = lift $ map MkLabeled $ readIORef ioRef

||| Write a labeled value to a secure reference
||| @ flow evidence that l may flow to l'
||| @ flow' evidence that l' may flow to l''
||| @ ref reference to write to
||| @ content value to write to reference
writeRef : Poset labelType
        => {l, l', l'' : labelType}
        -> {auto flow : l `leq` l'}
        -> {auto flow' : l' `leq` l''}
        -> (ref : SecRef l'' a)
        -> (content : Labeled l' a)
        -> DIO l ()
writeRef (MkSecRef ioRef) (MkLabeled content)
  = lift $ writeIORef ioRef content


module DndPem

%access export
%default total

--- You need this to get the final submit work
public export
AxiomPEM : Type
AxiomPEM = {a : Type} -> {b : Type} -> (a -> b) -> ((a -> Void) -> b) -> b

public export
AxiomDNE : Type
AxiomDNE = {a : Type} -> ((a -> Void) -> Void) -> a

from : AxiomDNE -> AxiomPEM
from dne ri nri = dne $ \bv => bv . nri $ bv . ri

to : AxiomPEM -> AxiomDNE
to pem = pem id . (absurd .)


-- ---------------------------------------------------------------- [ Tree.idr ]
-- Module      : Data.Hitchhiker.Tree
-- Description : An Idris implementation of hitchhiker trees.
-- Copyright   : (c) 2016 Eric Bailey
-- License     : BSD-3
-- Link        : https://github.com/elm-community/string-extra
-- --------------------------------------------------------------------- [ EOH ]
||| Hitchhiker trees.
module Data.Hitchhiker.Tree

import public Data.Vect

-- -------------------------------------------------------------- [ Statements ]

%access public export

||| Specify a change in value `a` for a particular key `k`.
data Statement : Type -> Type -> Type where

     ||| Assert the value of `k` is `a`.
     Assert : k -> a -> Statement k a

     ||| Retract the value of `k`.
     Retract : k -> Statement k a

export
keyFor : Statement k a -> k
keyFor (Assert k _) = k
keyFor (Retract k)  = k

export
toStatement : k -> Maybe a -> Statement k a
toStatement k = maybe (Retract k) (Assert k)

export
valueFor : Statement k a -> Maybe a
valueFor (Assert _ a) = Just a
valueFor (Retract _)  = Nothing

Log : Type -> Type -> Type
Log k a = List (Statement k a)

NodeLog : Nat -> Type -> Type -> Type
NodeLog l k a = Vect l (Statement k a)

Leaves : Nat -> Type -> Type -> Type
Leaves c k a = Vect c (k, Log k a)

-- type Hitchhiker l b k = (Ord k, KnownNat l, KnownNat b, 2 ::<= b)
Hitchhiker : Ord k => (l, b, k : Nat) -> Type
Hitchhiker l b k = LTE 2 b

mutual

    Children : (c, d, l, b : Nat) -> Type -> Type -> Type
    Children c d l b k a = Vect c (k, HNode d l b k a)

    data HNode : (d, l, b : Nat) -> Type -> Type -> Type where

         HLeaf : Ord k => Leaves c k a ->
                 {auto lower : LTE b c} ->
                 {auto upper : LTE c (2 * b)} ->
                 HNode 0 l b k a

         HInt  : Ord k => NodeLog e k a ->
                 Children c (pred d) l b k a ->
                 {auto pos   : LTE 1 b} ->
                 {auto lower : LTE b c} ->
                 {auto upper : LTE c (2 * b)} ->
                 {auto log   : LTE e l} ->
                 HNode d l b k a

data HTree : (l, b : Nat) -> (k, a : Type) -> Type where

     ||| Partial root node, acting like a leaf.
     Partial : Leaves c k a ->
               {auto lower : LTE 0 c} -> {auto upper : LTE c (2 * b)} ->
               HTree l b k a

     ||| Full root node, acting like an internal node.
     Full : NodeLog e k a -> Children c d l b k a ->
            -- {auto pos : LTE 1 2} ->
            {auto lower : LTE 2 c} ->
            {auto upper : LTE c (2 * b)} ->
            {auto log   : LTE e l} ->
            HTree l b k a

total
lookupOrdRange : Ord k => k -> Vect n (k, a) -> {auto pos : LTE 1 n} -> a
lookupOrdRange k0 ((_,a0) :: kas) = go a0 kas
  where
    go : a -> Vect m (k,a) -> a
    go a [] = a
    go a ((k, a') :: kas') =
        case compare k0 k of
             LT => a
             EQ => a'
             GT => go a' kas'

lookupLogs : Ord k => k -> HTree l b k a ->
             {auto two : LTE 2 b} ->
             Log k a
lookupLogs x ht =
    case ht of
         Partial lvs  => fromLeaves lvs
         Full lg chld => fromInt lg chld
  where
    fromNode : HNode d l b k a -> Log k a

    fromLeaves : Leaves c k a -> Log k a
    fromLeaves = fromMaybe [] . lookup x

    fromInt : NodeLog e k a ->
              Children c d l b k a ->
              {auto pos   : LTE 1 lb} ->
              {auto lower : LTE lb c} ->
              {auto upper : LTE c (2 * b)} ->
              {auto log   : LTE e l} ->
              Log k a
    fromInt lg chld {pos} {lower} =
        let (_ ** fromLg) = Vect.filter (\z => x == keyFor z) lg
            fromChld = fromNode (lookupOrdRange x chld {pos = lteTransitive pos lower}) in
            toList fromLg ++ fromChld

    fromNode (HLeaf lvs)    = fromLeaves lvs
    fromNode (HInt lg chld) = fromInt lg chld

empty : Ord k => {auto two : LTE 2 b} -> HTree l b k a
empty = Partial Nil

%access export

addLeaves : {res : Nat -> Nat -> Type -> Type -> Type} ->
            Ord k => (lb : Nat) -> (Leaves c' k a -> res l b k a) ->
            NodeLog e k a -> Leaves c k a ->
            {auto pos : LTE 1 b} ->
            {auto lower : LTE lb c} ->
            {auto upper : LTE lb b} ->
            Either (k, res l b k a) (Vect n (k, HNode 0 l b k a))

addInternal : {res : Nat -> Nat -> Type -> Type -> Type} ->
              Ord k => (lb : Nat) ->
              (NodeLog e' k a -> Children c' d l b k a -> res l b k a) ->
              NodeLog lc k a -> NodeLog e k a -> Children c d l b k a ->
              {auto p1 : 1 `LTE` lb} ->
              {auto p2 : lb `LTE` c} ->
              {auto p3 : c `LTE` 2 * b} ->
              {auto p4 : e `LTE` l} ->
              {auto p5 : lb `LTE` c'} ->
              {auto p6 : c' `LTE` 2 * b} ->
              {auto p7 : e' `LTE` l} ->
              Either (k, res l b k a) (Vect m (k, HNode (d + 1) l b k a))
-- addInternal lb mkRes lgAdd lg chld {e'} {l} {res} =
--     case isLTE (S l) e' of
--          Yes prf   => ?addInternal_rhs_rhs_1
--          No contra => let lg' = lgAdd ++ lg in Left (sameLevel lg' chld)
--   where
--     sameLevel : NodeLog e' k a -> Children c' d l b k a -> (k, res l b k a)

addLog : Ord k => NodeLog e k a -> HNode d l b k a ->
         {auto prf : LTE 1 b} ->
         (n ** Vect n (k, HNode d l b k a))
addLog lgAdd (HLeaf lvs)    = ?addLog_rhs_1
addLog lgAdd (HInt lg chld) = ?addLog_rhs_2

addStatements : Ord k => List (Statement k a) -> HTree l b k a ->
                {auto two : LTE 2 b} ->
                HTree l b k a
addStatements [] ht    = ht
addStatements stmts ht =
    let lgAdd = fromList stmts in
        case ht of
             Partial lvs  => ?addStatements_rhs_1
             Full lg chld => ?addStatements_rhs_2
  where
    handleOverflow : Vect n (k, HNode d l b k a) -> HTree l b k a
    checkOverflow : Either (k, HTree l b k a) (Vect n (k, HNode d l b k a)) -> HTree l b k a
    checkOverflow = either snd handleOverflow

fromList : Ord k => List (k,a) -> {auto two : LTE 2 b} -> HTree l b k a
fromList xs = addStatements (map (uncurry Assert) xs) empty

insert : Ord k => k -> a -> HTree l b k a ->
         {auto two : LTE 2 b} ->
         HTree l b k a
insert k a = addStatements [Assert k a]

delete : Ord k => k -> HTree l b k a -> {auto two : LTE 2 b} -> HTree l b k a
delete k = addStatements [Retract k]

addStatement : Ord k => Statement k a -> HTree l b k a ->
               {auto two : LTE 2 b} ->
               HTree l b k a
addStatement = addStatements . pure

lookup : Ord k => k -> HTree l b k a ->
         {auto two : LTE 2 b} ->
         Maybe a
lookup k ht = listToMaybe (lookupLogs k ht) >>= valueFor

adjustWithKey : Ord k => (k -> a -> a) -> k -> HTree l b k a ->
                {auto two : LTE 2 b} ->
                HTree l b k a
adjustWithKey f k ht = maybe ht (\v => insert k (f k v) ht) (lookup k ht)

adjust : Ord k => (a -> a) -> k -> HTree l b k a ->
         {auto two : LTE 2 b} ->
         HTree l b k a
adjust = adjustWithKey . const

updateWithKey : Ord k => (k -> a -> Maybe a) -> k -> HTree l b k a ->
                {auto two : LTE 2 b} ->
                HTree l b k a
updateWithKey f k ht =
    maybe ht (\v => addStatement (toStatement k (f k v)) ht) (lookup k ht)

update : Ord k => (a -> Maybe a) -> k -> HTree l b k a ->
         {auto two : LTE 2 b} ->
         HTree l b k a
update = updateWithKey . const

alter : Ord k => (Maybe a -> Maybe a) -> k -> HTree l b k a ->
        {auto two : LTE 2 b} ->
        HTree l b k a
alter f k ht = addStatement (toStatement k (f (lookup k ht))) ht

-- --------------------------------------------------------------------- [ EOF ]


-- Vehicle.idr
--
-- Demonstrates type families
-- includes first part of exercise 4.2

||| Power source for Vehicle type
data PowerSource = Petrol | Pedal | Electric

||| Vehicle data type parametrized by power source
data Vehicle : PowerSource -> Type where
  ||| Unicycle, powered by pedal
  Unicycle : Vehicle Pedal
  ||| Bicycle, powered by pedal
  Bicycle : Vehicle Pedal
  ||| Motorcycle, powered by petrol
  Motorcycle : (fuel : Nat) -> Vehicle Petrol
  ||| Car, powered by petrol
  Car : (fuel : Nat) -> Vehicle Petrol
  ||| Bus, powered by petrol
  Bus : (fuel : Nat) -> Vehicle Petrol
  ||| Tram, electical powered
  Tram : Vehicle Electric
  ||| ECar, electrical powered by battery
  ECar : (bat : Nat) -> Vehicle Electric

||| gives number of wheels
wheels : Vehicle power -> Nat
wheels Bicycle = 2
wheels (Car fuel) = 4
wheels (Bus fuel) = 4
wheels Unicycle = 1
wheels (Motorcycle fuel) = 2
wheels Tram = 16
wheels ECar = 4

||| refuels petrol powered vehicles
refuel : Vehicle Petrol -> Vehicle Petrol
refuel (Car fuel) = Car 100
refuel (Bus fuel) = Bus 200
refuel (Motorcycle fuel) = Motorcycle 50
refuel Bicycle impossible
refuel Unicycle impossible

||| charges electrical powered vehicles with battery
charge : Vehicle Electric -> Vehicle Electric
charge Tram = Tram   -- nothing changed, not fully reflected by type
charge (ECar bat) = ECar 70


module Sub02Apply54x12

import ProofColDivSeqBase
import ProofColDivSeqPostulate
import ProofColDivSeqPostuProof

%default total
-- %language ElabReflection

-- 3(18x+4) --A[6,-4]->E[2,-4]--> 3(2x)
export
apply54x12 : (Not . 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))))))
  -> (m : Nat **
        (LTE (S m)
             (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)))))),
         (Not . P) m))
apply54x12 {l} col = ((plus l l) ** (lte54x12 l, ae54x12To6x l col)) where
  lte54x12 : (l:Nat) -> LTE (S (plus l l))
                            (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))))))
  lte54x12 Z = LTESucc LTEZero
  lte54x12 (S k) = let lemma = lte54x12 k in rewrite (sym (plusSuccRightSucc k k)) in
    rewrite (sym (plusSuccRightSucc (plus k k) k)) in
    rewrite (sym (plusSuccRightSucc (plus (plus k k) k) (S (S (plus (plus k k) k))))) in
    rewrite (sym (plusSuccRightSucc (plus (plus k k) k) (S (plus (plus k k) k)))) in
    rewrite (sym (plusSuccRightSucc (plus (plus k k) k) (plus (plus k k) k))) in
    rewrite (sym (plusSuccRightSucc (plus (plus (plus k k) k) (plus (plus k k) k)) (S (S (S (S (S (S (plus (plus (plus k k) k) (plus (plus k k) k)))))))))) in
    rewrite (sym (plusSuccRightSucc (plus (plus (plus k k) k) (plus (plus k k) k)) (S (S (S (S (S (plus (plus (plus k k) k) (plus (plus k k) k))))))))) in
    rewrite (sym (plusSuccRightSucc (plus (plus (plus k k) k) (plus (plus k k) k)) (S (S (S (S (plus (plus (plus k k) k) (plus (plus k k) k)))))))) in
    rewrite (sym (plusSuccRightSucc (plus (plus (plus k k) k) (plus (plus k k) k)) (S (S (S (plus (plus (plus k k) k) (plus (plus k k) k))))))) in
    rewrite (sym (plusSuccRightSucc (plus (plus (plus k k) k) (plus (plus k k) k)) (S (S (plus (plus (plus k k) k) (plus (plus k k) k)))))) in
    rewrite (sym (plusSuccRightSucc (plus (plus (plus k k) k) (plus (plus k k) k)) (S (plus (plus (plus k k) k) (plus (plus k k) k))))) in
    rewrite (sym (plusSuccRightSucc (plus (plus (plus (plus k k) k) (plus (plus k k) k)) (S (plus (plus (plus k k) k) (plus (plus k k) k)))) (S (S (S (S (S (S (plus (plus (plus k k) k) (plus (plus k k) k)))))))))) in
    rewrite (sym (plusSuccRightSucc (plus (plus (plus (plus k k) k) (plus (plus k k) k)) (S (plus (plus (plus k k) k) (plus (plus k k) k)))) (S (S (S (S (S (plus (plus (plus k k) k) (plus (plus k k) k))))))))) in
    rewrite (sym (plusSuccRightSucc (plus (plus (plus (plus k k) k) (plus (plus k k) k)) (S (plus (plus (plus k k) k) (plus (plus k k) k)))) (S (S (S (S (plus (plus (plus k k) k) (plus (plus k k) k)))))))) in
    rewrite (sym (plusSuccRightSucc (plus (plus (plus (plus k k) k) (plus (plus k k) k)) (S (plus (plus (plus k k) k) (plus (plus k k) k)))) (S (S (S (plus (plus (plus k k) k) (plus (plus k k) k))))))) in
    rewrite (sym (plusSuccRightSucc (plus (plus (plus (plus k k) k) (plus (plus k k) k)) (S (plus (plus (plus k k) k) (plus (plus k k) k)))) (S (S (plus (plus (plus k k) k) (plus (plus k k) k)))))) in
    rewrite (sym (plusSuccRightSucc (plus (plus (plus (plus k k) k) (plus (plus k k) k)) (S (plus (plus (plus k k) k) (plus (plus k k) k)))) (S (plus (plus (plus k k) k) (plus (plus k k) k))))) in
      (lteSuccRight . lteSuccRight . lteSuccRight . lteSuccRight . lteSuccRight . lteSuccRight . lteSuccRight . lteSuccRight . lteSuccRight . lteSuccRight . lteSuccRight . lteSuccRight . lteSuccRight . lteSuccRight . lteSuccRight . lteSuccRight . LTESucc . LTESucc) lemma


module SMTLib

import Data.Vect
import System

%access public export

data NumTyp = IntT | RealT

data TypeT : Type where
  BoolT : TypeT
  NumT : NumTyp -> TypeT
  BitVecT : Nat -> TypeT

Show TypeT where
  show BoolT = "Bool"
  show (NumT IntT) = "Int"
  show (NumT RealT) = "Real"
  show (BitVecT n) = "(_ BitVec " ++ show n ++ ")"

data Expr : TypeT -> Type where
  VarExpr : String -> (t : TypeT) -> Expr t
  BoolExpr : Bool -> Expr BoolT
  BvExpr : Int -> (n : Nat) -> Expr (BitVecT n)
  BvAddExpr : Expr (BitVecT n) -> Expr (BitVecT n) -> Expr (BitVecT n)
  BvSubExpr : Expr (BitVecT n) -> Expr (BitVecT n) -> Expr (BitVecT n)
  BvMulExpr : Expr (BitVecT n) -> Expr (BitVecT n) -> Expr (BitVecT n)
  BvUDivExpr : Expr (BitVecT n) -> Expr (BitVecT n) -> Expr (BitVecT n)
  BvURemExpr : Expr (BitVecT n) -> Expr (BitVecT n) -> Expr (BitVecT n)
  BvSDivExpr : Expr (BitVecT n) -> Expr (BitVecT n) -> Expr (BitVecT n)
  BvSModExpr : Expr (BitVecT n) -> Expr (BitVecT n) -> Expr (BitVecT n)
  BvShLExpr :  Expr (BitVecT n) -> Expr (BitVecT n) -> Expr (BitVecT n)
  BvLShRExpr :  Expr (BitVecT n) -> Expr (BitVecT n) -> Expr (BitVecT n)
  BvAShRExpr :  Expr (BitVecT n) -> Expr (BitVecT n) -> Expr (BitVecT n)
  BvNegExpr : Expr (BitVecT n) -> Expr (BitVecT n)
  BvAndExpr : Expr (BitVecT n) -> Expr (BitVecT n) -> Expr (BitVecT n)
  BvOrExpr : Expr (BitVecT n) -> Expr (BitVecT n) -> Expr (BitVecT n)
  BvXorExpr : Expr (BitVecT n) -> Expr (BitVecT n) -> Expr (BitVecT n)
  BvNotExpr : Expr (BitVecT n) -> Expr (BitVecT n)
  BvULeExpr : Expr (BitVecT n) -> Expr (BitVecT n) -> Expr BoolT
  BvULtExpr : Expr (BitVecT n) -> Expr (BitVecT n) -> Expr BoolT
  BvUGeExpr : Expr (BitVecT n) -> Expr (BitVecT n) -> Expr BoolT
  BvUGtExpr : Expr (BitVecT n) -> Expr (BitVecT n) -> Expr BoolT
  BvSLeExpr : Expr (BitVecT n) -> Expr (BitVecT n) -> Expr BoolT
  BvSLtExpr : Expr (BitVecT n) -> Expr (BitVecT n) -> Expr BoolT
  BvSGeExpr : Expr (BitVecT n) -> Expr (BitVecT n) -> Expr BoolT
  BvSGtExpr : Expr (BitVecT n) -> Expr (BitVecT n) -> Expr BoolT
  IntExpr : Int -> Expr (NumT IntT)
  RealExpr : Double -> Expr (NumT RealT)
  AddExpr : Vect n (Expr (NumT a)) -> Expr (NumT a)
  MinusExpr : Vect n (Expr (NumT a)) -> Expr (NumT a)
  MulExpr : Vect n (Expr (NumT a)) -> Expr (NumT a)
  EqualExpr : Expr a -> Expr a -> Expr BoolT
  DistinctExpr : Vect n (Expr a) -> Expr BoolT
  LessExpr : Expr (NumT a) -> Expr (NumT a) -> Expr BoolT
  LessOrEqualExpr : Expr (NumT a) -> Expr (NumT a) -> Expr BoolT
  GreaterExpr : Expr (NumT a) -> Expr (NumT a) -> Expr BoolT
  GreaterOrEqualExpr : Expr (NumT a) -> Expr (NumT a) -> Expr BoolT
  AndExpr : Vect n (Expr BoolT) -> Expr BoolT
  OrExpr : Vect n (Expr BoolT) -> Expr BoolT
  NotExpr : Expr BoolT -> Expr BoolT
  ImpExpr : Expr BoolT -> Expr BoolT -> Expr BoolT
  IteExpr : Expr BoolT -> Expr a -> Expr a -> Expr a

bool : Bool -> Expr BoolT
bool x = BoolExpr x

bv : Int -> (n : Nat) -> Expr (BitVecT n)
bv x n = BvExpr x n

int : Int -> Expr (NumT IntT)
int x = IntExpr x

real : Double -> Expr (NumT RealT)
real x = RealExpr x

bvadd : Expr (BitVecT n) -> Expr (BitVecT n) -> Expr (BitVecT n)
bvadd l r = BvAddExpr l r

bvsub : Expr (BitVecT n) -> Expr (BitVecT n) -> Expr (BitVecT n)
bvsub l r = BvSubExpr l r

bvmul : Expr (BitVecT n) -> Expr (BitVecT n) -> Expr (BitVecT n)
bvmul l r = BvMulExpr l r

bvsdiv : Expr (BitVecT n) -> Expr (BitVecT n) -> Expr (BitVecT n)
bvsdiv l r = BvSDivExpr l r

bvsmod : Expr (BitVecT n) -> Expr (BitVecT n) -> Expr (BitVecT n)
bvsmod l r = BvSModExpr l r

bvudiv : Expr (BitVecT n) -> Expr (BitVecT n) -> Expr (BitVecT n)
bvudiv l r = BvUDivExpr l r

bvurem : Expr (BitVecT n) -> Expr (BitVecT n) -> Expr (BitVecT n)
bvurem l r = BvURemExpr l r

bvshl : Expr (BitVecT n) -> Expr (BitVecT n) -> Expr (BitVecT n)
bvshl l r = BvShLExpr l r

bvlshr : Expr (BitVecT n) -> Expr (BitVecT n) -> Expr (BitVecT n)
bvlshr l r = BvLShRExpr l r

bvashr : Expr (BitVecT n) -> Expr (BitVecT n) -> Expr (BitVecT n)
bvashr l r = BvAShRExpr l r

bvneg : Expr (BitVecT n) -> Expr (BitVecT n)
bvneg x = BvNegExpr x

bvand : Expr (BitVecT n) -> Expr (BitVecT n) -> Expr (BitVecT n)
bvand l r = BvAndExpr l r

bvor : Expr (BitVecT n) -> Expr (BitVecT n) -> Expr (BitVecT n)
bvor l r = BvOrExpr l r

bvxor : Expr (BitVecT n) -> Expr (BitVecT n) -> Expr (BitVecT n)
bvxor l r = BvXorExpr l r

bvnot : Expr (BitVecT n) -> Expr (BitVecT n)
bvnot x = BvNotExpr x

bvule : Expr (BitVecT n) -> Expr (BitVecT n) -> Expr BoolT
bvule l r = BvULeExpr l r

bvult : Expr (BitVecT n) -> Expr (BitVecT n) -> Expr BoolT
bvult l r = BvULtExpr l r

bvuge : Expr (BitVecT n) -> Expr (BitVecT n) -> Expr BoolT
bvuge l r = BvUGeExpr l r

bvugt : Expr (BitVecT n) -> Expr (BitVecT n) -> Expr BoolT
bvugt l r = BvUGtExpr l r

bvsle : Expr (BitVecT n) -> Expr (BitVecT n) -> Expr BoolT
bvsle l r = BvSLeExpr l r

bvslt : Expr (BitVecT n) -> Expr (BitVecT n) -> Expr BoolT
bvslt l r = BvSLtExpr l r

bvsge : Expr (BitVecT n) -> Expr (BitVecT n) -> Expr BoolT
bvsge l r = BvSGeExpr l r

bvsgt : Expr (BitVecT n) -> Expr (BitVecT n) -> Expr BoolT
bvsgt l r = BvSGtExpr l r

(+) : Expr (NumT a) -> Expr (NumT a) -> Expr (NumT a)
(+) l r = AddExpr [l, r]

(-) : Expr (NumT a) -> Expr (NumT a) -> Expr (NumT a)
(-) l r = MinusExpr [l, r]

add : Vect n (Expr (NumT a)) -> Expr (NumT a)
add xs = AddExpr xs

(*) : Expr (NumT a) -> Expr (NumT a) -> Expr (NumT a)
(*) l r = MulExpr [l, r]

mul : Vect n (Expr (NumT a)) -> Expr (NumT a)
mul xs = MulExpr xs

(==) : Expr a -> Expr a -> Expr BoolT
(==) l r = EqualExpr l r

distinct : Vect n (Expr a) -> Expr BoolT
distinct xs = DistinctExpr xs

(<) : Expr (NumT a) -> Expr (NumT a) -> Expr BoolT
(<) l r = LessExpr l r

(<=) : Expr (NumT a) -> Expr (NumT a) -> Expr BoolT
(<=) l r = LessOrEqualExpr l r

(>) : Expr (NumT a) -> Expr (NumT a) -> Expr BoolT
(>) l r = GreaterExpr l r

(>=) : Expr (NumT a) -> Expr (NumT a) -> Expr BoolT
(>=) l r = GreaterOrEqualExpr l r

(&&) : Expr BoolT -> Expr BoolT -> Expr BoolT
(&&) l r = AndExpr [l, r]

(||) : Expr BoolT -> Expr BoolT -> Expr BoolT
(||) l r = OrExpr [l, r]

and : Vect n (Expr BoolT) -> Expr BoolT
and xs = AndExpr xs

or : Vect n (Expr BoolT) -> Expr BoolT
or xs = OrExpr xs

not : Expr BoolT -> Expr BoolT
not x = NotExpr x

imp : Expr BoolT -> Expr BoolT -> Expr BoolT
imp l r = ImpExpr l r

ite' : Expr BoolT -> Expr BoolT -> Expr BoolT -> Expr BoolT
ite' p l r = and [imp p l, imp (not p) r]

ite : Expr BoolT -> Expr a -> Expr a -> Expr a
ite p l r = IteExpr p l r

data Cmd : Type -> Type where
  DeclareVarCmd : String -> (t : TypeT) -> Cmd (Expr t)
  DeclareVarsCmd : Traversable f => f String -> (t : TypeT) -> Cmd (f (Expr t))
  AssertCmd : Expr BoolT -> Cmd ()
  CheckSatCmd : Cmd ()
  GetModelCmd : Cmd ()
  SetOptionCmd : String -> Cmd ()
  SetLogicCmd : String -> Cmd ()

data Smt : Type -> Type where
  Pure : a -> Smt a
  Bind : Cmd a -> (a -> Smt b) -> Smt b

pure : a -> Smt a
pure x = Pure x

(>>=) : Smt a -> (a -> Smt b) -> Smt b
(>>=) (Pure x) f = f x
(>>=) (Bind cmd g) f = Bind cmd (\x => g x >>= f)

end : Smt ()
end = Pure ()

declareVar : String -> (t : TypeT) -> Smt (Expr t)
declareVar v t = Bind (DeclareVarCmd v t) pure

declareVars : Traversable f => f String -> (t : TypeT) -> Smt (f (Expr t))
declareVars vs t = Bind (DeclareVarsCmd vs t) pure

assert : Expr BoolT -> Smt ()
assert e = Bind (AssertCmd e) pure

checkSat : Smt ()
checkSat = Bind CheckSatCmd pure

getModel : Smt ()
getModel = Bind GetModelCmd pure

setOption : String -> Smt ()
setOption s = Bind (SetOptionCmd s) pure

setLogic : String -> Smt ()
setLogic s = Bind (SetLogicCmd s) pure

compileExpr : Expr t -> String
compileExpr (VarExpr x t) = x
compileExpr (BoolExpr x) = if x then "true" else "false"
compileExpr (BvExpr x n) = "(_ bv" ++ show x ++ " " ++ show n ++ ")"
compileExpr (IntExpr x) = show x
compileExpr (RealExpr x) = show x
compileExpr (BvAddExpr l r) = "(bvadd " ++ compileExpr l ++ " " ++ compileExpr r ++ ")"
compileExpr (BvMulExpr l r) = "(bvmul " ++ compileExpr l ++ " " ++ compileExpr r ++ ")"
compileExpr (BvSDivExpr l r) = "(bvsdiv " ++ compileExpr l ++ " " ++ compileExpr r ++ ")"
compileExpr (BvSModExpr l r) = "(bvsmod " ++ compileExpr l ++ " " ++ compileExpr r ++ ")"
compileExpr (BvUDivExpr l r) = "(bvudiv " ++ compileExpr l ++ " " ++ compileExpr r ++ ")"
compileExpr (BvURemExpr l r) = "(bvurem " ++ compileExpr l ++ " " ++ compileExpr r ++ ")"
compileExpr (BvShLExpr l r) = "(bvshl " ++ compileExpr l ++ " " ++ compileExpr r ++ ")"
compileExpr (BvLShRExpr l r) = "(bvlshr " ++ compileExpr l ++ " " ++ compileExpr r ++ ")"
compileExpr (BvAShRExpr l r) = "(bvashr " ++ compileExpr l ++ " " ++ compileExpr r ++ ")"
compileExpr (BvNegExpr x) = "(bvneg " ++ compileExpr x ++ ")"
compileExpr (BvAndExpr l r) = "(bvand " ++ compileExpr l ++ " " ++ compileExpr r ++ ")"
compileExpr (BvSubExpr l r) = "(bvsub " ++ compileExpr l ++ " " ++ compileExpr r ++ ")"
compileExpr (BvOrExpr l r) = "(bvor " ++ compileExpr l ++ " " ++ compileExpr r ++ ")"
compileExpr (BvXorExpr l r) = "(bvxor " ++ compileExpr l ++ " " ++ compileExpr r ++ ")"
compileExpr (BvNotExpr x) = "(bvnot " ++ compileExpr x ++ ")"
compileExpr (BvULeExpr l r) = "(bvule " ++ compileExpr l ++ " " ++ compileExpr r ++ ")"
compileExpr (BvULtExpr l r) = "(bvult " ++ compileExpr l ++ " " ++ compileExpr r ++ ")"
compileExpr (BvUGeExpr l r) = "(bvuge " ++ compileExpr l ++ " " ++ compileExpr r ++ ")"
compileExpr (BvUGtExpr l r) = "(bvugt " ++ compileExpr l ++ " " ++ compileExpr r ++ ")"
compileExpr (BvSLeExpr l r) = "(bvsle " ++ compileExpr l ++ " " ++ compileExpr r ++ ")"
compileExpr (BvSLtExpr l r) = "(bvslt " ++ compileExpr l ++ " " ++ compileExpr r ++ ")"
compileExpr (BvSGeExpr l r) = "(bvsge " ++ compileExpr l ++ " " ++ compileExpr r ++ ")"
compileExpr (BvSGtExpr l r) = "(bvsgt " ++ compileExpr l ++ " " ++ compileExpr r ++ ")"
compileExpr (AddExpr xs) = "(+ " ++ (unlines . toList . map compileExpr) xs ++ ")"
compileExpr (MinusExpr xs) = "(- " ++ (unlines . toList . map compileExpr) xs ++ ")"
compileExpr (MulExpr xs) = "(* " ++ (unlines . toList . map compileExpr) xs ++ ")"
compileExpr (EqualExpr l r) = "(= " ++ compileExpr l ++ " " ++ compileExpr r ++ ")"
compileExpr (DistinctExpr xs) = "(distinct " ++ (unlines . toList . map compileExpr) xs ++ ")"
compileExpr (LessExpr l r) = "(< " ++ compileExpr l ++ " " ++ compileExpr r ++ ")"
compileExpr (LessOrEqualExpr l r) = "(<= " ++ compileExpr l ++ " " ++ compileExpr r ++ ")"
compileExpr (GreaterExpr l r) = "(> " ++ compileExpr l ++ " " ++ compileExpr r ++ ")"
compileExpr (GreaterOrEqualExpr l r) = "(>= " ++ compileExpr l ++ " " ++ compileExpr r ++ ")"
compileExpr (AndExpr xs) = "(and " ++ (unlines . toList . map compileExpr) xs ++ ")"
compileExpr (OrExpr xs) = "(or " ++ (unlines . toList . map compileExpr) xs ++ ")"
compileExpr (NotExpr x) = "(not " ++ compileExpr x ++ ")"
compileExpr (ImpExpr l r) = "(=> " ++ compileExpr l ++ " " ++ compileExpr r ++ ")"
compileExpr (IteExpr p l r) = "(if " ++ compileExpr p ++ " " ++ compileExpr l ++ " " ++ compileExpr r ++ ")"

compileCmd : Cmd a -> (a, String)
compileCmd (DeclareVarCmd x t) = (VarExpr x t, "(declare-const " ++ x ++ " " ++ show t ++ ")")
compileCmd (DeclareVarsCmd xs t) = (map (\x => VarExpr x t) xs,
                                    (unlines . toList . map (\x => "(declare-const " ++ x ++ " " ++ show t ++ ")")) xs)
compileCmd (AssertCmd e) = ((), "(assert " ++ compileExpr e ++ ")")
compileCmd CheckSatCmd = ((), "(check-sat)")
compileCmd GetModelCmd = ((), "(get-model)")
compileCmd (SetOptionCmd s) = ((), "(set-option " ++ s ++ ")")
compileCmd (SetLogicCmd s) = ((), "(set-logic " ++ s ++ ")")

compile : Smt () -> String
compile smt = unlines $ compile' smt
  where
    compile' : Smt () -> List String
    compile' (Pure ()) = [""]
    compile' (Bind cmd f) = let (a, s) = compileCmd cmd in
                           let s' = compile' $ f a in
                           s :: s'

example0 : Expr BoolT
example0 = (bool True) && (bool True)

example1 : Expr BoolT
example1 = (bv 1 8) == (bv 1 8)

example2 : Smt ()
example2 = checkSat

example3 : Smt ()
example3 = do x <- declareVar "x" BoolT
              y <- declareVar "y" BoolT
              assert $ x && y
              checkSat
              getModel
              end

example4 : Smt ()
example4 = do [x, y] <- declareVars {f = Vect 2} ["x", "y"] BoolT
              assert $ not (x && y) == (not x || not y)
              checkSat
              getModel
              end

exec : String -> String -> IO Int
exec input output = system $ "z3 -smt2 " ++ input ++ " > " ++ output

data Result = Sat | UnSat | Unknown

Show Result where
  show Sat = "sat"
  show UnSat = "unsat"
  show Unknown = "unknown"

Model : Type
Model = List (String, String)

parseResult : List String -> Maybe (Result, List String)
parseResult ("sat" :: "(model " :: xs) = Just (Sat, xs)
parseResult ("unsat" :: xs) = Just (UnSat, xs)
parseResult ("unknown" :: xs) = Just (Unknown, xs)
parseResult _ = Nothing

parseVar : List Char -> Maybe (List Char)
parseVar ('(' :: 'd' :: 'e' :: 'f' :: 'i' :: 'n' :: 'e' :: '-' :: 'f' :: 'u' :: 'n' :: ' ' :: xs) = parseVar' xs
  where
    parseVar' : List Char -> Maybe (List Char)
    parseVar' (' ' :: xs) = Just []
    parseVar' (x :: xs) = do xs <- parseVar' xs
                             Just (x :: xs)
    parseVar' _ = Nothing
parseVar _ = Nothing

parseValue' : List Char -> Maybe (List Char)
parseValue' [] = Just []
parseValue' (')' :: _) = Just []
parseValue' (' ' :: _) = Just []
parseValue' [_] = Nothing
parseValue' (x :: xs) = do xs <- parseValue' xs
                           Just (x :: xs)

parseValue : List Char -> Maybe (List Char)
parseValue ('(' :: '_' :: ' ' :: 'b' :: 'v' :: xs) = parseValue' xs
parseValue xs = parseValue' xs

parseModel : List String -> Maybe (Model, List String)
parseModel [")"] = Just ([], [])
parseModel (x :: y :: xs) = do (model, xs) <- parseModel xs
                               var <- parseVar (unpack (ltrim x))
                               value <- parseValue (unpack (ltrim y))
                               Just ((pack var, pack value) :: model, xs)
parseModel _ = Nothing

sat : Smt () -> IO (Maybe (Result, Model))
sat smt = do _ <- writeFile "input.smt2" $ compile smt
             putStrLn "z3 solving"
             _ <- exec "input.smt2" "output.smt2"
             r <- readFile "output.smt2"
             case r of
               Left err => do printLn err; pure Nothing
               Right str => let r = parseResult $ filter (\line => not $ line == "") $ lines str in
                            case r of
                              Nothing => do putStrLn "Error parsing result"; pure Nothing
                              Just (Sat, xs) => let r = parseModel xs in
                                                case r of
                                                  Nothing => do putStrLn "Error parsing model"; pure Nothing
                                                  Just (model, xs) => pure $ Just (Sat, model)
                              Just (UnSat, xs) => pure $ Just (UnSat, [])
                              Just (Unknown, xs) => pure $ Just (Unknown, [])

print : Smt () -> IO ()
print smt = putStrLn $ compile smt


import Data.Vect

{-
Matrix type using type synonyms
 -}

Matrix : Nat -> Nat -> Type
Matrix i j = Vect i (Vect j Double)

testMatrix : Matrix 2 3
testMatrix = [[0, 0, 0], [0, 0, 0]]


module PrintF

----------------------------------------------------------------------
-- Aufwärmen

NNatsFun : Nat -> Type
NNatsFun Z = Nat
NNatsFun (S k) = Nat -> NNatsFun k

adder : (n:Nat) -> Nat -> NNatsFun n
adder Z acc = acc
adder (S k) acc = \n => adder k (acc+n)

----------------------------------------------------------------------
-- PRINTF

-- Beschreibt den Aufbau des Formatstrings:

data Format
     = Num Format
     | Str Format
     | Lit String Format
     | End

-- Typfunktion: Format -> Type

PrintfType : Format -> Type
PrintfType (Num rest) = Integer -> PrintfType rest
PrintfType (Str rest) = String -> PrintfType rest
PrintfType (Lit _ rest) = PrintfType rest
PrintfType End = String

-- Funktion mit Argumenten, die durch `format` gegeben werden
-- und die über `acc` die Ausgabe entsprechend des `format` erstellt

printfToString : (format : Format) -> (acc : String) -> PrintfType format
printfToString (Num rest) acc = \num => printfToString rest (acc ++ show num)
printfToString (Str rest) acc = \str => printfToString rest (acc ++ str)
printfToString (Lit out rest) acc = printfToString rest (acc ++ out)
printfToString End acc = acc

----------------------------------------------------------------------
-- Parsen
parseStringToFormat : String -> Format
parseStringToFormat input = parseChars (unpack input)
  where
    parseChars : List Char -> Format
    parseChars [] = End
    parseChars ('%' :: 'd' :: rest) = Num (parseChars rest)
    parseChars ('%' :: 's' :: rest) = Str (parseChars rest)
    parseChars (c :: rest) =
      case parseChars rest of
        Lit lit rest =>
          Lit (c `strCons` lit) rest
        sonst => Lit (pack [c]) sonst

----------------------------------------------------------------------
-- Endergebnis

printf : (format : String) -> PrintfType (parseStringToFormat format)
printf format = printfToString _ ""


Y : Type
X : Type
Term : Type -> Type

erase : Term a -> Y

unc : (x : Term a) -> erase x = y -> (x : Term a ** erase x = y)
unc x p = (x ** p)

f : (x : Term a) -> erase x = y -> X = a -> (x' : Term X ** erase x' = y)
f x h p = rewrite p in unc x h


module Sub13Apply108x36

import ProofColDivSeqBase
import ProofColDivSeqPostulate

%default total
-- %language ElabReflection

-- 3(36x+12) --F[5,-2]->B[1,-2]--> 3(16x+5)
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)))))))))))) 2
    -> 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))))))))) 2
fb108x36To48x15 o prf =
  let prf2 = lvDown (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)))))))))))) 2
                    prf in
    let prf3 = fb108x36To48x15' o prf2 in lvDown (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 prf3

export
apply108x36 : 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)))))))))))) 2
  -> (m : Nat **
        (LTE (S m)
             (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)))))))))))),
         P m 2))
apply108x36 {o} col = let col2 = fb108x36To48x15 o col in ((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)))))))))
                                                           ** (lte108x36 o, col2)) where
  lte108x36 : (o:Nat) -> LTE (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))))))))))
          (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))))))))))))
  lte108x36 Z = (lteSuccRight . lteSuccRight . lteSuccRight . lteSuccRight . lteSuccRight . lteSuccRight . LTESucc . LTESucc . LTESucc . LTESucc . LTESucc . LTESucc) LTEZero
  lte108x36 (S o) = let lemma = lte108x36 o in

    rewrite (sym (plusSuccRightSucc o o)) in

    rewrite (sym (plusSuccRightSucc (plus o o)  (S (plus o o)))) in
    rewrite (sym (plusSuccRightSucc (plus o o)  (plus o o))) in

    rewrite (sym (plusSuccRightSucc (plus (plus o o) (plus o o))  (S (S (S (plus (plus o o) (plus o o))))))) in
    rewrite (sym (plusSuccRightSucc (plus (plus o o) (plus o o))  (S (S (plus (plus o o) (plus o o)))))) in
    rewrite (sym (plusSuccRightSucc (plus (plus o o) (plus o o))  (S (plus (plus o o) (plus o o))))) in
    rewrite (sym (plusSuccRightSucc (plus (plus o o) (plus o o))  (plus (plus o o) (plus o o)))) in

    rewrite (sym (plusSuccRightSucc (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o)))  (S (S (S (S (S (S (S (plus (plus (plus o o) (plus o o))
                                                            (plus (plus o o) (plus o o)))))))))))) in
    rewrite (sym (plusSuccRightSucc (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o)))  (S (S (S (S (S (S (plus (plus (plus o o) (plus o o))
                                                            (plus (plus o o) (plus o o))))))))))) in
    rewrite (sym (plusSuccRightSucc (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o)))  (S (S (S (S (S (plus (plus (plus o o) (plus o o))
                                                            (plus (plus o o) (plus o o)))))))))) in
    rewrite (sym (plusSuccRightSucc (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o)))  (S (S (S (S (plus (plus (plus o o) (plus o o))
                                                            (plus (plus o o) (plus o o))))))))) in
    rewrite (sym (plusSuccRightSucc (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o)))  (S (S (S (plus (plus (plus o o) (plus o o))
                                                            (plus (plus o o) (plus o o)))))))) in
    rewrite (sym (plusSuccRightSucc (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o)))  (S (S (plus (plus (plus o o) (plus o o))
                                                            (plus (plus o o) (plus o o))))))) in
    rewrite (sym (plusSuccRightSucc (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o)))  (S (plus (plus (plus o o) (plus o o))
                                                            (plus (plus o o) (plus o o)))))) in
    rewrite (sym (plusSuccRightSucc (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))))) in

    rewrite (sym (plusSuccRightSucc (plus o o)  o)) in

    rewrite (sym (plusSuccRightSucc (plus (plus o o) o)  (S (S (plus (plus o o) o))))) in
    rewrite (sym (plusSuccRightSucc (plus (plus o o) o)  (S (plus (plus o o) o)))) in
    rewrite (sym (plusSuccRightSucc (plus (plus o o) o)  (plus (plus o o) o))) in

    rewrite (sym (plusSuccRightSucc (plus (plus (plus o o) o) (plus (plus o o) o))
              (S (S (S (S (S (S (plus (plus (plus o o) o) (plus (plus o o) o)))))))))) in
    rewrite (sym (plusSuccRightSucc (plus (plus (plus o o) o) (plus (plus o o) o))
              (S (S (S (S (S (plus (plus (plus o o) o) (plus (plus o o) o))))))))) in
    rewrite (sym (plusSuccRightSucc (plus (plus (plus o o) o) (plus (plus o o) o))
              (S (S (S (S (plus (plus (plus o o) o) (plus (plus o o) o)))))))) in
    rewrite (sym (plusSuccRightSucc (plus (plus (plus o o) o) (plus (plus o o) o))
              (S (S (S (plus (plus (plus o o) o) (plus (plus o o) o))))))) in
    rewrite (sym (plusSuccRightSucc (plus (plus (plus o o) o) (plus (plus o o) o))
              (S (S (plus (plus (plus o o) o) (plus (plus o o) o)))))) in
    rewrite (sym (plusSuccRightSucc (plus (plus (plus o o) o) (plus (plus o o) o))
              (S (plus (plus (plus o o) o) (plus (plus o o) o))))) in

    rewrite (sym (plusSuccRightSucc (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 (S (S (S (S (S (S (S (S (S (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))))))))))))))))))) in
    rewrite (sym (plusSuccRightSucc (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 (S (S (S (S (S (S (S (S (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)))))))))))))))))) in
    rewrite (sym (plusSuccRightSucc (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 (S (S (S (S (S (S (S (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))))))))))))))))) in
    rewrite (sym (plusSuccRightSucc (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 (S (S (S (S (S (S (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)))))))))))))))) in
    rewrite (sym (plusSuccRightSucc (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 (S (S (S (S (S (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))))))))))))))) in
    rewrite (sym (plusSuccRightSucc (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 (S (S (S (S (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)))))))))))))) in
    rewrite (sym (plusSuccRightSucc (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 (S (S (S (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))))))))))))) in
    rewrite (sym (plusSuccRightSucc (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 (S (S (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)))))))))))) in
    rewrite (sym (plusSuccRightSucc (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 (S (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))))))))))) in
    rewrite (sym (plusSuccRightSucc (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 (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)))))))))) in
    rewrite (sym (plusSuccRightSucc (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 (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))))))))) in
    rewrite (sym (plusSuccRightSucc (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)))))))) in

    rewrite (sym (plusSuccRightSucc (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 (S (S (S (S (S (S (S (S (S (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))))))))))))))))))) in
    rewrite (sym (plusSuccRightSucc (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 (S (S (S (S (S (S (S (S (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)))))))))))))))))) in
    rewrite (sym (plusSuccRightSucc (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 (S (S (S (S (S (S (S (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))))))))))))))))) in
    rewrite (sym (plusSuccRightSucc (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 (S (S (S (S (S (S (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)))))))))))))))) in
    rewrite (sym (plusSuccRightSucc (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 (S (S (S (S (S (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))))))))))))))) in
    rewrite (sym (plusSuccRightSucc (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 (S (S (S (S (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)))))))))))))) in
    rewrite (sym (plusSuccRightSucc (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 (S (S (S (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))))))))))))) in
    rewrite (sym (plusSuccRightSucc (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 (S (S (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)))))))))))) in
    rewrite (sym (plusSuccRightSucc (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 (S (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))))))))))) in
    rewrite (sym (plusSuccRightSucc (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 (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)))))))))) in
    rewrite (sym (plusSuccRightSucc (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 (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))))))))) in
    rewrite (sym (plusSuccRightSucc (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)))))))) in

      (lteSuccRight . lteSuccRight . lteSuccRight . lteSuccRight . lteSuccRight . lteSuccRight . lteSuccRight . lteSuccRight . lteSuccRight . lteSuccRight . lteSuccRight . lteSuccRight . lteSuccRight . lteSuccRight . lteSuccRight . lteSuccRight . lteSuccRight . lteSuccRight . lteSuccRight . lteSuccRight . LTESucc . LTESucc . LTESucc . LTESucc . LTESucc . LTESucc . LTESucc . LTESucc . LTESucc . LTESucc . LTESucc . LTESucc . LTESucc . LTESucc . LTESucc . LTESucc) lemma


module VerifiedInvertible

%default total
%hide Prelude.Algebra.(<*>)
%hide Prelude.Applicative.(<|>)

-- http://www.informatik.uni-marburg.de/~rendel/unparse/

-- core definitions

data T = tt

data PartIso : Type -> Type -> Type where
  MkPartIso : (to : a -> Maybe b) ->
              (from : b -> Maybe a) ->
              (toFrom : (y : b) -> maybe T (\x => x = y) (from y >>= to)) ->
              (fromTo : (x : a) -> maybe T (\y => y = x) (to x >>= from)) ->
              PartIso a b

class PartIsoFunctor (f : Type -> Type -> Type) where
  (<$>) : PartIso a b -> (f a c -> f b c)

class PFunctor (f : Type -> Type -> Type) where
  (<*>) : f a c -> Lazy (f b c) -> f (a, b) c

class PAlternative (f : Type -> Type -> Type) where
  (<|>) : f a c -> Lazy (f a c) -> f a c
  empty : f a c

class (PartIsoFunctor d, PFunctor d, PAlternative d) => Syntax (d : Type -> Type -> Type) c where
  pure : Eq a => a -> d a c
  item : d c c

-- helper functions

ipc_inverse : PartIso a b -> PartIso b a
ipc_inverse (MkPartIso f g tf ft) = MkPartIso g f ft tf

ipc_apply : PartIso a b -> a -> Maybe b
ipc_apply (MkPartIso f g ft ft) = f

ipc_unapply : PartIso a b -> b -> Maybe a
ipc_unapply = ipc_apply . ipc_inverse

-- Inspect and other stuff

data Inspect : a -> Type where
  wi : {A : Type} -> (x : A) -> (y : A) -> (eq: x = y) -> Inspect x

inspect : {A : Type} -> (x : A) -> Inspect x
inspect x = wi x _ refl

match : {A : Type} -> {x : A} -> (y : A) -> {eq : x = y} -> Inspect x
match y {eq} = wi _ y eq

maybe_assoc : (x : Maybe a) -> (f : a -> Maybe b) -> (g : b -> Maybe c) -> (x >>= (\y => f y >>= g)) = (x >>= f >>= g)
maybe_assoc Nothing f g = refl
maybe_assoc (Just x) f g = refl

maybe_unbind : (f : a -> Maybe b) -> (g : b -> Maybe c) -> (x : a) -> {y : b} ->
  (f x = Just y) -> g y = Just x >>= f >>= g
maybe_unbind f g x eq = rewrite eq in refl

-- Algebra

infixr 1 >=>
(>=>) : Monad m => (a -> m b) -> (b -> m c) -> (a -> m c)
f >=> g = \x => f x >>= g

pId : PartIso a a
pId = MkPartIso (\x => Just x) (\x => Just x) (\x => refl) (\x => refl)

(.) : PartIso b c -> PartIso a b -> PartIso a c
(MkPartIso gTo gFrom gTF gFT) . (MkPartIso fTo fFrom fTF fFT) =
  MkPartIso
    (ipc_apply (MkPartIso fTo fFrom fTF fFT) >=> ipc_apply (MkPartIso gTo gFrom gTF gFT))
    (ipc_unapply (MkPartIso gTo gFrom gTF gFT) >=> ipc_unapply (MkPartIso fTo fFrom fTF fFT))
    tf ft
  where
    -- these two are identical, would be nice to generalize
    tf y with (inspect $ gFrom y)
      | match Nothing {eq=eq} = rewrite eq in tt
      | match (Just tfy) {eq=eq} = rewrite eq in
        rewrite (maybe_assoc ((Just tfy) >>= fFrom) fTo gTo) in
          case (inspect $ fFrom tfy >>= fTo) of
            match Nothing {eq=eq2} => rewrite eq2 in tt
            match (Just tfy') {eq=eq2} => rewrite eq2 in
              rewrite (replace eq2 {P=(\y => maybe T (\x => x = tfy) y)} (fTF tfy)) in
                rewrite (maybe_unbind gFrom gTo y eq) in (gTF y)
    ft y with (inspect $ fTo y)
      | match Nothing {eq=eq} = rewrite eq in tt
      | match (Just gty) {eq=eq} = rewrite eq in
        rewrite (maybe_assoc ((Just gty) >>= gTo) gFrom fFrom) in
          case (inspect $ gTo gty >>= gFrom) of
            match Nothing {eq=eq2} => rewrite eq2 in tt
            match (Just gty') {eq=eq2} => rewrite eq2 in
              rewrite (replace eq2 {P=(\y => maybe T (\x => x = gty) y)} (gFT gty)) in
                rewrite (maybe_unbind fTo fFrom y eq) in (fFT y)

-- pTup : PartIso a b -> PartIso c d -> PartIso (a, c) (b, d)
-- pTup i j = MkPartIso f g where
--   f (a, b) = case (ipc_apply i a) of
--     Nothing => Nothing
--     Just x => case (ipc_apply j b) of
--       Nothing => Nothing
--       Just y => Just (x, y)
--   g (c, d) = case (ipc_unapply i c) of
--     Nothing => Nothing
--     Just x => case (ipc_unapply j d) of
--       Nothing => Nothing
--       Just y => Just (x, y)

-- associate : PartIso (a, (b, c)) ((a, b), c)
-- associate = MkPartIso f g where
--   f (a, (b, c)) = Just ((a, b), c)
--   g ((a, b), c) = Just (a, (b, c))

commute : PartIso (a, b) (b, a)
commute = MkPartIso f f (\(a,b) => refl) (\(a,b) => refl) where
  f : (c, d) -> Maybe (d, c)
  f (a, b) = Just (b, a)

unit : {a : Type} -> PartIso a (a, ())
unit = MkPartIso f g tf ft where
  f : {a : Type} -> a -> Maybe (a, ())
  f a = Just (a, ())
  g : {a : Type} -> (a, ()) -> Maybe a
  g (a, ()) = Just a
  tf (a, ()) = refl
  ft a = refl

-- *>, <*

infixl 3 *>
(*>) : Syntax d c => d () c -> d a c -> d a c
p *> q = ipc_inverse unit . commute <$> p <*> q

infixl 3 <*
(<*) : Syntax d c => d a c -> d () c -> d a c
p <* q = ipc_inverse unit <$> p <*> q

-- this function can't be verified: there's no isomorphism when we are
-- ignoring input; neither can it be changed, because it's quite
-- important to be able to ignore input
ignore : a -> PartIso a ()
ignore x = MkPartIso f g tf ft where
  f : a -> Maybe ()
  f y = Just ()
  g : () -> Maybe a
  g () = Just x
  tf () = refl
  ft y = believe_me (x = y)

-- Parser

data Parser a c = MkParser (List c -> Maybe (a, List c))

parse : Parser a c -> List c -> Maybe a
parse (MkParser p) s = p s >>= pure . fst

instance PartIsoFunctor Parser where
  iso <$> (MkParser p) = MkParser pf
  where
    pf l = do
      (x, rest) <- p l
      y <- ipc_apply iso x
      return (y, rest)

instance PFunctor Parser where
  (MkParser p) <*> mkpq = MkParser pf
  where
    pf l = do
      (x, rest) <- p l
      case mkpq of
        (MkParser q) => do
          (y, ret) <- q rest
          return ((x, y), ret)

instance PAlternative Parser where
  (MkParser p) <|> mkpq = MkParser pf
  where
    pf l = case p l of
      Just x => Just x
      Nothing => case mkpq of
        (MkParser q) => q l
  empty = MkParser (\s => Nothing)

instance Syntax Parser c where
  pure x = MkParser (\s => Just (x, s))
  item = MkParser f
  where
    f [] = Nothing
    f (x :: xs) = Just (x, xs)

-- Printer

data Printer a c = MkPrinter (a -> Maybe (List c))

compose : Printer a c -> a -> Maybe (List c)
compose (MkPrinter p) x = p x

instance PartIsoFunctor Printer where
  iso <$> (MkPrinter p) = MkPrinter (\b => ipc_unapply iso b >>= p)

instance PFunctor Printer where
  (MkPrinter p) <*> mkpq = MkPrinter pf
  where
    pf (x, y) = do
      x' <- p x
      case mkpq of
        (MkPrinter q) => do
          y' <- q y
          return (x' ++ y')

instance PAlternative Printer where
  (MkPrinter p) <|> mkpq = MkPrinter $ \s =>
    case (p s) of
      Nothing => case mkpq of
        (MkPrinter q) => q s
      x => x
  empty = MkPrinter (\s => Nothing)

instance Syntax Printer γ where
  pure x = MkPrinter (\y =>
    if x == y
    then Just []
    else Nothing)
  item = MkPrinter (\t => Just [t])

-- Basic parsers/composers

sat : Syntax d a => (a -> Bool) -> d a a
sat {a} p = (MkPartIso check check verify verify) <$> item
  where
    check : a -> Maybe a
    check x = if p x then Just x else Nothing
    verify y with (inspect $ p y)
      | match False {eq=eq} = rewrite eq in tt
      | match True {eq=eq} = rewrite eq in
        rewrite eq in refl

val : (Syntax d a, Eq a) => a -> d a a
val x = sat (== x)

-- -- rep

-- nilv : PartIso () (Vect 0 a)
-- nilv = MkPartIso (const . Just $ Vect.Nil {a=a})
--                  (\xs => case xs of
--                    [] => Just ()
--                    _ => Nothing)

-- consv : PartIso (a, Vect n a) (Vect (S n) a)
-- consv = MkPartIso c1 c2
--   where c1 (x, xs) = Just (x :: xs)
--         c2 (x :: xs) = Just (x, xs)

-- rep : Syntax d c => (n : Nat) -> d a c -> d (Vect n a) c
-- rep Z p = nilv <$> pure ()
-- rep (S k) p = consv <$> p <*> rep k p

-- -- many

-- nil : PartIso () (List a)
-- nil = MkPartIso c1 c2
--   where
--     c1 () = Just []
--     c2 [] = Just ()
--     c2 (x :: xs) = Nothing

-- cons : PartIso (a, List a) (List a)
-- cons = MkPartIso c1 c2
--   where
--     c1 (x, l) = Just $ x :: l
--     c2 [] = Nothing
--     c2 (x :: xs) = Just (x, xs)

-- partial many : Syntax d c => d a c -> d (List a) c
-- many p = (cons <$> (p <*> (many p)))
--      <|> (nil <$> pure ())

-- -- Various types

-- -- List Bool <-> Nat, with size and endianness

-- data Endianness = BE | LE

-- order : Endianness -> Vect n Bool -> Vect n Bool
-- order BE = reverse
-- order LE = id

-- natToBits : Nat -> Endianness -> (k : Nat) -> Vect k Bool
-- natToBits n e v = order e $ natToBits' n v
--   where natToBits' : Nat -> (k : Nat) -> Vect k Bool
--         natToBits' n Z = []
--         natToBits' Z bits = replicate bits False
--         natToBits' n (S bits) = (mod n 2 /= Z) :: natToBits' (div n 2) bits

-- bitsToNat : Vect n Bool -> Endianness -> Nat
-- bitsToNat v e = bitsToNat' (order e v)
--   where bitsToNat' : Vect k Bool -> Nat
--         bitsToNat' [] = Z
--         bitsToNat' (v :: l) = (if v then 1 else 0) + 2 * bitsToNat' l

-- nat : Syntax d Bool => Endianness -> Nat -> d Nat Bool
-- nat endianness size = MkPartIso pf cf <$> rep size item
--   where
--     pf l = Just $ bitsToNat l endianness
--     cf x = Just $ natToBits x endianness size

-- testing

test : Syntax d Nat => d (Nat, Nat) Nat
test = (val 1) <*> item
   <|> (val 2) <*> (val 3)

-- test2 : Syntax d Bool => d (Nat, Bool) Bool
-- test2 = (nat BE 4) <*> (val True)
--     <|> (nat BE 2) <*> (val False)

-- partial test3 : Syntax d Bool => d (Bool, List Nat) Bool
-- test3 = (val True) <*> ((ignore False <$> item) *> (many $ nat BE 4))
--     <|> (val False) <*> ((ignore True <$> item) *> (many $ nat LE 3))

-- -- compose test3 (False, [1,2,3])
-- -- parse test3 [False, True, False, False, False, True, False, True, True, False]

-- -- foldl

-- partial driver : (a -> Maybe a) -> (a -> a)
-- driver step state = case step state of
--   Just state' => driver step state'
--   Nothing => state

-- partial iterate : PartIso a a -> PartIso a a
-- iterate step = MkPartIso f g where
--   f = Just . driver (ipc_apply step)
--   g = Just . driver (ipc_unapply step)

-- step : PartIso (a, b) a -> PartIso (a, List b) (a, List b)
-- step i = (pTup i pId) . associate . (pTup pId (ipc_inverse cons))

-- partial foldl : PartIso (a, b) a -> PartIso (a, List b) a
-- foldl i = ipc_inverse unit . (pTup pId (ipc_inverse nil)) . iterate (step i)


-- --------------------------------------------------------------- [ PVect.idr ]
-- Module    : PVect.idr
-- Copyright : (c) 2015,2016 See CONTRIBUTORS.md
-- License   : see LICENSE
-- --------------------------------------------------------------------- [ EOH ]
||| A `list` construct to create lists of dependent types.
|||
||| One of the problems with using dependent types is that types
||| depend on values. This affects the ability to construct lists of
||| values that have a dependent type. The existing `List` type cannot
||| be used as it requires all elements to have the same type.
module Data.PVect

import Data.Vect
import Data.DVect

import Data.DList

import public Decidable.Equality.Indexed

%default total
%access export

using (aTy : Type, elemTy : (aTy -> Type), x : aTy)

  ||| A list construct for dependent types.
  |||
  ||| @aTy    The type of the value contained within the list element type.
  ||| @len    The length of the list.
  ||| @elemTy The type of the elements within the list
  ||| @as     The List used to contain the different values within the type.
  public export
  data PVect : (aTy : Type)
            -> (elemTy : aTy -> Type)
            -> (predTy : aTy -> Type)
            -> (len : Nat)
            -> (as : Vect len aTy)
            -> (ps : DVect aTy predTy len as)
            -> Type where
    ||| Create an empty List
    Nil  : PVect aTy elemTy prfTy Z Nil Nil
    ||| Cons
    |||
    ||| @ex The element to add
    ||| @rest The list for `elem` to be added to.
    Ext : (ex : elemTy x)
       -> (pf : prfTy x)
       -> (rest : PVect aTy elemTy prfTy n xs ps)
       -> PVect aTy elemTy prfTy (S n) (x::xs) (pf::ps)

(++) : PVect aTy eTy pTy x     xs         xps
    -> PVect aTy eTy pTy y     ys         yps
    -> PVect aTy eTy pTy (x+y) (xs ++ ys) (xps ++ yps)
(++) xs ys with (xs)
  (++) xs ys | [] = ys
  (++) xs ys | (Ext ex pf rest) = Ext ex pf ((++) rest ys)

-- -------------------------------------------------------------- [ Form Tests ]
isNil : PVect aTy elemTy prfTy n as ps -> Bool
isNil Nil     = True
isNil (Ext x pf xs) = False

isCons : PVect aTy elemTy prfTy n as ps -> Bool
isCons l = isNil l == False

-- ------------------------------------------------------------------ [ Length ]

length : PVect aTy elemTy prfTy n as ps -> Nat
length {n} _ = n

-- ---------------------------------------------------------------- [ Indexing ]

index : (idx : Fin l)
     -> (vs : PVect aTy elemTy prfTy l as ps)
     -> elemTy (index idx as)
index FZ (Ext ex px rest) = ex
index (FS x) (Ext ex px rest) = index x rest

head : (vs : PVect aTy elemTy prfTy (S n) (a::as) (p::ps))
    -> (elemTy a, prfTy a)
head (Ext x p rest) = (x,p)

tail : (vs : PVect aTy eTy pTy (S n) (a :: as) (p :: ps))
    -> (PVect aTy eTy pTy n as ps)
tail (Ext ex px rest) = rest

last : (vs : PVect aTy eTy pTy (S n) as ps)
    -> (eTy (last as), pTy (last as))
last (Ext ex px rest) with (rest)
  last (Ext ex px rest) | [] = (ex, px)
  last (Ext ex px rest) | (Ext ey py xs) = last (Ext ey py xs)

init : (vs : PVect aTy eTy pTy (S n) as ps)
    -> PVect aTy eTy pTy n (init as) (init ps)
init (Ext ex px []) = []
init (Ext ex px (Ext ey py zs)) = Ext ex px $ init (Ext ey py zs)

take : (n : Nat)
    -> (vs : PVect aTy eTy pTy (n+m) as ps)
    -> PVect aTy eTy pTy n (take n as) (take n ps)
take Z vs = []
take (S k) (Ext ex px rest) = Ext ex px $ take k rest

{-
takeWhile' : ({i : aTy} -> eTy i -> Bool)
          -> (vs : DVect aTy eTy l is)
          -> (l' : Nat ** bs : Vect l' aTy ** DVect aTy eTy l' bs)
takeWhile' f Nil = (_ ** _ ** Nil)
takeWhile' f (ex :: rest) =
  if f ex
  then (_ ** _ ** DVect.(::) ex (snd $ snd (takeWhile' f rest)))
  else (_ ** _ ** Nil)

drop : (n  : Nat)
    -> (vs : DVect aTy eTy (n + m) as)
    -> DVect aTy eTy m (drop n as)
drop Z vs = vs
drop (S k) (ex :: rest) = drop k rest

dropWhile' : ({i : aTy} -> eTy i -> Bool)
          -> (vs : DVect aTy eTy l is)
          -> (l' : Nat ** bs : Vect l' aTy ** DVect aTy eTy l' bs)
dropWhile' f Nil = (_ ** _ ** Nil)
dropWhile' f (ex :: rest) =
  if f ex
  then dropWhile' f rest
  else (_ ** _ ** DVect.(::) ex rest)

replicate : (n : Nat)
         -> (e : eTy i)
         -> DVect aTy eTy n (replicate n i)
replicate Z e = []
replicate (S k) e = e :: replicate k e

equals : ({i,j : aTy} -> e i -> e j -> Bool)
       -> DVect aTy e a as
       -> DVect aTy e b bs
       -> Bool
equals _ Nil Nil = True
equals f (x::xs) (y::ys) =
  if f x y
  then equals f xs ys
  else False
equals _ _ _ = False

compare : ({i,j : aTy} -> e i -> e j -> Bool)
        -> ({i,j : aTy} -> e i -> e j -> Ordering)
        -> DVect aTy e a as
        -> DVect aTy e b bs
        -> Ordering
compare _ _ Nil Nil = EQ
compare _ _ Nil _   = LT
compare _ _ _   Nil = GT
compare e c (a::as) (b::bs) =
  if (not (e a b))
  then c a b
  else compare e c as bs

concat : (xss : DVect (Vect n iTy) (DVect iTy e n) m iis)
      -> DVect iTy e (m * n) (concat iis)
concat [] = Nil
concat (ex :: rest) = ex ++ concat rest

foldr : ({i : iTy} -> e i -> p -> p)
     -> p
     -> DVect iTy e l as
     -> p
foldr _ acc Nil = acc
foldr f acc (x::xs) = f x (foldr f acc xs)

foldl : ({i : iTy} -> p -> e i -> p)
     -> p
     -> DVect iTy e l as
     -> p
foldl f init Nil = init
foldl f init (x::xs) = foldl f (f init x) xs

namespace Clean
  traverse : Applicative f
          => (funcTy : aTy -> bTy)
          -> (func : {a : aTy} -> elemTy a -> f $ elemTyB (funcTy a))
          -> (xs   : DVect aTy elemTy n as)
          -> f (DVect bTy elemTyB n (map funcTy as))
  traverse fTy func Nil     = pure Nil
  traverse fTy func (x::xs) = [| DVect.(::) (func x) (traverse fTy func xs) |]

namespace ToVect
 traverseToVect : Applicative f
        => (func : {a : aTy} -> elemTy a -> f b)
        -> (xs   : DVect aTy elemTy n as)
        -> f (Vect n b)
 traverseToVect func Nil     = pure Nil
 traverseToVect func (x::xs) = [| (::) (func x) (ToVect.traverseToVect func xs) |]

traverseToVDP : Applicative f
             => {bTy : Type}
             -> {elemTyB : bTy -> Type}
             -> (func : {a : aTy}
                     -> elemTy a
                     -> f (c ** elemTyB c))
             -> (xs : DVect aTy elemTy n as)
             -> f (Vect n (b ** elemTyB b))
traverseToVDP func [] = pure Nil
traverseToVDP func (e :: rest) = [| Vect.(::) (func e) $ traverseToVDP func rest|]

mapToVect : ({i : iTy} -> e i -> b)
         -> DVect iTy e l as
         -> Vect l b
mapToVect _ Nil = Nil
mapToVect f (x::xs) = f x :: mapToVect f xs

mapToList : ({i : iTy} -> e i -> b)
         -> DVect iTy e l as
         -> List b
mapToList f Nil = Nil
mapToList f (ex :: rest) = f ex :: mapToList f rest

toList' : Vect l type -> List type
toList' [] = []
toList' (x :: xs) = x :: toList' xs

mapToDList : DVect iTy e l as
          -> DList iTy e (toList' as)
mapToDList [] = []
mapToDList (ex :: rest) = ex :: mapToDList rest

mapMaybeToVect : ({i : iTy} -> e i -> Maybe b)
              -> DVect iTy e l as
              -> (l' ** Vect l' b)
mapMaybeToVect f Nil = (_ ** Nil)
mapMaybeToVect f (x::xs) =
  let rest = mapMaybeToVect f xs in
   case f x of
     Nothing => rest
     Just y  => (_ ** y :: (snd rest))

zipWith : (fT : iTy -> jTy -> kTy)
       -> (fV : {i : iTy} -> {j : jTy} -> e i -> f j -> g (fT i j))
       -> (xs : DVect iTy e l as)
       -> (ys : DVect jTy f l bs)
       -> DVect kTy g l (zipWith fT as bs)
zipWith fT fV [] [] = []
zipWith fT fV (ex :: restx) (ey :: resty) =
    fV ex ey :: zipWith fT fV restx resty

-- -------------------------------------------------------------------- [ Show ]

show : (showFunc : {a : aTy} -> elemTy a -> String)
    -> (vs : DVect aTy elemTy l as)
    -> String
show showFunc vs {l} = unwords strL
  where
     asVStr : Vect l String
     asVStr = mapToVect showFunc vs

     strL : List String
     strL = ["["] ++ intersperse ","  (toList asVStr) ++ ["]"]

-- -------------------------------------------------------------------- [ Elem ]

data VElem : (iTy : Type)
          -> (elemTy : iTy -> Type)
          -> (e : elemTy i)
          -> (es : DVect iTy elemTy l is)
          -> (prf : Elem i is)
          -> Type
  where
    Hier : (x=y) -> VElem iTy eTy x (y::xs) Here
    Er   : (komst : VElem iTy eTy x xs later)
        -> VElem iTy eTy x (not_x::xs) (There later)

Uninhabited (VElem iTy eTy a Nil prf) where
  uninhabited (Hier _) impossible
  uninhabited (Er _) impossible

vectIsEmpty : DecEq iTy => VElem iTy eTy e [] prf -> Void
vectIsEmpty (Hier _) impossible
vectIsEmpty (Er _) impossible

elemsNotEqual : DecEqIdx iTy eTy => (contra : (ex = ey) -> Void) -> VElem iTy eTy ex (ey :: rest) Here -> Void
elemsNotEqual contra (Hier Refl) = contra Refl

notInVect : DecEqIdx iTy eTy
        => (contra : VElem iTy eTy ex rest later -> Void)
        -> VElem iTy eTy ex (ey :: rest) (There later) -> Void
notInVect contra (Er komst) = contra komst

isElem' : {eTy : iTy -> Type}
       -> (DecEqIdx iTy eTy)
       => (e  : eTy i)
       -> (es : DVect iTy eTy l is)
       -> (prf : Elem i is)
       -> Dec (VElem iTy eTy e es prf)
isElem' ex (ey :: rest) Here with (decEq ex ey Refl)
  isElem' ey (ey :: rest) Here | (Yes Refl) = Yes (Hier Refl)
  isElem' ex (ey :: rest) Here | (No contra) = No (elemsNotEqual contra)

isElem' ex (ey :: rest) (There later) with (isElem' ex rest later)
  isElem' ex (ey :: rest) (There later) | (Yes komst) = Yes (Er komst)
  isElem' ex (ey :: rest) (There later) | (No contra) =
    No (notInVect contra)

isElem : {eTy : iTy -> Type}
      -> (DecEqIdx iTy eTy)
      => (e  : eTy i)
      -> (es : DVect iTy eTy l is)
      -> {auto prf : Elem i is}
      -> Dec (VElem iTy eTy e es prf)
isElem e es {prf} = isElem' e es prf

dropElem : (as : DVect iTy eTy (S l) is)
        -> (prf : VElem iTy eTy a as idx)
        -> DVect iTy eTy l (dropElem is idx)
dropElem (a :: x) (Hier _) = x
dropElem {l = (S k)} (not_x :: xs) (Er komst) =
  not_x :: dropElem xs komst
-}
-- --------------------------------------------------------------------- [ EOF ]


||| ported from https://github.com/pepijnkokke/FirstOrderUnificationInAgda
module Unification

import Data.Fin

%default total
%access public export

||| An identifier
data Name = MkName String

Eq Name where
  (MkName x) == (MkName y) = x == y

||| A term in an untyped lambda-calculus with variables indexed by `v`.
||| Either a variable, an identifier, or a function applied to an argument.
||| We keep track of the depth `d` to ensure terms are finite.
||| @ v the type `Var`s are indexed by
||| @ d the depth of the AST
data TermD : (v : Type) -> (d : Nat) -> Type where
  ||| A variable with index `i`
  Var : (i : v) -> TermD v Z
  ||| an identifier with name `n`
  Identifier : (n : Name) -> TermD v Z
  ||| function application, `f` applied to `x`
  App : (f : TermD v d1) -> (x : TermD v d2) -> TermD v (S (max d1 d2))

||| A TermD with `d` as a reified witness instead of a type param
data Term : (v : Type) -> Type where
  MkTerm : Exists (TermD v) -> Term v

namespace Term
  depth : Term v -> Nat
  depth (MkTerm t) = getWitness t

  {- implicit conversions to/from TermD -}
  implicit
  toTermD : (t : Term v) -> TermD v (depth t)
  toTermD (MkTerm t) = getProof t

  implicit
  fromTermD : TermD v d -> Term v
  fromTermD {d=d} t = MkTerm (Evidence d t)

  {- constructors for Term (making use of implicit conversions to/from TermD) -}
  var : v -> Term v
  var v = Var v

  identifier : {v : Type} -> Name -> Term v
  identifier c = Identifier c

  app : Term v -> Term v -> Term v
  app left right = App left right

  ||| The catamorphism for Term.
  ||| We unpack and use `d` to convince idris `fold'` is total,
  ||| so callers of `fold` can ignore `d` and not worry about totality.
  fold : (v -> r) -> (Name -> r) -> (r -> r -> r) -> Term v -> r
  fold v n f (MkTerm (Evidence d t)) = fold' v n f d t where
    fold' v n f d t = case t of
      Var i => v i
      Identifier c => n c
      App {d1=d1} {d2=d2} l r => f (fold' v n f d1 l) (fold' v n f d2 r)

Functor Term where
  map f t = fold (var . f) identifier app t

Applicative Term where
  pure = var
  mf <*> ma = fold (\f => map f ma) identifier app mf

Monad Term where
  t >>= f = fold f identifier app t

Foldable Term where
  foldr op z t = fold op (const id) (.) t z

Traversable Term where
  traverse f = fold
    (\y => [| var (f y) |])
    (\c => pure (identifier c))
    (\l, r => [| app l r |])

{- Now for some unification... -}

||| A substitution of a `Term (Fin n)` for a variable in `Fin (S n)`.
||| @ n the number of vars after applying the substitution.
data Subst : (n : Nat) -> Type where
  ||| Make a Subst
  ||| @ x the variable to replace
  ||| @ t the term (not mentioning `x`) to replace `x` with
  MkSubst : (x : Fin (S n)) -> (t : Term (Fin n)) -> Subst n

{-
  thick and thin help keep track of var indexes when vars are
  introduced or removed.
-}
||| `thick x` maps each `Fin (S n)` that is not `x` to a unique `Fin n`.
||| Useful when removing `x` from the set of vars (e.g. with a `Subst`).
||| y |-> Just y,        if y < x,
|||       Nothing,       if y = x,
|||       Just (pred y), if y > x
thick : (x, y : Fin (S n)) -> Maybe (Fin n)
thick {n=S k} FZ     (FS y) = Just y              -- y < x
thick {n=S k} (FS x) FZ     = Just FZ             -- y > x
thick {n=S k} (FS x) (FS y) = FS <$> thick x y    -- recurse
thick         _      _      = Nothing             -- y = x

||| `thin x` maps each `Fin n` to a unique `Fin (S n)` that is not `x`.
||| The inverse of `y |-> thick x y` for `y`s s.t. `y != x`.
||| Useful for undoing a `Subst`.
||| y |-> FS y, if y >= x,
|||       y,    if y < x
thin : (x : Fin (S n)) -> (y : Fin n) -> Fin (S n)
thin FZ     y      = FS y
thin x      FZ     = FZ
thin (FS x) (FS y) = FS (thin x y)

||| replaces any occurrance of a `Var y` in `t` with `r` if
||| `x == y` or `Var (thick x y)` otherwise.
||| @ s the substitution to make
||| @ t the term to search and replace in
subst : (s : Subst m) -> (t : Term (Fin (S m))) -> Term (Fin m)
subst (MkSubst x r) t = map (thick x) t >>= (maybe r var)

||| A list of substitutions, of the form
||| `[Subst m d, Subst m-1 d, Subst m-2 d, ...]`.
||| Substitutions are applied left to right.
||| Each `s` in a SubstList reduces the number of variables by one, so
||| successive substitutions operate on the reduced set of var idxs.
||| @ m the number of vars before applying the substitutions.
||| @ n the number of vars remaining after applying the substitutions.
data SubstList : (m : Nat) -> (n : Nat) -> Type where
  Nil : {n : Nat} -> SubstList n n  -- no op, number of vars is unchanged
  (::) : (s : Subst m) ->
         (tail : SubstList m n)
           -> SubstList (S m) n

||| Check that `x` doesn't appear in `t`.
||| If it does, return Nothing.
||| If it doesn't, return `t` but over a smaller set of vars.
check : (x : Fin (S n)) -> (t : Term (Fin (S n))) -> Maybe (Term (Fin n))
check x = traverse (thick x)

||| As long as the var x doesn't appear in t, we can unify x and t
||| by substituting t for x.
flexRigid : Fin n -> Term (Fin n) -> Maybe (Exists (SubstList n))
flexRigid {n=Z} _ _ = Nothing  -- impossible, Fin Z is uninhabited
flexRigid {n=S n'} x t = map (\t' => Evidence n' [MkSubst x t']) (check x t)

||| we can always unify two variables
flexFlex : (x, y : Fin n) -> Exists (SubstList n)
flexFlex {n=Z} _ _ = Evidence Z []  -- impossible, Fin Z is uninhabited
flexFlex {n=S n'} x y = case thick x y of
  Nothing => Evidence (S n') []                -- x = y, no subst needed
  Just y' => Evidence n' [MkSubst x (Var y')]  -- x != y, sub y' for x

||| helper function for unify
unify' : (t1 : TermD (Fin m) d1) ->
         (t2 : TermD (Fin m) d2) ->
         (acc : Exists (SubstList m))
           -> Maybe (Exists (SubstList m))
unify' t1 t2 (Evidence n (s :: tail)) =  -- non-empty `acc`
  map consS $ unify' (subst s t1) (subst s t2) (Evidence n tail) where
    consS (Evidence n' sl) = Evidence n' (s :: sl)
unify' (Var x1) (Var x2) _ = Just (flexFlex x1 x2)
unify' (Var x1) t2       _ = flexRigid x1 t2
unify' t1       (Var x2) _ = flexRigid x2 t1
unify' {m=m} (Identifier c1) (Identifier c2) acc =
  if c1 == c2 then Just acc else Nothing
unify' (App la ra) (App lb rb) acc =
  Just acc >>= unify' la lb >>= unify' ra rb
unify' _ _ _ = Nothing  -- can't unify identifier with function application
                        -- (in first-order unification, anyway)

||| Finds the most general substitutions of terms for variables that makes
||| the two terms equal.
unify : (t1 : Term (Fin m)) -> (t2 : Term (Fin m)) -> Maybe (Exists (SubstList m))
unify {m=m} t1 t2 = unify' t1 t2 (Evidence m [])


module Decidable.Decidable

import Data.Rel
import Data.Fun

||| Interface for decidable n-ary Relations
public export
interface Decidable (ts : Vect k Type) (p : Rel ts) where
  total decide : liftRel ts p Dec

||| Given a `Decidable` n-ary relation, provides a decision procedure for
||| this relation.
decision : (ts : Vect k Type) -> (p : Rel ts) -> (Decidable ts p) => liftRel ts p Dec
decision ts p = decide {ts} {p}

using (a : Type, x : a)
  public export
  data Given : Dec a -> Type where
    Always : Given (Yes x)