> module SequentialDecisionProblems.applications.Main

> import Data.Fin
> import Data.List
> import Data.List.Quantifiers
> import Data.So
> import Effects
> import Effect.Exception
> import Effect.StdIO

> import SequentialDecisionProblems.CoreTheory
> import SequentialDecisionProblems.FullTheory
> import SequentialDecisionProblems.TabBackwardsInduction
> import SequentialDecisionProblems.Utils
> import SequentialDecisionProblems.NonDeterministicDefaults
> import SequentialDecisionProblems.CoreTheoryOptDefaults
> import SequentialDecisionProblems.FullTheoryOptDefaults
> import SequentialDecisionProblems.TabBackwardsInductionOptDefaults

> import SequentialDecisionProblems.applications.LowHigh
> import SequentialDecisionProblems.applications.AvailableUnavailable
> import SequentialDecisionProblems.applications.GoodBad

> import Sigma.Sigma
> import Double.Predicates
> import Double.Postulates
> import Double.Operations
> import Double.Properties
> import NonNegDouble.NonNegDouble
> import NonNegDouble.Constants
> import NonNegDouble.BasicOperations
> import NonNegDouble.Operations
> import NonNegDouble.Properties
> import NonNegDouble.Predicates
> import NonNegDouble.LTEProperties
> import NonNegDouble.Measures
> import NonNegDouble.MeasureProperties
> import Finite.Predicates
> import Finite.Operations
> import Finite.Properties
> import Decidable.Predicates
> import Decidable.Properties
> import LocalEffect.Exception
> import LocalEffect.StdIO
> import Fin.Operations
> import List.Operations
> import List.Properties
> import Unit.Properties

> -- %default total
> %auto_implicits off

> -- %logging 5

* Controls

> SequentialDecisionProblems.CoreTheory.Ctrl _ _ = LowHigh

* States

> SequentialDecisionProblems.CoreTheory.State t
> = (Fin (S t), LowHigh, AvailableUnavailable, GoodBad)

* Transition function

> -- The critical cumulated emissions threshold
> crE : Double
> crE = 0.0

> -- The critical number of decision steps
> crN : Nat
> crN = 8

> -- The transition function:
>
> -- The transition function: high emissions
>
> -- The transition function: high emissions, unavailable GHG technologies
>
> -- The transition function: high emissions, unavailable GHG technologies, good world
> SequentialDecisionProblems.CoreTheory.nexts t (e, High, Unavailable, Good) Low =
>   let ttres : List (State (S t))
>             = [(weaken e, Low,  Unavailable, Good),
>                (    FS e, High, Unavailable, Good),
>                (weaken e, Low,    Available, Good),
>                (    FS e, High,   Available, Good),
>                (weaken e, Low,  Unavailable,  Bad),
>                (    FS e, High, Unavailable,  Bad),
>                (weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   let tfres : List (State (S t))
>             = [(weaken e, Low,  Unavailable, Good),
>                (    FS e, High, Unavailable, Good),
>                (weaken e, Low,    Available, Good),
>                (    FS e, High,   Available, Good),
>                (weaken e, Low,  Unavailable,  Bad),
>                (    FS e, High, Unavailable,  Bad),
>                (weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   let ftres : List (State (S t))
>             = [(weaken e, Low,  Unavailable, Good),
>                (    FS e, High, Unavailable, Good),
>                (weaken e, Low,    Available, Good),
>                (    FS e, High,   Available, Good),
>                (weaken e, Low,  Unavailable,  Bad),
>                (    FS e, High, Unavailable,  Bad),
>                (weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   let ffres : List (State (S t))
>             = [(weaken e, Low,  Unavailable, Good),
>                (    FS e, High, Unavailable, Good),
>                (weaken e, Low,    Available, Good),
>                (    FS e, High,   Available, Good),
>                (weaken e, Low,  Unavailable,  Bad),
>                (    FS e, High, Unavailable,  Bad),
>                (weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   case (t <= crN) of
>     True  => case (fromFin e <= crE) of
>                True  => ttres
>                False => tfres
>     False => case (fromFin e <= crE) of
>                True  => ftres
>                False => ffres
> SequentialDecisionProblems.CoreTheory.nexts t (e, High, Unavailable, Good) High =
>   let ttres : List (State (S t))
>             = [(weaken e, Low,  Unavailable, Good),
>                (    FS e, High, Unavailable, Good),
>                (weaken e, Low,    Available, Good),
>                (    FS e, High,   Available, Good),
>                (weaken e, Low,  Unavailable,  Bad),
>                (    FS e, High, Unavailable,  Bad),
>                (weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   let tfres : List (State (S t))
>             = [(weaken e, Low,  Unavailable, Good),
>                (    FS e, High, Unavailable, Good),
>                (weaken e, Low,    Available, Good),
>                (    FS e, High,   Available, Good),
>                (weaken e, Low,  Unavailable,  Bad),
>                (    FS e, High, Unavailable,  Bad),
>                (weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   let ftres : List (State (S t))
>             = [(weaken e, Low,  Unavailable, Good),
>                (    FS e, High, Unavailable, Good),
>                (weaken e, Low,    Available, Good),
>                (    FS e, High,   Available, Good),
>                (weaken e, Low,  Unavailable,  Bad),
>                (    FS e, High, Unavailable,  Bad),
>                (weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   let ffres : List (State (S t))
>             = [(weaken e, Low,  Unavailable, Good),
>                (    FS e, High, Unavailable, Good),
>                (weaken e, Low,    Available, Good),
>                (    FS e, High,   Available, Good),
>                (weaken e, Low,  Unavailable,  Bad),
>                (    FS e, High, Unavailable,  Bad),
>                (weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   case (t <= crN) of
>     True  => case (fromFin e <= crE) of
>                True  => ttres
>                False => tfres
>     False => case (fromFin e <= crE) of
>                True  => ftres
>                False => ffres
>
> -- The transition function: high emissions, unavailable GHG technologies, bad world
> SequentialDecisionProblems.CoreTheory.nexts t (e, High, Unavailable, Bad) Low =
>   let ttres : List (State (S t))
>             = [(weaken e, Low,  Unavailable,  Bad),
>                (    FS e, High, Unavailable,  Bad),
>                (weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   let tfres : List (State (S t))
>             = [(weaken e, Low,  Unavailable,  Bad),
>                (    FS e, High, Unavailable,  Bad),
>                (weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   let ftres : List (State (S t))
>             = [(weaken e, Low,  Unavailable,  Bad),
>                (    FS e, High, Unavailable,  Bad),
>                (weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   let ffres : List (State (S t))
>             = [(weaken e, Low,  Unavailable,  Bad),
>                (    FS e, High, Unavailable,  Bad),
>                (weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   case (t <= crN) of
>     True  => case (fromFin e <= crE) of
>                True  => ttres
>                False => tfres
>     False => case (fromFin e <= crE) of
>                True  => ftres
>                False => ffres
> SequentialDecisionProblems.CoreTheory.nexts t (e, High, Unavailable, Bad) High =
>   let ttres : List (State (S t))
>             = [(weaken e, Low,  Unavailable,  Bad),
>                (    FS e, High, Unavailable,  Bad),
>                (weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   let tfres : List (State (S t))
>             = [(weaken e, Low,  Unavailable,  Bad),
>                (    FS e, High, Unavailable,  Bad),
>                (weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   let ftres : List (State (S t))
>             = [(weaken e, Low,  Unavailable,  Bad),
>                (    FS e, High, Unavailable,  Bad),
>                (weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   let ffres : List (State (S t))
>             = [(weaken e, Low,  Unavailable,  Bad),
>                (    FS e, High, Unavailable,  Bad),
>                (weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   case (t <= crN) of
>     True  => case (fromFin e <= crE) of
>                True  => ttres
>                False => tfres
>     False => case (fromFin e <= crE) of
>                True  => ftres
>                False => ffres
>
>
> -- The transition function: high emissions, available GHG technologies
>
> -- The transition function: high emissions, available GHG technologies, good world
> SequentialDecisionProblems.CoreTheory.nexts t (e, High, Available, Good) Low =
>   let ttres : List (State (S t))
>             = [(weaken e, Low,    Available, Good),
>                (    FS e, High,   Available, Good),
>                (weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   let tfres : List (State (S t))
>             = [(weaken e, Low,    Available, Good),
>                (    FS e, High,   Available, Good),
>                (weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   let ftres : List (State (S t))
>             = [(weaken e, Low,    Available, Good),
>                (    FS e, High,   Available, Good),
>                (weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   let ffres : List (State (S t))
>             = [(weaken e, Low,    Available, Good),
>                (    FS e, High,   Available, Good),
>                (weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   case (t <= crN) of
>     True  => case (fromFin e <= crE) of
>                True  => ttres
>                False => tfres
>     False => case (fromFin e <= crE) of
>                True  => ftres
>                False => ffres
> SequentialDecisionProblems.CoreTheory.nexts t (e, High, Available, Good) High =
>   let ttres : List (State (S t))
>             = [(weaken e, Low,    Available, Good),
>                (    FS e, High,   Available, Good),
>                (weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   let tfres : List (State (S t))
>             = [(weaken e, Low,    Available, Good),
>                (    FS e, High,   Available, Good),
>                (weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   let ftres : List (State (S t))
>             = [(weaken e, Low,    Available, Good),
>                (    FS e, High,   Available, Good),
>                (weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   let ffres : List (State (S t))
>             = [(weaken e, Low,    Available, Good),
>                (    FS e, High,   Available, Good),
>                (weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   case (t <= crN) of
>     True  => case (fromFin e <= crE) of
>                True  => ttres
>                False => tfres
>     False => case (fromFin e <= crE) of
>                True  => ftres
>                False => ffres
>
> -- The transition function: high emissions, available GHG technologies, bad world
> SequentialDecisionProblems.CoreTheory.nexts t (e, High, Available, Bad) Low =
>   let ttres : List (State (S t))
>             = [(weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   let tfres : List (State (S t))
>             = [(weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   let ftres : List (State (S t))
>             = [(weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   let ffres : List (State (S t))
>             = [(weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   case (t <= crN) of
>     True  => case (fromFin e <= crE) of
>                True  => ttres
>                False => tfres
>     False => case (fromFin e <= crE) of
>                True  => ftres
>                False => ffres
> SequentialDecisionProblems.CoreTheory.nexts t (e, High, Available, Bad) High =
>   let ttres : List (State (S t))
>             = [(weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   let tfres : List (State (S t))
>             = [(weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   let ftres : List (State (S t))
>             = [(weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   let ffres : List (State (S t))
>             = [(weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   case (t <= crN) of
>     True  => case (fromFin e <= crE) of
>                True  => ttres
>                False => tfres
>     False => case (fromFin e <= crE) of
>                True  => ftres
>                False => ffres
>
>
>
>
> -- The transition function: low emissions
>
> -- The transition function: low emissions, unavailable GHG technologies
>
> -- The transition function: low emissions, unavailable GHG technologies, good world
> SequentialDecisionProblems.CoreTheory.nexts t (e, Low, Unavailable, Good) Low =
>   let ttres : List (State (S t))
>             = [(weaken e, Low,  Unavailable, Good),
>                (    FS e, High, Unavailable, Good),
>                (weaken e, Low,    Available, Good),
>                (    FS e, High,   Available, Good),
>                (weaken e, Low,  Unavailable,  Bad),
>                (    FS e, High, Unavailable,  Bad),
>                (weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   let tfres : List (State (S t))
>             = [(weaken e, Low,  Unavailable, Good),
>                (    FS e, High, Unavailable, Good),
>                (weaken e, Low,    Available, Good),
>                (    FS e, High,   Available, Good),
>                (weaken e, Low,  Unavailable,  Bad),
>                (    FS e, High, Unavailable,  Bad),
>                (weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   let ftres : List (State (S t))
>             = [(weaken e, Low,  Unavailable, Good),
>                (    FS e, High, Unavailable, Good),
>                (weaken e, Low,    Available, Good),
>                (    FS e, High,   Available, Good),
>                (weaken e, Low,  Unavailable,  Bad),
>                (    FS e, High, Unavailable,  Bad),
>                (weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   let ffres : List (State (S t))
>             = [(weaken e, Low,  Unavailable, Good),
>                (    FS e, High, Unavailable, Good),
>                (weaken e, Low,    Available, Good),
>                (    FS e, High,   Available, Good),
>                (weaken e, Low,  Unavailable,  Bad),
>                (    FS e, High, Unavailable,  Bad),
>                (weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   case (t <= crN) of
>     True  => case (fromFin e <= crE) of
>                True  => ttres
>                False => tfres
>     False => case (fromFin e <= crE) of
>                True  => ftres
>                False => ffres
> SequentialDecisionProblems.CoreTheory.nexts t (e, Low, Unavailable, Good) High =
>   let ttres : List (State (S t))
>             = [(weaken e, Low,  Unavailable, Good),
>                (    FS e, High, Unavailable, Good),
>                (weaken e, Low,    Available, Good),
>                (    FS e, High,   Available, Good),
>                (weaken e, Low,  Unavailable,  Bad),
>                (    FS e, High, Unavailable,  Bad),
>                (weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   let tfres : List (State (S t))
>             = [(weaken e, Low,  Unavailable, Good),
>                (    FS e, High, Unavailable, Good),
>                (weaken e, Low,    Available, Good),
>                (    FS e, High,   Available, Good),
>                (weaken e, Low,  Unavailable,  Bad),
>                (    FS e, High, Unavailable,  Bad),
>                (weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   let ftres : List (State (S t))
>             = [(weaken e, Low,  Unavailable, Good),
>                (    FS e, High, Unavailable, Good),
>                (weaken e, Low,    Available, Good),
>                (    FS e, High,   Available, Good),
>                (weaken e, Low,  Unavailable,  Bad),
>                (    FS e, High, Unavailable,  Bad),
>                (weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   let ffres : List (State (S t))
>             = [(weaken e, Low,  Unavailable, Good),
>                (    FS e, High, Unavailable, Good),
>                (weaken e, Low,    Available, Good),
>                (    FS e, High,   Available, Good),
>                (weaken e, Low,  Unavailable,  Bad),
>                (    FS e, High, Unavailable,  Bad),
>                (weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   case (t <= crN) of
>     True  => case (fromFin e <= crE) of
>                True  => ttres
>                False => tfres
>     False => case (fromFin e <= crE) of
>                True  => ftres
>                False => ffres
>
> -- The transition function: low emissions, unavailable GHG technologies, bad world
> SequentialDecisionProblems.CoreTheory.nexts t (e, Low, Unavailable, Bad) Low =
>   let ttres : List (State (S t))
>             = [(weaken e, Low,  Unavailable,  Bad),
>                (    FS e, High, Unavailable,  Bad),
>                (weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   let tfres : List (State (S t))
>             = [(weaken e, Low,  Unavailable,  Bad),
>                (    FS e, High, Unavailable,  Bad),
>                (weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   let ftres : List (State (S t))
>             = [(weaken e, Low,  Unavailable,  Bad),
>                (    FS e, High, Unavailable,  Bad),
>                (weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   let ffres : List (State (S t))
>             = [(weaken e, Low,  Unavailable,  Bad),
>                (    FS e, High, Unavailable,  Bad),
>                (weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   case (t <= crN) of
>     True  => case (fromFin e <= crE) of
>                True  => ttres
>                False => tfres
>     False => case (fromFin e <= crE) of
>                True  => ftres
>                False => ffres
> SequentialDecisionProblems.CoreTheory.nexts t (e, Low, Unavailable, Bad) High =
>   let ttres : List (State (S t))
>             = [(weaken e, Low,  Unavailable,  Bad),
>                (    FS e, High, Unavailable,  Bad),
>                (weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   let tfres : List (State (S t))
>             = [(weaken e, Low,  Unavailable,  Bad),
>                (    FS e, High, Unavailable,  Bad),
>                (weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   let ftres : List (State (S t))
>             = [(weaken e, Low,  Unavailable,  Bad),
>                (    FS e, High, Unavailable,  Bad),
>                (weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   let ffres : List (State (S t))
>             = [(weaken e, Low,  Unavailable,  Bad),
>                (    FS e, High, Unavailable,  Bad),
>                (weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   case (t <= crN) of
>     True  => case (fromFin e <= crE) of
>                True  => ttres
>                False => tfres
>     False => case (fromFin e <= crE) of
>                True  => ftres
>                False => ffres
>
>
> -- The transition function: low emissions, available GHG technologies
>
> -- The transition function: low emissions, available GHG technologies, good world
> SequentialDecisionProblems.CoreTheory.nexts t (e, Low, Available, Good) Low =
>   let ttres : List (State (S t))
>             = [(weaken e, Low,    Available, Good),
>                (    FS e, High,   Available, Good),
>                (weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   let tfres : List (State (S t))
>             = [(weaken e, Low,    Available, Good),
>                (    FS e, High,   Available, Good),
>                (weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   let ftres : List (State (S t))
>             = [(weaken e, Low,    Available, Good),
>                (    FS e, High,   Available, Good),
>                (weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   let ffres : List (State (S t))
>             = [(weaken e, Low,    Available, Good),
>                (    FS e, High,   Available, Good),
>                (weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   case (t <= crN) of
>     True  => case (fromFin e <= crE) of
>                True  => ttres
>                False => tfres
>     False => case (fromFin e <= crE) of
>                True  => ftres
>                False => ffres
> SequentialDecisionProblems.CoreTheory.nexts t (e, Low, Available, Good) High =
>   let ttres : List (State (S t))
>             = [(weaken e, Low,    Available, Good),
>                (    FS e, High,   Available, Good),
>                (weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   let tfres : List (State (S t))
>             = [(weaken e, Low,    Available, Good),
>                (    FS e, High,   Available, Good),
>                (weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   let ftres : List (State (S t))
>             = [(weaken e, Low,    Available, Good),
>                (    FS e, High,   Available, Good),
>                (weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   let ffres : List (State (S t))
>             = [(weaken e, Low,    Available, Good),
>                (    FS e, High,   Available, Good),
>                (weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   case (t <= crN) of
>     True  => case (fromFin e <= crE) of
>                True  => ttres
>                False => tfres
>     False => case (fromFin e <= crE) of
>                True  => ftres
>                False => ffres
>
> -- The transition function: low emissions, available GHG technologies, bad world
> SequentialDecisionProblems.CoreTheory.nexts t (e, Low, Available, Bad) Low =
>   let ttres : List (State (S t))
>             = [(weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   let tfres : List (State (S t))
>             = [(weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   let ftres : List (State (S t))
>             = [(weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   let ffres : List (State (S t))
>             = [(weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   case (t <= crN) of
>     True  => case (fromFin e <= crE) of
>                True  => ttres
>                False => tfres
>     False => case (fromFin e <= crE) of
>                True  => ftres
>                False => ffres
> SequentialDecisionProblems.CoreTheory.nexts t (e, Low, Available, Bad) High =
>   let ttres : List (State (S t))
>             = [(weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   let tfres : List (State (S t))
>             = [(weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   let ftres : List (State (S t))
>             = [(weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   let ffres : List (State (S t))
>             = [(weaken e, Low,    Available,  Bad),
>                (    FS e, High,   Available,  Bad)] in
>   case (t <= crN) of
>     True  => case (fromFin e <= crE) of
>                True  => ttres
>                False => tfres
>     False => case (fromFin e <= crE) of
>                True  => ftres
>                False => ffres

* |Val| and |LTE|:

> SequentialDecisionProblems.CoreTheory.Val =
>   NonNegDouble.NonNegDouble

> SequentialDecisionProblems.CoreTheory.plus =
>   NonNegDouble.Operations.plus

> SequentialDecisionProblems.CoreTheory.zero =
>   fromInteger @{NumNonNegDouble} 0

> SequentialDecisionProblems.CoreTheory.LTE =
>   NonNegDouble.Predicates.LTE

> SequentialDecisionProblems.FullTheory.reflexiveLTE =
>   NonNegDouble.LTEProperties.reflexiveLTE

> SequentialDecisionProblems.FullTheory.transitiveLTE =
>   NonNegDouble.LTEProperties.transitiveLTE

> SequentialDecisionProblems.FullTheory.monotonePlusLTE =
>   NonNegDouble.LTEProperties.monotonePlusLTE

> SequentialDecisionProblems.CoreTheoryOptDefaults.totalPreorderLTE =
>   NonNegDouble.LTEProperties.totalPreorderLTE

* Reward function

> -- Ratio of the benefits in a bad world and the benefits in a good world
> badOverGood : NonNegDouble
> badOverGood = cast 0.5

> -- Sanity check
> badOverGoodLTEone : badOverGood `NonNegDouble.Predicates.LTE` one
> badOverGoodLTEone = MkLTE Oh

> -- Ratio between low emissions and step benefits in good world, when
> -- effective technologies for reducing GHG emissions are unavailable
> lowOverGoodUnavailable : NonNegDouble
> lowOverGoodUnavailable = cast 0.1

> -- Ratio between low emissions and step benefits in good world, when
> -- effective technologies for reducing GHG emissions are available
> lowOverGoodAvailable : NonNegDouble
> lowOverGoodAvailable = cast 0.2

> -- Ratio between high emissions and step benefits in a good world
> highOverGood : NonNegDouble
> highOverGood = cast 0.3

> -- Sanity check
> lowOverGoodUnavailableLTEone : lowOverGoodUnavailable `NonNegDouble.Predicates.LTE` one
> lowOverGoodUnavailableLTEone = MkLTE Oh

> -- Sanity check
> lowOverGoodAvailableLTEone : lowOverGoodAvailable `NonNegDouble.Predicates.LTE` one
> lowOverGoodAvailableLTEone = MkLTE Oh

> -- Sanity check
> lowOverGoodUnavailableLTElowOverGoodAvailable : lowOverGoodUnavailable `NonNegDouble.Predicates.LTE` lowOverGoodAvailable
> lowOverGoodUnavailableLTElowOverGoodAvailable = MkLTE Oh

> -- Sanity check
> highOverGoodLTEone : highOverGood `NonNegDouble.Predicates.LTE` one
> highOverGoodLTEone = MkLTE Oh

> -- Sanity check
> lowLTEhigh : lowOverGoodAvailable `NonNegDouble.Predicates.LTE` highOverGood
> lowLTEhigh = MkLTE Oh

> -- Reward function:
> using implementation NumNonNegDouble
>
>   SequentialDecisionProblems.CoreTheory.reward _ _ _ (e, High, Unavailable, Good) =
>     one               + one * highOverGood
>   SequentialDecisionProblems.CoreTheory.reward _ _ _ (e, High, Unavailable,  Bad) =
>     one * badOverGood + one * highOverGood
>
>   SequentialDecisionProblems.CoreTheory.reward _ _ _ (e, High,   Available, Good) =
>     one               + one * highOverGood
>   SequentialDecisionProblems.CoreTheory.reward _ _ _ (e, High,   Available,  Bad) =
>     one * badOverGood + one * highOverGood
>
>   SequentialDecisionProblems.CoreTheory.reward _ _ _ (e,  Low, Unavailable, Good) =
>     one               + one * lowOverGoodUnavailable
>   SequentialDecisionProblems.CoreTheory.reward _ _ _ (e,  Low, Unavailable,  Bad) =
>     one * badOverGood + one * lowOverGoodUnavailable
>
>   SequentialDecisionProblems.CoreTheory.reward _ _ _ (e,  Low,   Available, Good) =
>     one               + one * lowOverGoodAvailable
>   SequentialDecisionProblems.CoreTheory.reward _ _ _ (e,  Low,   Available,  Bad) =
>     one * badOverGood + one * lowOverGoodAvailable

* Completing the problem specification

To be able to apply the verified, generic backwards induction algorithm
of |CoreTheory| to compute optimal policies for our problem, we have to
explain how the decision maker accounts for uncertainties on rewards
induced by uncertainties in the transition function. We first assume
that the decision maker measures uncertain rewards by their expected
value:

> SequentialDecisionProblems.CoreTheory.meas = average
> SequentialDecisionProblems.FullTheory.measMon = monotoneAverage

Further on, we have to implement the notions of viability and
reachability. We start by positing that all states are viable for any
number of steps:

> -- Viable : (n : Nat) -> State t -> Type
> SequentialDecisionProblems.CoreTheory.Viable n x = Unit

From this definition, it trivially follows that all elements of an
arbitrary list of states are viable for an arbitrary number of steps:

> viableLemma : {t, n : Nat} -> (xs : List (State t)) -> SequentialDecisionProblems.CoreTheory.All (Viable n) xs
> viableLemma  Nil      = Nil
> viableLemma {t} {n} (x :: xs) = () :: (viableLemma {t} {n} xs)

This fact and the (less trivial) result that simple probability
distributions are never empty, see |nonEmptyLemma| in
|MonadicProperties| in |List|, allows us to show that the above
definition of |Viable| fulfills |viableSpec1|:

> -- viableSpec1 : (x : State t) -> Viable (S n) x -> GoodCtrl t x
> SequentialDecisionProblems.CoreTheory.viableSpec1 {t} {n} s v =
>   MkSigma Low (ne, av) where
>     ne : SequentialDecisionProblems.CoreTheory.NotEmpty (nexts t s Low)
>     ne = ?kika -- nonEmptyLemma (nexts t s Low)
>     av : SequentialDecisionProblems.CoreTheory.All (Viable {t = S t} n) (nexts t s Low)
>     av = viableLemma {t = S t} (nexts t s Low)

> SequentialDecisionProblems.Utils.finiteViable n x = finiteUnit

> SequentialDecisionProblems.Utils.decidableViable n x = decidableUnit

For reachability, we proceed in a similar way. We say that all states
are reachable

> -- Reachable : State t' -> Type
> SequentialDecisionProblems.CoreTheory.Reachable x' = Unit

which immediately implies |reachableSpec1|:

> -- reachableSpec1 : (x : State t) -> Reachable {t' = t} x -> (y : Ctrl t x) -> All (Reachable {t' = S t}) (nexts t x y)
> SequentialDecisionProblems.CoreTheory.reachableSpec1 {t} x r y = all (nexts t x y) where
>   all : (xs : List (State (S t))) -> SequentialDecisionProblems.CoreTheory.All (Reachable {t' = S t}) xs
>   all Nil = Nil
>   all (x :: xs) = () :: (all xs)

and decidability of |Reachable|:

> SequentialDecisionProblems.TabBackwardsInduction.decidableReachable x = decidableUnit

Finally, we have to show that controls are finite

> -- finiteCtrl : {t : Nat} -> (x : State t) -> Finite (Ctrl t x)
> SequentialDecisionProblems.Utils.finiteCtrl _ = finiteLowHigh

and, in order to use the fast, tail-recursive tabulated version of
backwards induction, that states are finite:

> SequentialDecisionProblems.TabBackwardsInduction.finiteState t =
>   finiteTuple4 finiteFin finiteLowHigh finiteAvailableUnavailable finiteGoodBad

* Optimal policies, optimal decisions, ...

We can now apply the results of our |CoreTheory| and of the |FullTheory|
to compute verified optimal policies, possible state-control sequences,
etc. To this end, we need to be able to show the outcome of the decision
process. This means implemeting functions to print states and controls:

> -- showState : {t : Nat} -> State t -> String
> SequentialDecisionProblems.Utils.showState {t} (e, High, Unavailable, Good) =
>   "(" ++ show (finToNat e) ++ ",H,U,G)"
> SequentialDecisionProblems.Utils.showState {t} (e, High, Unavailable,  Bad) =
>   "(" ++ show (finToNat e) ++ ",H,U,B)"
> SequentialDecisionProblems.Utils.showState {t} (e, High,   Available, Good) =
>   "(" ++ show (finToNat e) ++ ",H,A,G)"
> SequentialDecisionProblems.Utils.showState {t} (e, High,   Available,  Bad) =
>   "(" ++ show (finToNat e) ++ ",H,A,B)"
> SequentialDecisionProblems.Utils.showState {t} (e,  Low, Unavailable, Good) =
>   "(" ++ show (finToNat e) ++ ",L,U,G)"
> SequentialDecisionProblems.Utils.showState {t} (e,  Low, Unavailable,  Bad) =
>   "(" ++ show (finToNat e) ++ ",L,U,B)"
> SequentialDecisionProblems.Utils.showState {t} (e,  Low,   Available, Good) =
>   "(" ++ show (finToNat e) ++ ",L,A,G)"
> SequentialDecisionProblems.Utils.showState {t} (e,  Low,   Available,  Bad) =
>   "(" ++ show (finToNat e) ++ ",L,A,B)"

> -- showControl : {t : Nat} -> {x : State t} -> Ctrl t x -> String
> SequentialDecisionProblems.Utils.showCtrl {t} {x}  Low = "L"
> SequentialDecisionProblems.Utils.showCtrl {t} {x} High = "H"

> -- ad-hoc trajectories computation
> adHocPossibleStateCtrlSeqs  :  {t, n : Nat} -> (x : State t) ->
>                                (ps : PolicySeq t n) ->
>                                M (StateCtrlSeq t n)
> adHocPossibleStateCtrlSeqs {t} {n = Z}    x  Nil        =
>   [(Nil x)]
> adHocPossibleStateCtrlSeqs {t} {n = S m}  x (p :: ps')  =
>   map ((MkSigma x y) ::) (mx' >>= f) where
>     y   :  Ctrl t x
>     y   =  ctrl (p x () ())
>     mx' :  M (State (S t))
>     mx' =  nexts t x y
>     f   :  State (S t) -> M (StateCtrlSeq (S t) m)
>     f x' = adHocPossibleStateCtrlSeqs {n = m} x' ps'

> adHocApp : {t, m, n : Nat} ->
>            StateCtrlSeq t m -> Policy (t + m) (S n) -> M (StateCtrlSeq t (S m))
> adHocApp {t} {m = Z} {n} (Nil {t} x) p = map (f . g) (nexts t x y) where
>   q : Policy t (S n)
>   q = replace {P = \ X => Policy X (S n)} (plusZeroRightNeutral t) p
>   y : Ctrl t x
>   y = ctrl (q x () ())
>   f : StateCtrlSeq (S t) Z -> StateCtrlSeq t (S Z)
>   f = ((MkSigma x y) ::)
>   g : State (S t) -> StateCtrlSeq (S t) Z
>   g x' = Nil x'
> adHocApp {t} {m = S m'} {n} (xy :: xys) p = map f (adHocApp xys q) where
>   f : StateCtrlSeq (S t) (S m') -> StateCtrlSeq t (S (S m'))
>   f = (xy ::)
>   q : Policy (S t + m') (S n)
>   q = replace {P = \ X => Policy X (S n)} (sym (plusSuccRightSucc t m')) p

> adHocExtend : {t, m, n : Nat} ->
>               M (StateCtrlSeq t m) -> PolicySeq (t + m) n -> M (StateCtrlSeq t (m + n))
> adHocExtend mxys {t} {m} {n = Z}         Nil  =
>   replace {P = \ X => M (StateCtrlSeq t X)} (sym (plusZeroRightNeutral m)) mxys
> adHocExtend mxys {t} {m} {n = S n'} (p :: ps) = s2 where
>   s1 : M (StateCtrlSeq t (plus (S m) n'))
>   s1 = adHocExtend mxys' (replace {P = \ X => PolicySeq X n'} (plusSuccRightSucc t m) ps) where
>     mxys' : M (StateCtrlSeq t (S m))
>     mxys' = mxys >>= f where
>       f : StateCtrlSeq t m ->  M (StateCtrlSeq t (S m))
>       f xys = adHocApp xys p
>   s2 : M (StateCtrlSeq t (plus m (S n')))
>   s2 = replace {P = \ X => M (StateCtrlSeq t X)} (plusSuccRightSucc m n') s1

> adHocPossibleStateCtrlSeqs1 : {t, n : Nat} -> (x : State t) ->
>                               (ps : PolicySeq t n) -> M (StateCtrlSeq t n)
> adHocPossibleStateCtrlSeqs1 {t} {n} x ps = s2 where
>
>   s1 : M (StateCtrlSeq t (Z + n))
>   s1 = adHocExtend [(Nil {t} x)] (replace {P = \ X => PolicySeq X n} (sym (plusZeroRightNeutral t)) ps)
>   s2 : M (StateCtrlSeq t n)
>   s2 = replace {P = \ X => M (StateCtrlSeq t X)} (plusZeroLeftNeutral n) s1

>
> using implementation ShowNonNegDouble
>
>   computation : { [STDIO] } Eff ()
>   computation =
>     do putStr ("enter number of steps:\n")
>        nSteps <- getNat
>        case (decidableViable {t = Z} nSteps (FZ, High, Unavailable, Good)) of
>          (Yes v) => do putStrLn ("computing optimal policies ...")
>                        -- ps   <- pure (backwardsInduction Z nSteps)
>                        ps   <- pure (tabTailRecursiveBackwardsInduction Z nSteps)
>                        putStrLn ("computing optimal controls ...")
>                        -- mxys <- pure (possibleStateCtrlSeqs (FZ, High, Unavailable, Good) () v ps)
>                        mxys <- pure (adHocPossibleStateCtrlSeqs (FZ, High, Unavailable, Good) ps)
>                        putStrLn "possible state-control sequences:"
>                        putStr "  "
>                        putStrLn (showlong mxys)
>                        mvs <- pure (possibleRewards' mxys)
>                        putStrLn "possible rewards:"
>                        putStr "  "
>                        putStrLn (show mvs)
>                        putStrLn ("done!")
>          (No _)  => putStrLn ("initial state non viable for " ++ cast {from = Int} (cast nSteps) ++ " steps")

> main : IO ()
> main = run computation

> {-

> ---}

-- Local Variables:
-- idris-packages: ("effects")
-- End:


module ProofColDivSeqMain

import ProofColDivSeqBase
import ProofColDivSeqPostulate
import ProofColDivSeqPostuProof
import Sub01Apply18x3
import Sub02Apply54x12
import Sub03Apply54x30
import Sub04Apply54x48
import Sub05Apply9x6
import Sub06Apply36x9
import Sub07Apply108x27
import Sub08Apply108x63
import Sub09Apply108x99
import Sub10Apply108x18
import Sub11Apply108x54
import Sub12Apply108x90
import Sub13Apply108x36
import Sub14Apply108x72
import Sub15Apply108x108

%default total
-- %language ElabReflection

-- 無限降下法の十分条件
unifi : (n:Nat) -> (Not . P) (S n) -> (m ** (LTE (S m) (S n), (Not . P) m))
unifi n prf with (mod3 n)
  unifi (j + j + j)         prf | ThreeZero with (parity j)
    unifi ((k+k) + (k+k) + (k+k))       prf | ThreeZero | Even = apply18x3 prf
    unifi (S (k+k) + S (k+k) + S (k+k)) prf | ThreeZero | Odd with (mod3 k)
      unifi (S ((l+l+l)+(l+l+l)) + S ((l+l+l)+(l+l+l)) + S ((l+l+l)+(l+l+l))) prf | ThreeZero | Odd | ThreeZero = apply54x12 prf
      unifi (S ((S (l+l+l))+(S (l+l+l))) + S ((S (l+l+l))+(S (l+l+l))) + S ((S (l+l+l))+(S (l+l+l)))) prf | ThreeZero | Odd | ThreeOne = apply54x30 prf
      unifi (S ((S (S (l+l+l)))+(S (S (l+l+l)))) + S ((S (S (l+l+l)))+(S (S (l+l+l)))) + S ((S (S (l+l+l)))+(S (S (l+l+l))))) prf | ThreeZero | Odd | ThreeTwo = apply54x48 prf
  unifi (S (j + j + j))     prf | ThreeOne = apply9x6 prf
  unifi (S (S (j + j + j))) prf | ThreeTwo with (parity j)
    unifi (S (S ((k+k) + (k+k) + (k+k)))) prf | ThreeTwo | Even with (parity k)
      unifi (S (S (((l+l)+(l+l)) + ((l+l)+(l+l)) + ((l+l)+(l+l))))) prf | ThreeTwo | Even | Even = apply36x9 prf
      unifi (S (S ((S (l+l)+S (l+l)) + (S (l+l)+S (l+l)) + (S (l+l)+S (l+l))))) prf | ThreeTwo | Even | Odd with (mod3 l)
        unifi (S (S ((S ((o+o+o)+(o+o+o))+S ((o+o+o)+(o+o+o))) + (S ((o+o+o)+(o+o+o))+S ((o+o+o)+(o+o+o))) + (S ((o+o+o)+(o+o+o))+S ((o+o+o)+(o+o+o)))))) prf | ThreeTwo | Even | Odd | ThreeZero = apply108x27 prf
        unifi (S (S ((S (S (o+o+o)+S (o+o+o))+S (S (o+o+o)+ S (o+o+o))) + (S (S (o+o+o)+ S (o+o+o))+S (S (o+o+o)+ S (o+o+o))) + (S (S (o+o+o)+ S (o+o+o))+S (S (o+o+o)+ S (o+o+o)))))) prf | ThreeTwo | Even | Odd | ThreeOne = apply108x63 prf
        unifi (S (S ((S (S (S (o+o+o))+S (S (o+o+o)))+S (S (S (o+o+o))+ S (S (o+o+o))) + (S (S (S (o+o+o))+ S (S (o+o+o)))+S (S (S (o+o+o))+ S (S (o+o+o)))) + (S (S (S (o+o+o))+ S (S (o+o+o)))+S (S (S (o+o+o))+ S (S (o+o+o)))))))) prf | ThreeTwo | Even | Odd | ThreeTwo = apply108x99 prf
    unifi (S (S (S (k+k) + S (k+k) + S (k+k)))) prf | ThreeTwo | Odd with (parity k)
      unifi (S (S (S ((l+l)+(l+l)) + S ((l+l)+(l+l)) + S ((l+l)+(l+l))))) prf | ThreeTwo | Odd | Even with (mod3 l)
        unifi (S (S (S (((o+o+o)+(o+o+o))+((o+o+o)+(o+o+o))) + S (((o+o+o)+(o+o+o))+((o+o+o)+(o+o+o))) + S (((o+o+o)+(o+o+o))+((o+o+o)+(o+o+o)))))) prf | ThreeTwo | Odd | Even | ThreeZero = apply108x18 prf
        unifi (S (S (S ((S (o+o+o)+S (o+o+o))+(S (o+o+o)+S (o+o+o))) + S ((S (o+o+o)+S (o+o+o))+(S (o+o+o)+S (o+o+o))) + S ((S (o+o+o)+S (o+o+o))+(S (o+o+o)+S (o+o+o)))))) prf | ThreeTwo | Odd | Even | ThreeOne = apply108x54 prf
        unifi (S (S (S ((S (S (o+o+o))+S (S (o+o+o)))+(S (S (o+o+o))+S (S (o+o+o)))) + S ((S (S (o+o+o))+S (S (o+o+o)))+(S (S (o+o+o))+S (S (o+o+o)))) + S ((S (S (o+o+o))+S (S (o+o+o)))+(S (S (o+o+o))+S (S (o+o+o))))))) prf | ThreeTwo | Odd | Even | ThreeTwo = apply108x90 prf
      unifi (S (S (S (S (l+l)+ S (l+l)) + S (S (l+l)+S (l+l)) + S (S (l+l)+S (l+l))))) prf | ThreeTwo | Odd | Odd with (mod3 l)
        unifi (S (S (S (S ((o+o+o)+(o+o+o))+ S ((o+o+o)+(o+o+o))) + S (S ((o+o+o)+(o+o+o))+S ((o+o+o)+(o+o+o))) + S (S ((o+o+o)+(o+o+o))+S ((o+o+o)+(o+o+o)))))) prf | ThreeTwo | Odd | Odd | ThreeZero = apply108x36 prf
        unifi (S (S (S (S (S (o+o+o)+S (o+o+o))+ S (S (o+o+o)+S (o+o+o))) + S (S (S (o+o+o)+S (o+o+o))+S (S (o+o+o)+S (o+o+o))) + S (S (S (o+o+o)+S (o+o+o))+S (S (o+o+o)+S (o+o+o)))))) prf | ThreeTwo | Odd | Odd | ThreeOne = apply108x72 prf
        unifi (S (S (S (S (S (S (o+o+o))+S (S (o+o+o)))+ S (S (S (o+o+o))+S (S (o+o+o)))) + S (S (S (S (o+o+o))+S (S (o+o+o)))+S (S (S (o+o+o))+S (S (o+o+o)))) + S (S (S (S (o+o+o))+S (S (o+o+o)))+S (S (S (o+o+o))+S (S (o+o+o))))))) prf | ThreeTwo | Odd | Odd | ThreeTwo = apply108x108 prf

-- 最終的な定理
divSeqLimited : (n : Nat) -> Limited $ divSeq (n+n+n)
divSeqLimited n = replace (definiP n) {P=id} $ infiniteDescent0 unifi base0
                                    -- ↑このPはreplace内部のPだよ


module Hedgehog.Internal.Range

import Data.Bounded
import Data.Maybe
import Data.Fin
import Decidable.Equality

import Hedgehog.Internal.Util

%hide Prelude.Range

public export
MaxSizeNat : Nat
MaxSizeNat = 100

||| A wrapper of a natural number plus an erased proof that
||| the value is within its bounds.
|||
||| Unlike the original Hedgehog, we allow values in the range [0,100]
||| treating size as a proper percentage.
public export
record Size where
  constructor MkSize
  size     : Nat
  0 sizeOk : size <= MaxSizeNat = True

public export
mkSize : (n : Nat) -> {auto 0 ok : n <= MaxSizeNat = True} -> Size
mkSize n = MkSize n ok

public export
minSize : Size
minSize = mkSize 0

public export
maxSize : Size
maxSize = mkSize MaxSizeNat

export
mkSizeMay : Nat -> Maybe Size
mkSizeMay n with (decEq (n <= MaxSizeNat) True)
  mkSizeMay n | Yes _ = Just $ mkSize n
  mkSizeMay n | No  c = Nothing

export
mkSizeOrZero : Nat -> Size
mkSizeOrZero = fromMaybe minSize . mkSizeMay

export
mkSizeOrMax : Nat -> Size
mkSizeOrMax = fromMaybe maxSize . mkSizeMay

public export
resize : (Nat -> Nat) -> Size -> Size
resize f s = mkSizeOrMax (f s.size)

--------------------------------------------------------------------------------
--          Interface Implementations
--------------------------------------------------------------------------------

export
Show Size where
  showPrec p (MkSize n _) = showPrec p n

export
Eq Size where
  (==) = (==) `on` size

export
Ord Size where
  compare = compare `on` size

export
MinBound Size where
  minBound = minSize

export
MaxBound Size where
  maxBound = maxSize

export
Num Size where
  a + b = mkSizeOrMax $ size a + size b
  a * b = mkSizeOrMax $ size a * size b
  fromInteger = mkSizeOrMax . fromInteger

public export
ToInteger Size where toInteger = toInteger . size

--------------------------------------------------------------------------------
--          Range
--------------------------------------------------------------------------------

public export
record Range a where
  constructor MkRange
  origin  : a
  bounds' : Size -> (a,a)

export
bounds : Size -> Range a -> (a,a)
bounds s r = r.bounds' s

||| Get the lower bound of a range for the given size.
export
lowerBound : Ord a => Size -> Range a -> a
lowerBound sz = uncurry min . bounds sz

||| Get the lower bound of a range for the given size.
export
upperBound : Ord a => Size -> Range a -> a
upperBound sz = uncurry max . bounds sz

export
Functor Range where
  map f (MkRange origin bounds') =
    MkRange (f origin) $
            \si => let (x,y) = bounds' si
                    in (f x, f y)

--------------------------------------------------------------------------------
--          Constant Ranges
--------------------------------------------------------------------------------

||| Construct a range which represents a constant single value.
|||
||| >>> bounds x $ singleton 5
||| (5,5)
|||
||| >>> origin $ singleton 5
||| 5
export
singleton : a -> Range a
singleton x = MkRange x $ \_ => (x, x)

||| Construct a range which is unaffected by the size parameter with a origin
||| point which may differ from the bounds.
|||
||| A range from @-10@ to @10@, with the origin at @0@:
|||
||| >>> bounds x $ constantFrom 0 (-10) 10
||| (-10,10)
|||
||| >>> origin $ constantFrom 0 (-10) 10
||| 0
|||
||| A range from @1970@ to @2100@, with the origin at @2000@:
|||
||| >>> bounds x $ constantFrom 2000 1970 2100
||| (1970,2100)
|||
||| >>> origin $ constantFrom 2000 1970 2100
||| 2000
export
constantFrom : (origin,lower,upper : a) -> Range a
constantFrom o l u = MkRange o $ \_ => (l, u)

||| Construct a range which is unaffected by the size parameter.
|||
||| A range from @0@ to @10@, with the origin at @0@:
|||
||| >>> bounds x $ constant 0 10
||| (0,10)
|||
||| >>> origin $ constant 0 10
||| 0
|||
export
constant : a -> a -> Range a
constant x y = constantFrom x x y

||| Construct a range which is unaffected by the size parameter using the full
||| range of a data type.
|||
||| A range from @-128@ to @127@, with the origin at @0@:
|||
||| >>> bounds x (constantBounded :: Range Int8)
||| (-128,127)
|||
||| >>> origin (constantBounded :: Range Int8)
||| 0
export
constantBounded : (MinBound a, MaxBound a, Num a) => Range a
constantBounded = constantFrom 0 minBound maxBound

--------------------------------------------------------------------------------
--          Linear Ranges
--------------------------------------------------------------------------------

||| Truncate a value so it stays within some range.
|||
||| >>> clamp 5 10 15
||| 10
|||
||| >>> clamp 5 10 0
||| 5
export
clamp : Ord a => (lower,upper,val : a) -> a
clamp l u v = if l > u then min l (max u v)
                       else min u (max l v)

||| Scales an integral value linearly with the size parameter.
export
scaleLinear : ToInteger a => Size -> (origin,bound : a) -> a
scaleLinear sz o0 b0 =
  let o    = toInteger o0
      b    = toInteger b0
      rng  = b - o
      diff = (rng * toInteger sz) `safeDiv` 100
   in fromInteger $ o + diff

||| Scales a fractional value linearly with the size parameter.
export
scaleLinearFrac :  (Neg a, Fractional a) => Size -> (origin,bound : a) -> a
scaleLinearFrac sz o b =
  let diff = (b - o) * (fromIntegral sz / 100)
   in o + diff

export %inline
scaled :  Ord a
       => (scale : Size -> (origin,bound : a) -> a)
       -> (origin,lower,upper : a)
       -> Range a
scaled f o l u = MkRange o $ \sz => let x_sized = clamp l u $ f sz o l
                                        y_sized = clamp l u $ f sz o u
                                     in (x_sized, y_sized)

||| Construct a range which scales the bounds relative to the size parameter.
|||
||| >>> bounds 0 $ linearFrom 0 (-10) 10
||| (0,0)
|||
||| >>> bounds 50 $ linearFrom 0 (-10) 20
||| (-5,10)
|||
||| >>> bounds 100 $ linearFrom 0 (-10) 20
||| (-10,20)
export
linearFrom : Ord a => ToInteger a => (origin,lower,upper : a) -> Range a
linearFrom = scaled scaleLinear

export
linear : Ord a => ToInteger a => (origin,upper : a) -> Range a
linear origin upper = linearFrom origin origin upper

export
linearFin : (n : Nat) -> Range (Fin $ S n)
linearFin n = map toFin $ linearFrom 0 0 (natToInteger n)
  where toFin : Integer -> Fin (S n)
        toFin k = fromMaybe FZ (integerToFin k (S n))

||| Construct a range which scales the bounds relative to the size parameter.
|||
||| This works the same as 'linearFrom', but for fractional values.
export
linearFracFrom :  (Neg a, Ord a, Fractional a)
               => (origin,lower,uppder : a) -> Range a
linearFracFrom = scaled scaleLinearFrac

||| Construct a range which is scaled relative to the size parameter and uses
||| the full range of a data type.
|||
||| >>> bounds 0 (linearBounded :: Range Int8)
||| (0,0)
|||
||| >>> bounds 50 (linearBounded :: Range Int8)
||| (-64,64)
|||
||| >>> bounds 99 (linearBounded :: Range Int8)
|||   (-128,127)
export
linearBounded :  (MinBound a, MaxBound a, Ord a, ToInteger a) => Range a
linearBounded = linearFrom minBound minBound maxBound

--------------------------------------------------------------------------------
--          Exponential Ranges
--------------------------------------------------------------------------------

EulerMinus1 : Double
EulerMinus1 = euler - 1

||| Scale a floating-point number exponentially with the size parameter.
|||
||| Note : This scales the difference between the two values exponentially.
export
scaleExponentialDouble : Size -> (origin,bound : Double) -> Double
scaleExponentialDouble sz o b =
  let e     = fromIntegral sz / 100.0
      delta = b - o
      diff  = pow (abs delta + 1) e - 1
   in o + diff * signum delta

||| Scale an integral exponentially with the size parameter.
export
scaleExponential : ToInteger a => Size -> (origin,bound : a) -> a
scaleExponential sz o b =
  round (scaleExponentialDouble sz (fromIntegral o) (fromIntegral b))

export
exponentialDoubleFrom : (origin,lower,upper : Double) -> Range Double
exponentialDoubleFrom = scaled scaleExponentialDouble

export
exponentialDouble : (lower,upper : Double) -> Range Double
exponentialDouble l u = exponentialDoubleFrom l l u

export
exponentialFrom : Ord a => ToInteger a => (origin,lower,upper : a) -> Range a
exponentialFrom = scaled scaleExponential

export
exponential : Ord a => ToInteger a => (lower,upper : a) -> Range a
exponential l u = exponentialFrom l l u

export
exponentialFin : (n : Nat) -> Range (Fin $ S n)
exponentialFin n = map toFin $ exponentialFrom 0 0 (natToInteger n)
  where toFin : Integer -> Fin (S n)
        toFin k = fromMaybe FZ (integerToFin k (S n))

--------------------------------------------------------------------------------
--          Tests and Proofs
--------------------------------------------------------------------------------

singletonOriginId : origin (singleton x) = x
singletonOriginId = Refl

singletonBoundsId : bounds s (singleton x) = (x,x)
singletonBoundsId = Refl

constantFromOrigin : origin (constantFrom o l u) = o
constantFromOrigin = Refl

constantFromBounds : bounds s (constantFrom o l u) = (l,u)
constantFromBounds = Refl

constantOrigin : origin (constant o u) = o
constantOrigin = Refl

constantBounds : bounds s (constant o u) = (o,u)
constantBounds = Refl

One : Int
One = 1

Zero : Int
Zero = 0

clamp1_100_0 : clamp One 100 0 = One
clamp1_100_0 = Refl

clamp100_1_0 : clamp 100 One 0 = One
clamp100_1_0 = Refl

clamp1_100_150 : clamp One 100 150 = 100
clamp1_100_150 = Refl

clamp100_1_150 : clamp 100 One 150 = 100
clamp100_1_150 = Refl

clamp1_100_50 : clamp One 100 50 = 50
clamp1_100_50 = Refl

clamp100_1_50 : clamp 100 One 50 = 50
clamp100_1_50 = Refl


module Utils.Time

import Utils.Num
import Data.Fin
import Data.String.Parser
import Data.Vect
import Data.String
import Data.Fin.Extra
import Utils.Misc
import Decidable.Equality
import Generics.Derive

%language ElabReflection

public export
data Month : Type where
  January : Month
  February : Month
  March : Month
  April : Month
  May : Month
  June : Month
  July : Month
  August : Month
  September : Month
  October : Month
  November : Month
  December : Month

export
next_month : Month -> Month
next_month January    = February
next_month February   = March
next_month March      = April
next_month April      = May
next_month May        = June
next_month June       = July
next_month July       = August
next_month August     = September
next_month September  = October
next_month October    = November
next_month November   = December
next_month December   = January

export
prev_month : Month -> Month
prev_month January    = December
prev_month February   = January
prev_month March      = February
prev_month April      = March
prev_month May        = April
prev_month June       = May
prev_month July       = June
prev_month August     = July
prev_month September  = August
prev_month October    = September
prev_month November   = October
prev_month December   = November

public export
next_prev_month : (m : Month) -> (next_month $ prev_month m) = m
next_prev_month January    = Refl
next_prev_month February   = Refl
next_prev_month March      = Refl
next_prev_month April      = Refl
next_prev_month May        = Refl
next_prev_month June       = Refl
next_prev_month July       = Refl
next_prev_month August     = Refl
next_prev_month September  = Refl
next_prev_month October    = Refl
next_prev_month November   = Refl
next_prev_month December   = Refl

public export
prev_next_month : (m : Month) -> (prev_month $ next_month m) = m
prev_next_month January    = Refl
prev_next_month February   = Refl
prev_next_month March      = Refl
prev_next_month April      = Refl
prev_next_month May        = Refl
prev_next_month June       = Refl
prev_next_month July       = Refl
prev_next_month August     = Refl
prev_next_month September  = Refl
prev_next_month October    = Refl
prev_next_month November   = Refl
prev_next_month December   = Refl

export
nat_to_month : Nat -> Maybe Month
nat_to_month 1  = Just January
nat_to_month 2  = Just February
nat_to_month 3  = Just March
nat_to_month 4  = Just April
nat_to_month 5  = Just May
nat_to_month 6  = Just June
nat_to_month 7  = Just July
nat_to_month 8  = Just August
nat_to_month 9  = Just September
nat_to_month 10 = Just October
nat_to_month 11 = Just November
nat_to_month 12 = Just December
nat_to_month _ = Nothing

export
fin_to_month : Fin 12 -> Month
fin_to_month 0  = January
fin_to_month 1  = February
fin_to_month 2  = March
fin_to_month 3  = April
fin_to_month 4  = May
fin_to_month 5  = June
fin_to_month 6  = July
fin_to_month 7  = August
fin_to_month 8  = September
fin_to_month 9  = October
fin_to_month 10 = November
fin_to_month 11 = December

export
month_to_nat : Month -> Nat
month_to_nat January   = 1
month_to_nat February  = 2
month_to_nat March     = 3
month_to_nat April     = 4
month_to_nat May       = 5
month_to_nat June      = 6
month_to_nat July      = 7
month_to_nat August    = 8
month_to_nat September = 9
month_to_nat October   = 10
month_to_nat November  = 11
month_to_nat December  = 12

export
month_num_of_dates : Integer -> Month -> Nat
month_num_of_dates _ January = 31
month_num_of_dates year February =
  if year `mod` 4 /= 0 then 28
    else if year `mod` 100 /= 0 then 29
    else if year `mod` 400 /= 0 then 28
    else 29
month_num_of_dates _ March = 31
month_num_of_dates _ April = 30
month_num_of_dates _ May = 31
month_num_of_dates _ June = 30
month_num_of_dates _ July = 31
month_num_of_dates _ August = 31
month_num_of_dates _ September = 30
month_num_of_dates _ October = 31
month_num_of_dates _ November = 30
month_num_of_dates _ December = 31

%runElab derive "Month" [Generic, Meta, DecEq, Eq, Ord, Show]

public export
data FinNonZero : Fin n -> Type where
  FSIsNonZero : FinNonZero (FS x)

show_nat_pad : Nat -> Nat -> String
show_nat_pad p n =
  let str = show n
      pad_len = minus p $ length str
      pad = replicate pad_len '0'
  in pad <+> str

show_fin_pad : Nat -> Fin n -> String
show_fin_pad p n = show_nat_pad p $ finToNat n

public export
record Date where
  constructor MkDate
  year : Integer
  month : Month
  day : Fin (S (month_num_of_dates year month))
  day_non_zero : FinNonZero day

export
epoch_date : Date
epoch_date = MkDate 1970 January 1 FSIsNonZero

export
Eq Date where
  a == b =
    case decEq a.year b.year of
      Yes prf0 =>
        case decEq a.month b.month of
          Yes prf1 => a.day == (rewrite prf0 in rewrite prf1 in b.day)
          No _ => False
      No _ => False

export
Ord Date where
  a `compare` b =
    case decEq a.year b.year of
      Yes prf0 =>
        case decEq a.month b.month of
          Yes prf1 =>
            let b_day = rewrite prf0 in rewrite prf1 in b.day
            in a.day `compare` b_day
          No _ => a.month `compare` b.month
      No _ => a.year `compare` b.year

export
Show Date where
  show date =
    (show $ date.year)
    <+> "/"
    <+> (show $ month_to_nat date.month)
    <+> "/"
    <+> (show_fin_pad 2 date.day)

date_to_tuple : Date -> (Integer, Integer, Integer)
date_to_tuple date = (date.year, natToInteger $ pred $ month_to_nat date.month, finToInteger date.day - 1)

date_to_normalized_tuple : Date -> (Integer, Month, Integer)
date_to_normalized_tuple date = (date.year, date.month, finToInteger date.day - 1)

tuple_normalize_month : (Integer, Integer, Integer) -> (Integer, Month, Integer)
tuple_normalize_month (year, month, day) =
  ( if month >= 0 then year + month `div` 12 else year + ((month + 1) `div` 12) - 1
  , fin_to_month $ modFinNZ (integerToNat $ month `mod'` 12) 12 SIsNonZero
  , day)

tuple_normalize_next_month : (Integer, Month, Integer) -> (Integer, Month, Integer)
tuple_normalize_next_month (year, month, day) =
  let next = next_month month
      new_year = case month of { December => year + 1; _ => year; }
  in (new_year, next, day - (natToInteger $ month_num_of_dates year month))

tuple_normalize_prev_month : (Integer, Month, Integer) -> (Integer, Month, Integer)
tuple_normalize_prev_month (year, month, day) =
  let prev = prev_month month
      new_year = case month of { January => year - 1; _ => year; }
  in (new_year, prev, day + (natToInteger $ month_num_of_dates year prev))

tuple_normalize_day : (Integer, Month, Integer) -> Date
tuple_normalize_day (year, month, day) =
  case integerToFin day (month_num_of_dates year month) of
    Just day' => MkDate year month (FS day') FSIsNonZero
    Nothing =>
      if day < 0
        then tuple_normalize_day $ tuple_normalize_prev_month (year, month, day)
        else tuple_normalize_day $ tuple_normalize_next_month (year, month, day)

tuple_normalize_date : (Integer, Integer, Integer) -> Date
tuple_normalize_date = tuple_normalize_day . tuple_normalize_month

days_between_dates' : (Integer, Month, Integer) -> (Integer, Month, Integer) -> Integer
days_between_dates' a@(a_year, a_month, a_day) b@(b_year, b_month, b_day) =
  case collapse_ordering [a_year `compare` b_year, a_month `compare` b_month] of
    LT => days_between_dates' (tuple_normalize_next_month a) b
    GT => days_between_dates' (tuple_normalize_prev_month a) b
    EQ => a_day - b_day

export
days_between_dates : Date -> Date -> Integer
days_between_dates a b = days_between_dates' (date_to_normalized_tuple a) (date_to_normalized_tuple b)

day_in_seconds : Nat
day_in_seconds = 24 * 60 * 60

day_in_milliseconds : Nat
day_in_milliseconds = 24 * 60 * 60 * 1000

day_in_seconds' : Integer
day_in_seconds' = natToInteger day_in_seconds

day_in_milliseconds' : Integer
day_in_milliseconds' = natToInteger day_in_milliseconds

public export
record DateTime where
  constructor MkDateTime
  date : Date
  hour : Fin 24
  minute : Fin 60
  second : Fin 60
  subsecond : Fin 1000
  offset_plus_or_minus : Bool
  hour_offset : Nat
  minute_offset : Fin 60

second_of_day : Nat -> (Fin 24, Fin 60, Fin 60)
second_of_day x =
  let second = modFinNZ x 60 SIsNonZero
      x = divNatNZ x 60 SIsNonZero
      minute = modFinNZ x 60 SIsNonZero
      x = divNatNZ x 60 SIsNonZero
      hour = modFinNZ x 24 SIsNonZero
  in (hour, minute, second)

export
epoch_to_datetime : Integer -> DateTime
epoch_to_datetime epoch =
  let (hour, minute, second) = second_of_day $ integerToNat $ epoch `mod'` day_in_seconds'
      dayno = if epoch >= 0 then epoch `div` day_in_seconds' else ((epoch + 1) `div` day_in_seconds') - 1
      date = tuple_normalize_date (1970, 0, dayno)
  in MkDateTime date hour minute second 0 True 0 0

export
millisecond_epoch_to_datetime : Integer -> DateTime
millisecond_epoch_to_datetime epoch =
  let subsecond' = integerToNat $ epoch `mod'` 1000
  in { subsecond := modFinNZ subsecond' 1000 SIsNonZero } (epoch_to_datetime $ epoch `div` 1000)

datetime_to_epoch' : DateTime -> Integer
datetime_to_epoch' datetime =
  (days_between_dates datetime.date epoch_date) * day_in_seconds'
  + (finToInteger datetime.hour * 60 * 60)
  + (finToInteger datetime.minute * 60)
  + (finToInteger datetime.second)

millisecond_datetime_to_epoch' : DateTime -> Integer
millisecond_datetime_to_epoch' datetime = (datetime_to_epoch' datetime) * 1000 + finToInteger datetime.subsecond

export
gmt_datetime : DateTime -> DateTime
gmt_datetime datetime =
  let epoch = millisecond_datetime_to_epoch' datetime
      offset = (1000 * 3600 * natToInteger datetime.hour_offset) + (1000 * 60 * finToInteger datetime.minute_offset)
      epoch = if datetime.offset_plus_or_minus then epoch - offset else epoch + offset
  in millisecond_epoch_to_datetime epoch

export
datetime_to_epoch : DateTime -> Integer
datetime_to_epoch = datetime_to_epoch' . gmt_datetime

export
millisecond_datetime_to_epoch : DateTime -> Integer
millisecond_datetime_to_epoch = millisecond_datetime_to_epoch' . gmt_datetime

export
Show DateTime where
  show datetime =
    (show $ datetime.date)
    <+> " "
    <+> (show_fin_pad 2 datetime.hour)
    <+> ":"
    <+> (show_fin_pad 2 datetime.minute)
    <+> ":"
    <+> (show_fin_pad 2 datetime.second)
    <+> "."
    <+> (show_fin_pad 3 datetime.subsecond)
    <+> " "
    <+> (if datetime.offset_plus_or_minus then "+" else "-")
    <+> (show_nat_pad 2 datetime.hour_offset)
    <+> ":"
    <+> (show_fin_pad 2 datetime.minute_offset)

fromDigits : Num a => (Fin 10 -> a) -> List (Fin 10) -> a
fromDigits f xs = foldl addDigit 0 xs
  where
    addDigit : a -> Fin 10 -> a
    addDigit num d = 10*num + f d

natFromDigits : List (Fin 10) -> Nat
natFromDigits = fromDigits finToNat

natFromNDigits : Monad m => Nat -> ParseT m Nat
natFromNDigits n = natFromDigits . toList <$> ntimes n digit

finNFromNDigits : Monad m => String -> (n : Nat) -> Nat -> ParseT m (Fin n)
finNFromNDigits name bound n = do
  number <- natFromNDigits n
  case natToFin number bound of
    Just fin => pure fin
    Nothing => fail $ "invalid " <+> name <+> ": " <+> show number

export
utc_time : Monad m => ParseT m DateTime
utc_time = do
  yy <- natFromDigits . toList <$> ntimes 2 digit
  mm <- natFromDigits . toList <$> ntimes 2 digit
  let year = if yy < 50 then 2000 + natToInteger yy else 1900 + natToInteger yy
  let Just month = nat_to_month mm
  | Nothing => fail $ "invalid month: " <+> show mm
  FS fs_day <- finNFromNDigits "day" (S $ month_num_of_dates year month) 2
  | FZ => fail "invalid day: 0"
  hour <- finNFromNDigits "hour" 24 2
  minute <- finNFromNDigits "minute" 60 2
  second <- option 0 $ finNFromNDigits "second" 60 2
  sign <- satisfy (const True)
  let date = MkDate year month (FS fs_day) FSIsNonZero
  let mk_date_time = MkDateTime date hour minute second 0
  case sign of
    'Z' => pure $ mk_date_time True 0 0
    '+' => uncurry (mk_date_time True) <$> utc_time_offset
    '-' => uncurry (mk_date_time False) <$> utc_time_offset
    err => fail $ "unrecognized timezone sign: " <+> show err
  where
    utc_time_offset : ParseT m (Nat, Fin 60)
    utc_time_offset = do
      hh <- natFromDigits . toList <$> ntimes 2 digit
      minute_off <- finNFromNDigits "minute offset" 60 2
      pure (hh, minute_off)

export
generalized_time : Monad m => ParseT m DateTime
generalized_time = do
  year <- natToInteger . natFromDigits . toList <$> ntimes 4 digit
  mm <- natFromDigits . toList <$> ntimes 2 digit
  let Just month = nat_to_month mm
  | Nothing => fail $ "invalid month: " <+> show mm

  FS fs_day <- finNFromNDigits "day" (S $ month_num_of_dates year month) 2
  | FZ => fail "invalid day: 0"

  hour <- finNFromNDigits "hour" 24 2
  minute <- option 0 $ finNFromNDigits "minute" 60 2
  second <- option 0 $ finNFromNDigits "second" 60 2

  subsecond <- option 0 subsecond

  sign <- satisfy (const True)
  let date = MkDate year month (FS fs_day) FSIsNonZero
  let mk_date_time = MkDateTime date hour minute second subsecond
  case sign of
    'Z' => pure $ mk_date_time True 0 0
    '+' => uncurry (mk_date_time True) <$> utc_time_offset
    '-' => uncurry (mk_date_time False) <$> utc_time_offset
    err => fail $ "unrecognized timezone sign: " <+> show err
  where
    utc_time_offset : ParseT m (Nat, Fin 60)
    utc_time_offset = do
      hh <- natFromDigits . toList <$> ntimes 2 digit
      minute_off <- finNFromNDigits "minute offset" 60 2
      pure (hh, minute_off)
    subsecond : ParseT m (Fin 1000)
    subsecond = do
      a <- char '.' *> natFromNDigits 1
      b <- option 0 $ natFromNDigits 1
      c <- option 0 $ natFromNDigits 1
      case natToFin (a * 100 + b * 10 + c) 1000 of
        Just x => pure x
        Nothing => fail "how"

export
parse_utc_time : String -> Either String DateTime
parse_utc_time = map fst . parse utc_time

export
parse_generalized_time : String -> Either String DateTime
parse_generalized_time = map fst . parse generalized_time


|||
module Toolkit.Data.Graph.EdgeBounded.HasExactDegree.All

import Decidable.Equality

import Data.String
import Data.List.Elem
import Data.List.Quantifiers

import Toolkit.Decidable.Do
import Toolkit.Decidable.Informative

import Toolkit.Data.Nat
import Toolkit.Data.Pair
import Toolkit.Data.List.Size
import Toolkit.Data.List.Occurs.Does

import Toolkit.Data.Graph.EdgeBounded

import public Toolkit.Data.Graph.EdgeBounded.DegreeCommon
import public Toolkit.Data.Graph.EdgeBounded.HasExactDegree

%default total

public export
HasExactDegrees : Vertices type -> Edges -> Type
HasExactDegrees vs es = All (\v => HasExactDegree v es) vs

errorHead : {x : type}
         -> {p : type -> Type}
         -> (p x -> Void)
         -> All p (x :: xs) -> Void
errorHead contra (y :: z) = contra y

errorTail : (All p xs -> Void) -> All p (x :: xs) -> Void
errorTail f (y :: z) = f z

all : {type : Type}
   -> {p  : type -> Type}
   -> (f  : (x : type) -> DecInfo e (p x))
   -> (xs : List type)
         -> DecInfo e (All p xs)
all f [] = Yes []
all f (x :: xs) with (f x)
  all f (x :: xs) | (Yes prfWhy) with (all f xs)
    all f (x :: xs) | (Yes prfWhy) | (Yes y)
      = Yes (prfWhy :: y)
    all f (x :: xs) | (Yes prfWhy) | (No msgWhyNot prfWhyNot)
      = No msgWhyNot (errorTail prfWhyNot)

  all f (x :: xs) | (No msgWhyNot prfWhyNot) = No msgWhyNot (errorHead prfWhyNot)

export
hasExactDegrees : {type : Type}
               -> (vs   : Vertices type)
               -> (es   : Edges)
                       -> DecInfo (HasExactDegree.Error type)
                                  (HasExactDegrees vs es)
hasExactDegrees vs es = all (\v => hasExactDegree v es) vs

-- [ EOF ]


{- These files given an introductory look at programming in the (currently
 - pre-alpha) release of Idris2, a dependently typed programming language.
 - They can be run by installing Idris2 (not trivial at the moment), and
 - the interactive type-driven development features described are available
 - through the Idris Atom plugin. Not all features of dependently typed
 - programmming are touched on, most significantly equality proofs are not
 - discussed.
 -
 - While Haskell makes the claim "all values have types" Idris and other
 - dependently typed languages add the claim that "values are types and
 - types are values." -}

x : Int -- x has type Int (note the symbols : and :: are reversed from Haskell)
x = 4

y : Type -- y has type Type
y = Int  -- y's value is a type

z : Type -- the value Type has type Type as well.
z = Type

{- For proofs in our system to be sound, we should not have Type : Type
 - as this leads to paradoxes. We should instead have universes or levels
 - of types, i.e. Type : Type1, Type1 : Type2. This already exists in other
 - dependently typed languages and will eventually exist in Idris2. Don't
 - worry about this for now, as it will not be important to the examples.

 - To understand dependent types, we will first revisit polymorphism. What
 - are polymorphic values? They are functions on types. -}

-- Consider the polymorphic identity function
id : a -> a
id x = x

{- id2 below makes explicit in the type signature what is implicit above, that
 - it takes an initial {implicit} type argument and returns a monomorphic
 - identity function on that type. Since the value is implicit, Idris can
 - infer it from the other arguments. -}

id2 : {a : Type} -> a -> a -- takes a value `a` of type `Type`
id2 x = x                  -- and a value `x` of type `a`.

id3 : Int -> Int
id3 = id2 { a = Int } -- you can pass implicit arguments explicitly

{- We could also write an identity function with an explicit type parameter -}

id4 : (a : Type) -> a -> a
id4 a x = x

{- These are already examples of a "dependent type", where part of the type
 - (the type `a` of the second argument in this instance) depends on the
 - *value* `a` passed (possibly implicitly) as the first argument. -}


module Solutions.IO

import Data.List1
import Data.String

import System.File

%default total

--------------------------------------------------------------------------------
--          Pure Side Effects?
--------------------------------------------------------------------------------

-- 1
rep : (String -> String) -> IO ()
rep f = do
  s <- getLine
  putStrLn (f s)

-- 2
covering
repl : (String -> String) -> IO ()
repl f = do
  _ <- rep f
  repl f

-- 3
covering
replTill : (String -> Either String String) -> IO ()
replTill f = do
  s <- getLine
  case f s of
    Left  msg => putStrLn msg
    Right msg => do
      _ <- putStrLn msg
      replTill f

-- 4
data Error : Type where
  NotAnInteger    : (value : String) -> Error
  UnknownOperator : (value : String) -> Error
  ParseError      : (input : String) -> Error

dispError : Error -> String
dispError (NotAnInteger v)    = "Not an integer: " ++ v ++ "."
dispError (UnknownOperator v) = "Unknown operator: " ++ v ++ "."
dispError (ParseError v)      = "Invalid expression: " ++ v ++ "."

readInteger : String -> Either Error Integer
readInteger s = maybe (Left $ NotAnInteger s) Right $ parseInteger s

readOperator : String -> Either Error (Integer -> Integer -> Integer)
readOperator "+" = Right (+)
readOperator "*" = Right (*)
readOperator s   = Left (UnknownOperator s)

eval : String -> Either Error Integer
eval s =
  let [x,y,z]  := forget $ split isSpace s | _ => Left (ParseError s)
      Right v1 := readInteger x  | Left e => Left e
      Right op := readOperator y | Left e => Left e
      Right v2 := readInteger z  | Left e => Left e
   in Right $ op v1 v2

covering
exprProg : IO ()
exprProg = replTill prog
  where prog : String -> Either String String
        prog "done" = Left "Goodbye!"
        prog s      = Right . either dispError show $ eval s

-- 5
covering
replWith :  (state      : s)
         -> (next       : s -> String -> Either res s)
         -> (dispState  : s -> String)
         -> (dispResult : res -> s -> String)
         -> IO ()
replWith state next dispState dispResult = do
  _     <- putStrLn (dispState state)
  input <- getLine
  case next state input of
    Left  result => putStrLn (dispResult result state)
    Right state' => replWith state' next dispState dispResult

-- 6
data Abort : Type where
  NoNat : (input : String) -> Abort
  Done  : Abort

printSum : Nat -> String
printSum n =
  "Current sum: " ++ show n ++ "\nPlease enter a natural number:"

printRes : Abort -> Nat -> String
printRes (NoNat input) _ =
  "Not a natural number: " ++ input ++ ". Aborting..."
printRes Done k =
  "Final sum: " ++ show k ++ "\nHave a nice day."

readInput : Nat -> String -> Either Abort Nat
readInput _ "done" = Left Done
readInput n s      = case parseInteger {a = Integer} s of
  Nothing => Left $ NoNat s
  Just v  => if v >= 0 then Right (cast v + n) else Left (NoNat s)

covering
sumProg : IO ()
sumProg = replWith 0 readInput printSum printRes

--------------------------------------------------------------------------------
--          Do Blocks, Desugared
--------------------------------------------------------------------------------

-- 1
ex1a : IO String
ex1a = do
  s1 <- getLine
  s2 <- getLine
  s3 <- getLine
  pure $ s1 ++ reverse s2 ++ s3

ex1aBind : IO String
ex1aBind =
  getLine >>= (\s1 =>
    getLine >>= (\s2 =>
      getLine >>= (\s3 =>
        pure $ s1 ++ reverse s2 ++ s3
      )
    )
  )

ex1aBang : IO String
ex1aBang =
  pure $ !getLine ++ reverse !getLine ++ !getLine

ex1b : Maybe Integer
ex1b = do
  n1 <- parseInteger "12"
  n2 <- parseInteger "300"
  Just $ n1 + n2 * 100

ex1bBind : Maybe Integer
ex1bBind =
  parseInteger "12" >>= (\n1 =>
    parseInteger "300" >>= (\n2 =>
      Just $ n1 + n2 * 100
    )
  )

ex1bBang : Maybe Integer
ex1bBang =
  Just $ !(parseInteger "12") + !(parseInteger "300") * 100

-- 2
data List01 : (nonEmpty : Bool) -> Type -> Type where
  Nil  : List01 False a
  (::) : a -> List01 False a -> List01 ne a

head : List01 True a -> a
head (x :: _) = x

weaken : List01 ne a -> List01 False a
weaken []       = []
weaken (h :: t) = h :: t

map01 : (a -> b) -> List01 ne a -> List01 ne b
map01 _ []       = []
map01 f (x :: y) = f x :: map01 f y

tail : List01 True a -> List01 False a
tail (_ :: t) = weaken t

(++) : List01 ne1 a -> List01 ne2 a -> List01 (ne1 || ne2) a
(++) []       []       = []
(++) []       (h :: t) = h :: t
(++) (h :: t) xs       = h :: weaken (t ++ xs)

concat' : List01 ne1 (List01 ne2 a) -> List01 False a
concat' []       = []
concat' (x :: y) = weaken (x ++ concat' y)

concat :  {ne1, ne2 : _}
       -> List01 ne1 (List01 ne2 a)
       -> List01 (ne1 && ne2) a
concat {ne1 = True}  {ne2 = True}  (x :: y) = x ++ concat' y
concat {ne1 = True}  {ne2 = False} x        = concat' x
concat {ne1 = False} {ne2 = _}     x        = concat' x

namespace List01
  export
  (>>=) :  {ne1, ne2 : _}
        -> List01 ne1 a
        -> (a -> List01 ne2 b)
        -> List01 (ne1 && ne2) b
  as >>= f = concat (map01 f as)

--------------------------------------------------------------------------------
--          Working with Files
--------------------------------------------------------------------------------

-- 1
namespace IOErr
  export
  pure : a -> IO (Either e a)
  pure = pure . Right

  export
  fail : e -> IO (Either e a)
  fail = pure . Left

  export
  lift : IO a -> IO (Either e a)
  lift = map Right

  export
  catch : IO (Either e1 a) -> (e1 -> IO (Either e2 a)) -> IO (Either e2 a)
  catch io f = do
    Left err <- io | Right v => pure v
    f err

  export
  (>>=) : IO (Either e a) -> (a -> IO (Either e b)) -> IO (Either e b)
  io >>= f = Prelude.do
    Right v <- io | Left err => fail err
    f v

  export
  (>>) : IO (Either e ()) -> Lazy (IO (Either e a)) -> IO (Either e a)
  iou >> ioa = Prelude.do
    Right _ <- iou | Left err => fail err
    ioa

covering
countEmpty'' : (path : String) -> IO (Either FileError Nat)
countEmpty'' path = withFile path Read pure (go 0)
  where covering go : Nat -> File -> IO (Either FileError Nat)
        go k file = do
          False <- lift (fEOF file) | True => pure k
          "\n"  <- fGetLine file    | _  => go k file
          go (k + 1) file

-- 2
covering
countWords : (path : String) -> IO (Either FileError Nat)
countWords path = withFile path Read pure (go 0)
  where covering go : Nat -> File -> IO (Either FileError Nat)
        go k file = do
          False <- lift (fEOF file) | True => pure k
          s     <- fGetLine file
          go (k + length (words s)) file

-- 3
covering
withLines :  (path : String)
          -> (accum : s -> String -> s)
          -> (initialState : s)
          -> IO (Either FileError s)
withLines path accum ini = withFile path Read pure (go ini)
  where covering go : s -> File -> IO (Either FileError s)
        go st file = do
          False <- lift (fEOF file) | True => pure st
          line  <- fGetLine file
          go (accum st line) file

covering
countEmpty3 : (path : String) -> IO (Either FileError Nat)
countEmpty3 path = withLines path acc 0
  where acc : Nat -> String -> Nat
        acc k "\n" = k + 1
        acc k _    = k

covering
countWords2 : (path : String) -> IO (Either FileError Nat)
countWords2 path = withLines path (\n,s => n + length (words s)) 0

-- 4
covering
foldLines :  Monoid s
          => (path : String)
          -> (f    : String -> s)
          -> IO (Either FileError s)
foldLines path f = withLines path (\vs => (vs <+>) . f) neutral

-- 5

-- Instead of returning a triple of natural numbers,
-- it is better to make the semantics clear and use
-- a custom record type to store the result.
--
-- In a larger, more-complex application it might be
-- even better to make things truly type safe and
-- define a single field record together with an instance
-- of monoid for each kind of count.
record WC where
  constructor MkWC
  lines : Nat
  words : Nat
  chars : Nat

Semigroup WC where
  MkWC l1 w1 c1 <+> MkWC l2 w2 c2 = MkWC (l1 + l2) (w1 + w2) (c1 + c2)

Monoid WC where
  neutral = MkWC 0 0 0

covering
toWC : String -> WC
toWC s = MkWC 1 (length (words s)) (length s)

covering
wordCount : (path : String) -> IO (Either FileError WC)
wordCount path = foldLines path toWC

-- this is for testing the `wordCount` example.
covering
testWC : (path : String) -> IO ()
testWC path = Prelude.do
  Right (MkWC ls ws cs) <- wordCount path
    | Left err => putStrLn "Error: \{show err}"
  putStrLn "\{show ls} lines, \{show ws} words, \{show cs} characters"


module Lambda

%default total

data Ty = TyFunc Ty Ty | TyNat

data Context = Empty | Extend Context Ty

data Contains : Context -> Ty -> Type where
  Here : Contains (Extend g ty) ty
  There : (rest :  Contains g ty)
       -> Contains (Extend g not_ty) ty

data Term : Context -> Ty -> Type where
  Var : (idx : Contains g ty)
     -> Term g ty

  Func : (body : Term (Extend g tyA) tyB)
      -> Term g (TyFunc tyA tyB)

  App : (func : Term g (TyFunc tyA tyB))
     -> (var  : Term g tyA)
     -> Term g tyB

  Zero : Term g TyNat

  Next : (inner : Term g TyNat)
      -> Term g TyNat

  Case : (scrutinee : Term g TyNat)
      -> (zero : Term g tyA)
      -> (next : Term (Extend g TyNat) tyA)
      -> Term g tyA

  Plus : Term g TyNat
      -> Term g TyNat
      -> Term g TyNat

  Rec : (body : Term (Extend g tyA) tyA)
    -> Term g tyA

term : Term Empty
            (TyFunc TyNat -- (Var Here) -
                    (TyFunc TyNat -- (Var (There Here)) -
                            (TyFunc TyNat -- (Var (There (There Here))) -
                                    (TyFunc TyNat -- (Var (There (There (There Here)))) -
                                           (TyFunc TyNat -- (Var (There (There (There (There Here))))) -
                                                   (TyFunc TyNat -- (Var (There (There (There (There (There Here)))))) -
                                                           (TyFunc TyNat -- (Var (There (There (There (There (There (There Here)))))))
                                                                   TyNat
                                                                   )
                                                                   )
                                                                   )
                                                                   )
                                                                   )
                                                                   )
                                                                   )
term =
  Func
      (Func
           (Func
                (Func
                     (Func
                          (Func
                               (Func
                                    (Func (Plus (Var Here)
                                                (Plus (Var (There Here))
                                                      (Plus (Var (There (There Here)))
                                                            (Plus (Var (There (There (There Here))))
                                                                  (Plus (Var (There (There (There (There Here)))))
                                                                        (Plus  (Var (There (There (There (There (There Here))))))
                                                                               (Var (There (There (There (There (There (There Here)))))))
                                                                        )
                                                                  )
                                                            )
                                                      )
                                                 )
                                          )
                                    )
                               )
                          )
                     )
                )
            )
       )


%default total

--changing codata to dat would make "ones" non-total
codata Stream' : Type -> Type where
  (::) : (e : a) -> Stream' a -> Stream' a

ones : Stream' Nat
ones = 1 :: ones


module Hoare

-- Based on http://eprints.nottingham.ac.uk/10779/1/Thesis.pdf

Pre : Type -> Type
Pre s = s -> Type

Post : Type -> Type -> Type
Post s a = s -> a -> s -> Type

HoareState : (s : Type) -> Pre s -> (a : Type) -> Post s a -> Type
HoareState s pre a post = (d : DPair s pre) -> DPair (a, s) (\(x, f) => post (fst d) x f)

return : {a : Type} -> (x : a) -> HoareState s (\_ => ()) a (\i, y, f => (i = f, x = y))
return x (st ** ()) = ((x, st) ** (Refl, Refl))

(>>=) : {p1 : Pre s} -> {q1 : Post s a} -> {p2 : a -> Pre s} -> {q2 : a -> Post s b} ->
        HoareState s p1 a q1 ->
        ((x : a) -> HoareState s (p2 x) b (q2 x)) ->
        HoareState s (\s1 => (p1 s1, ((x : a) -> (s2 : s) -> q1 s1 x s2 -> p2 x s2))) b
                     (\s1, x, s3 => DPair (a, s) (\(y,s2) => (q1 s1 y s2, q2 y s2 x s3)))
(>>=) c1 c2 (s1 ** (s1p1, f)) = let ((x, s2) ** p) = (c1 (s1 ** s1p1)) in
                                let ((y, s3) ** q) = c2 x (s2 ** (f x s2 p)) in
                                ((y, s3) ** ((x, s2) ** (p, q)))

get : HoareState s (\_ => ()) s (\i, x, f => (i = f, x = i))
get (st ** ()) = ((st, st) ** (Refl, Refl))

put : (st : s) -> HoareState s (\_ => ()) () (\i, x, f => f = st)
put st _ = (((), st) ** Refl)


{-
0.42
1.68
1.34
0.34
-}
module Main

main : IO ()
main =
  traverse_
    (\op => printLn $ 0.84 `op` 0.5)
    [(*), (/), (+), (-)]


module Solutions.Folds

import Data.Maybe
import Data.SnocList
import Data.Vect

%default total

--------------------------------------------------------------------------------
--          Recursion
--------------------------------------------------------------------------------

-- 1

anyList : (a -> Bool) -> List a -> Bool
anyList p []        = False
anyList p (x :: xs) = case p x of
  False => anyList p xs
  True  => True

allList : (a -> Bool) -> List a -> Bool
allList p []        = True
allList p (x :: xs) = case p x of
  True  => allList p xs
  False => False

-- 2

findList : (a -> Bool) -> List a -> Maybe a
findList f []        = Nothing
findList f (x :: xs) = if f x then Just x else findList f xs

-- 3

collectList : (a -> Maybe b) -> List a -> Maybe b
collectList f []        = Nothing
collectList f (x :: xs) = case f x of
  Just vb => Just vb
  Nothing => collectList f xs

-- Note utility function `Data.Maybe.toMaybe` in the implementation
lookupList : Eq a => a -> List (a,b) -> Maybe b
lookupList va = collectList (\(k,v) => toMaybe (k == va) v)

-- 4

mapTR : (a -> b) -> List a -> List b
mapTR f = go Lin
  where go : SnocList b -> List a -> List b
        go sx []        = sx <>> Nil
        go sx (x :: xs) = go (sx :< f x) xs

-- 5

filterTR : (a -> Bool) -> List a -> List a
filterTR f = go Lin
  where go : SnocList a -> List a -> List a
        go sx []        = sx <>> Nil
        go sx (x :: xs) = if f x then go (sx :< x) xs else go sx xs

-- 6

mapMaybeTR : (a -> Maybe b) -> List a -> List b
mapMaybeTR f = go Lin
  where go : SnocList b -> List a -> List b
        go sx []        = sx <>> Nil
        go sx (x :: xs) = case f x of
          Just vb => go (sx :< vb) xs
          Nothing => go sx xs

catMaybesTR : List (Maybe a) -> List a
catMaybesTR = mapMaybeTR id

-- 7

concatTR : List a -> List a -> List a
concatTR xs ys = (Lin <>< xs) <>> ys

-- 8

bindTR : List a -> (a -> List b) -> List b
bindTR xs f = go Lin xs
  where go : SnocList b -> List a -> List b
        go sx []        = sx <>> Nil
        go sx (x :: xs) = go (sx <>< f x) xs

joinTR : List (List a) -> List a
joinTR = go Lin
  where go : SnocList a -> List (List a) -> List a
        go sx []        = sx <>> Nil
        go sx (x :: xs) = go (sx <>< x) xs

--------------------------------------------------------------------------------
--          A few Notes on Totality Checking
--------------------------------------------------------------------------------

record Tree a where
  constructor Node
  value  : a
  forest : List (Tree a)

Forest : Type -> Type
Forest = List . Tree

example : Tree Bits8
example = Node 0 [Node 1 [], Node 2 [Node 3 [], Node 4 [Node 5 []]]]

mutual
  treeSize : Tree a -> Nat
  treeSize (Node _ forest) = S $ forestSize forest

  forestSize : Forest a -> Nat
  forestSize []        = 0
  forestSize (x :: xs) = treeSize x + forestSize xs

-- 1

mutual
  treeDepth : Tree a -> Nat
  treeDepth (Node _ forest) = S $ forestDepth forest

  forestDepth : Forest a -> Nat
  forestDepth []        = 0
  forestDepth (x :: xs) = max (treeDepth x) (forestDepth xs)

-- 2

-- It's often easier to write complex interface implementations
-- via a utility function.
--
-- Of course, we could also use a `mutual` block as with
-- `treeSize` and `forestSize` here.
treeEq : Eq a => Tree a -> Tree a -> Bool
treeEq (Node v1 f1) (Node v2 f2) = v1 == v2 && go f1 f2
  where go : Forest a -> Forest a -> Bool
        go []        []        = True
        go (x :: xs) (y :: ys) = treeEq x y && go xs ys
        go _         _         = False

Eq a => Eq (Tree a) where (==) = treeEq

-- 3

treeMap : (a -> b) -> Tree a -> Tree b
treeMap f (Node value forest) = Node (f value) (go forest)
  where go : Forest a -> Forest b
        go []        = []
        go (x :: xs) = treeMap f x :: go xs

Functor Tree where map = treeMap

-- 4

treeShow : Show a => Prec -> Tree a -> String
treeShow p (Node value forest) =
  showCon p "Node" $ showArg value ++ case forest of
    []      => " []"
    x :: xs => " [" ++ treeShow Open x ++ go xs ++ "]"

  where go : Forest a -> String
        go []        = ""
        go (y :: ys) = ", " ++ treeShow Open y ++ go ys

Show a => Show (Tree a) where showPrec = treeShow

-- 5

mutual
  treeToVect : (tr : Tree a) -> Vect (treeSize tr) a
  treeToVect (Node value forest) = value :: forestToVect forest

  forestToVect : (f : Forest a) -> Vect (forestSize f) a
  forestToVect []        = []
  forestToVect (x :: xs) = treeToVect x ++ forestToVect xs

--------------------------------------------------------------------------------
--          Interface Foldable
--------------------------------------------------------------------------------

-- 1

data Crud : (i : Type) -> (a : Type) -> Type where
  Create : (value : a) -> Crud i a
  Update : (id : i) -> (value : a) -> Crud i a
  Read   : (id : i) -> Crud i a
  Delete : (id : i) -> Crud i a

Foldable (Crud i) where
  foldr acc st (Create value)   = acc value st
  foldr acc st (Update _ value) = acc value st
  foldr _   st (Read _)         = st
  foldr _   st (Delete _)       = st

  foldl acc st (Create value)   = acc st value
  foldl acc st (Update _ value) = acc st value
  foldl _   st (Read _)         = st
  foldl _   st (Delete _)       = st

  null (Create _)   = False
  null (Update _ _) = False
  null (Read _)     = True
  null (Delete _)   = True

  foldMap f (Create value)   = f value
  foldMap f (Update _ value) = f value
  foldMap _ (Read _)         = neutral
  foldMap _ (Delete _)       = neutral

  foldlM acc st (Create value)   = acc st value
  foldlM acc st (Update _ value) = acc st value
  foldlM _   st (Read _)         = pure st
  foldlM _   st (Delete _)       = pure st

  toList (Create v)   = [v]
  toList (Update _ v) = [v]
  toList (Read _)     = []
  toList (Delete _)   = []

-- 2

data Response : (e, i, a : Type) -> Type where
  Created : (id : i) -> (value : a) -> Response e i a
  Updated : (id : i) -> (value : a) -> Response e i a
  Found   : (values : List a) -> Response e i a
  Deleted : (id : i) -> Response e i a
  Error   : (err : e) -> Response e i a

Foldable (Response e i) where
  foldr acc st (Created _ value) = acc value st
  foldr acc st (Updated _ value) = acc value st
  foldr acc st (Found values)    = foldr acc st values
  foldr _   st (Deleted _)       = st
  foldr _   st (Error _)         = st

  foldl acc st (Created _ value) = acc st value
  foldl acc st (Updated _ value) = acc st value
  foldl acc st (Found values)    = foldl acc st values
  foldl _   st (Deleted _)       = st
  foldl _   st (Error _)         = st

  null (Created _ _)     = False
  null (Updated _ _)     = False
  null (Found values)    = null values
  null (Deleted _)       = True
  null (Error _)         = True

  foldMap f (Created _ value) = f value
  foldMap f (Updated _ value) = f value
  foldMap f (Found values)    = foldMap f values
  foldMap f (Deleted _)       = neutral
  foldMap f (Error _)         = neutral

  toList (Created _ value) = [value]
  toList (Updated _ value) = [value]
  toList (Found values)    = values
  toList (Deleted _)       = []
  toList (Error _)         = []

  foldlM acc st (Created _ value) = acc st value
  foldlM acc st (Updated _ value) = acc st value
  foldlM acc st (Found values)    = foldlM acc st values
  foldlM _   st (Deleted _)       = pure st
  foldlM _   st (Error _)         = pure st

-- 3

data List01 : (nonEmpty : Bool) -> Type -> Type where
  Nil  : List01 False a
  (::) : a -> List01 False a -> List01 ne a

list01ToList : List01 ne a -> List a
list01ToList = go Lin
  where go : SnocList a -> List01 ne' a -> List a
        go sx []        = sx <>> Nil
        go sx (x :: xs) = go (sx :< x) xs

list01FoldMap : Monoid m => (a -> m) -> List01 ne a -> m
list01FoldMap f = go neutral
  where go : m -> List01 ne' a -> m
        go vm []        = vm
        go vm (x :: xs) = go (vm <+> f x) xs

Foldable (List01 ne) where
  foldr acc st []        = st
  foldr acc st (x :: xs) = acc x (foldr acc st xs)

  foldl acc st []        = st
  foldl acc st (x :: xs) = foldl acc (acc st x) xs

  null []       = True
  null (_ :: _) = False

  toList = list01ToList

  foldMap = list01FoldMap

  foldlM _ st []        = pure st
  foldlM f st (x :: xs) = f st x >>= \st' => foldlM f st' xs

-- 4

mutual
  foldrTree : (el -> st -> st) -> st -> Tree el -> st
  foldrTree f v (Node value forest) = f value (foldrForest f v forest)

  foldrForest : (el -> st -> st) -> st -> Forest el -> st
  foldrForest _ v []        = v
  foldrForest f v (x :: xs) = foldrTree f (foldrForest f v xs) x

mutual
  foldlTree : (st -> el -> st) -> st -> Tree el -> st
  foldlTree f v (Node value forest) = foldlForest f (f v value) forest

  foldlForest : (st -> el -> st) -> st -> Forest el -> st
  foldlForest _ v []        = v
  foldlForest f v (x :: xs) = foldlForest f (foldlTree f v x) xs

mutual
  foldMapTree : Monoid m => (el -> m) -> Tree el -> m
  foldMapTree f (Node value forest) = f value <+> foldMapForest f forest

  foldMapForest : Monoid m => (el -> m) -> Forest el -> m
  foldMapForest _ []        = neutral
  foldMapForest f (x :: xs) = foldMapTree f x <+> foldMapForest f xs

mutual
  toListTree : Tree el -> List el
  toListTree (Node value forest) = value :: toListForest forest

  toListForest : Forest el -> List el
  toListForest []        = []
  toListForest (x :: xs) = toListTree x ++ toListForest xs

mutual
  foldlMTree : Monad m => (st -> el -> m st) -> st -> Tree el -> m st
  foldlMTree f v (Node value forest) =
    f v value >>= \v' => foldlMForest f v' forest

  foldlMForest : Monad m => (st -> el -> m st) -> st -> Forest el -> m st
  foldlMForest _ v []        = pure v
  foldlMForest f v (x :: xs) =
    foldlMTree f v x >>= \v' => foldlMForest f v' xs

Foldable Tree where
  foldr   = foldrTree
  foldl   = foldlTree
  foldMap = foldMapTree
  foldlM  = foldlMTree
  null _  = False
  toList  = toListTree

-- 5

record Comp (f,g : Type -> Type) (a : Type) where
  constructor MkComp
  unComp  : f (g a)

Foldable f => Foldable g => Foldable (Comp f g) where
  foldr f st (MkComp v)  = foldr (flip $ foldr f) st v
  foldl f st (MkComp v)  = foldl (foldl f) st v
  foldMap f (MkComp v)   = foldMap (foldMap f) v
  foldlM f st (MkComp v) = foldlM (foldlM f) st v
  toList (MkComp v)      = foldMap toList v
  null (MkComp v)        = all null v

record Product (f,g : Type -> Type) (a : Type) where
  constructor MkProduct
  fst : f a
  snd : g a

Foldable f => Foldable g => Foldable (Product f g) where
  foldr f st (MkProduct v w)  = foldr f (foldr f st w) v
  foldl f st (MkProduct v w)  = foldl f (foldl f st v) w
  foldMap f (MkProduct v w)   = foldMap f v <+> foldMap f w
  toList  (MkProduct v w)     = toList v ++ toList w
  null (MkProduct v w)        = null v && null w
  foldlM f st (MkProduct v w) = foldlM f st v >>= \st' => foldlM f st' w

--------------------------------------------------------------------------------
--          Tests
--------------------------------------------------------------------------------

iterateTR : Nat -> (a -> a) -> a -> List a
iterateTR k f = go k Lin
  where go : Nat -> SnocList a -> a -> List a
        go 0     sx _ = sx <>> Nil
        go (S k) sx x = go k (sx :< x) (f x)

values : List Integer
values = iterateTR 100000 (+1) 0

main : IO ()
main = do
  printLn . length $ mapTR (*2)  values
  printLn . length $ filterTR (\n => n `mod` 2 == 0)  values
  printLn . length $ mapMaybeTR (\n => toMaybe (n `mod` 2 == 1) "foo")  values
  printLn . length $ concatTR values values
  printLn . length $ bindTR [1..500] (\n => iterateTR n (+1) n)


module Main

import Effekt.CpsStaged
import Effekt.IteratedUnstaged
import Effekt.IteratedStaged
import Effekt.AvoidEta

fail : STM (String :: rs) a
fail = shift0 (\k => do
  pure (Str "no"))

flip : STM (r :: rs) Bool
flip = shift0 (\k => do
  resume k Tru
  resume k Fls)

delimitFail : STM rs String
delimitFail {rs} = do
  a <- reset0 fail
  pure {rs} ((Cat (Str "Answer was: ") a))

emit : Exp a -> STM (List a :: rs) ()
emit a = shift0 (\k => do
  as <- resume k Uni
  pure ((Con a as)))

collect : STM (List a :: rs) b -> STM rs (List a)
collect {rs} {a} m = reset0 {a=List a} {rs} (
  (>>=) {rs=List a :: rs} m (\u => pure {rs=List a :: rs} Emp))

choice : Exp Int -> STM (String :: rs) Int
choice {rs} = letrec (\recurse => \n => do
  ifthenelse ((Sma n (Lit 1)))
    fail
    (do
      b <- flip
      ifthenelse b
        (pure {rs=String::rs} n)
        (recurse ((Sub n (Lit 1))))))

triple : Exp Int -> Exp Int -> STM (String :: rs) (Int, Int, Int)
triple {rs} n s = do
  i  <- choice n
  j  <- choice ((Sub i (Lit 1)))
  k  <- choice ((Sub j (Lit 1)))
  ifthenelse ((Equ ((Add i ((Add j k)))) s))
    (pure {rs=String::rs} (Tri i j k))
    fail

emitTriples : STM (String :: List (Int, Int, Int) :: rs) String
emitTriples {rs} =
  (>>=) {rs=(String :: List (Int, Int, Int) :: rs)} (triple (Lit 9) (Lit 15))  (\ijk =>
  (>>=) {rs=(String :: List (Int, Int, Int) :: rs)} (lift (emit ijk)) (\u =>
  pure {rs=(String :: List (Int, Int, Int) :: rs)} (Str "done")))

emittedTriples : STM [] (List (Int, Int, Int))
emittedTriples = collect {rs=[]} (reset0 (emitTriples {rs=[]}))

first : Exp a -> STM (Maybe a :: rs) ()
first {rs} a = shift0 (\k => do
  pure {rs} ((Jus a)))

firstTriples : STM (String :: Maybe (Int, Int, Int) :: rs) String
firstTriples {rs} =
  (>>=) {rs=(String :: Maybe (Int, Int, Int) :: rs)} (triple (Lit 9) (Lit 15))  (\ijk =>
  (>>=) {rs=(String :: Maybe (Int, Int, Int) :: rs)} (lift (first ijk)) (\u =>
  pure {rs=(String :: Maybe (Int, Int, Int) :: rs)} (Str "done")))
firstTriple : STM [] (Maybe (Int, Int, Int))
firstTriple = reset0 {a=Maybe (Int,Int,Int)} {rs=[]} (
  (>>=) {rs=[Maybe (Int,Int,Int)]} (reset0 (firstTriples {rs=[]})) (\u =>
  pure {rs=[Maybe (Int,Int,Int)]} Noh))

program : Exp (Stm [String,List Int] String -> Stm [] (List Int))
program = Lam (\m =>
  reify (collect {rs=[]} {a=Int} {b=String} (reset0 {rs=[List Int]} (
    reflect {a=String} {rs=[String,List Int]} m))))

main : IO ()
main = putStrLn (pretty 0 emittedTriples)

  -- generated_source : List (Int, Int, Int)
  -- generated_source =
  -- ((((let f0 x1 = (\x2 => (\x3 =>
  --    (if (x1 < 1)
  --      then (x3 "no")
  --      else ((x2 x1) (\x4 => (((f0 (x1 - 1)) x2) x3)))))) in f0) 9) (\x0 => (\x1 =>
  -- ((((let f2 x3 = (\x4 => (\x5 =>
  --    (if (x3 < 1)
  --      then (x5 "no")
  --      else ((x4 x3) (\x6 => (((f2 (x3 - 1)) x4) x5)))))) in f2) (x0 - 1)) (\x2 => (\x3 =>
  -- ((((let f4 x5 = (\x6 => (\x7 =>
  --    (if (x5 < 1)
  --      then (x7 "no")
  --      else ((x6 x5) (\x8 => (((f4 (x5 - 1)) x6) x7)))))) in f4) (x2 - 1)) (\x4 => (\x5 =>
  --        (if ((x0 + (x2 + x4)) == 15)
  --          then ((x0, x2, x4) :: (x5 "done"))
  --          else (x5 "no"))))) x3)))) x1)))) (\x0 => []))