module Ex3 where  -- Conor: 5.5/15 (marked in sem 1 for 3.1-3.4)
                  -- really 5.5/7 then another 3/8 in sem 2, giving 8.5/15

----------------------------------------------------------------------------
-- EXERCISE 3 -- MONADS FOR HUTTON'S RAZOR
--
-- VALUE:     15%
-- DEADLINE:  5pm, Friday 20 November (week 9)
--
-- DON'T SUBMIT, COMMIT!
--
-- The purpose of this exercise is to introduce you to some useful
-- mathematical structures and build good tools for working with
-- vectors
----------------------------------------------------------------------------

open import CS410-Prelude
open import CS410-Monoid
open import CS410-Nat
open import CS410-Vec
open import CS410-Functor

-- HINT: your tasks are heralded with the eminently searchable tag, "???"

----------------------------------------------------------------------------
-- HUTTON'S RAZOR
----------------------------------------------------------------------------

HVal : Set   -- the set of *values* for Hutton's Razor
HVal = Two + Nat   -- Booleans or natural numbers

data HExp (X : Set) : Set where
  var        : X -> HExp X                  -- variables
  val        : HVal -> HExp X               -- values
  _+H_ _>=H_ : (e1 e2 : HExp X) -> HExp X   -- addition, comparison
  ifH_then_else_ : (e1 e2 e3 : HExp X) -> HExp X  -- conditional

_>=2_ : Nat -> Nat -> Two
x      >=2  zero   = tt
zero   >=2  suc _  = ff
suc m  >=2  suc n  = m >=2 n

----------------------------------------------------------------------------
-- ??? 3.1 the HExp syntax-with-substitution monad            (score: 2 / 2)
----------------------------------------------------------------------------

-- Show that HExp is a monad, where the "bind" operation performs
-- simultaneous substitution (transforming all the variables in a term).

hExpMonad : Monad HExp
hExpMonad = record { return = λ x → var x
                   ; _>>=_ = λ x f → hExpMonad>>= x f
                   ; law1 = λ x f → refl
                   ; law2 = λ t → hExpMonadLaw2 t
                   ; law3 = λ f g t → hExpMonadLaw3 t g f
                   } where

  hExpMonad>>= : {X Y : Set} → HExp X → (X → HExp Y) → HExp Y
  hExpMonad>>= (var x) f = f x
  hExpMonad>>= (val x) f = val x
  hExpMonad>>= (x +H y) f = hExpMonad>>= x f +H hExpMonad>>= y f
  hExpMonad>>= (x >=H y) f = hExpMonad>>= x f >=H  hExpMonad>>= y f
  hExpMonad>>= (ifH x then y else z) f = ifH hExpMonad>>= x f then
               hExpMonad>>= y f else hExpMonad>>= z f

  hExpMonadLaw2 : {X : Set} → (t : HExp X) →  hExpMonad>>= t (λ x → var x) == t
  hExpMonadLaw2 (var x) = refl
  hExpMonadLaw2 (val x) = refl
  hExpMonadLaw2 (x +H y) rewrite hExpMonadLaw2 x | hExpMonadLaw2 y  = refl
  hExpMonadLaw2 (x >=H y) rewrite hExpMonadLaw2 x | hExpMonadLaw2 y  = refl
  hExpMonadLaw2 (ifH x then y else z) rewrite hExpMonadLaw2 x | hExpMonadLaw2 y | hExpMonadLaw2 z = refl

  hExpMonadLaw3 : {X Y Z : Set} →  (t  : HExp X) → (g  : Y → HExp Z) → (f  : X → HExp Y) → hExpMonad>>= (hExpMonad>>= t f) g == hExpMonad>>= t (λ x → hExpMonad>>= (f x) g)
  hExpMonadLaw3 (var x) g t = refl
  hExpMonadLaw3 (val x) g t = refl
  hExpMonadLaw3 (f +H f₁) g t rewrite hExpMonadLaw3 f g t | hExpMonadLaw3 f₁ g t = refl
  hExpMonadLaw3 (f >=H f₁) g t rewrite hExpMonadLaw3 f g t | hExpMonadLaw3 f₁ g t = refl
  hExpMonadLaw3 (ifH f then f₁ else f₂) g t rewrite hExpMonadLaw3 f g t | hExpMonadLaw3 f₁ g t | hExpMonadLaw3 f₂ g t = refl

----------------------------------------------------------------------------
-- ??? 3.2 the error management monad                         (score: 1 / 1)
----------------------------------------------------------------------------

-- show that "+ E" is monadic, generalising the "Maybe" monad by allowing
-- some sort of error report

errorMonad : (E : Set) -> Monad \ V -> V + E   -- "value or error"
errorMonad E = record { return = λ x → tt , x
                      ; _>>=_ = errorMonad>>=
                      ; law1 = λ x f → refl
                      ; law2 = errorMonadLaw2
                      ; law3 = errorMonadLaw3
                      } where

  errorMonad>>= : {X Y : Set} → X + E → (X → Y + E) → Y + E
  errorMonad>>= (tt , snd) y = y snd
  errorMonad>>= (ff , snd) y = ff , snd

  errorMonadLaw2 : {X : Set} (t : X + E) → errorMonad>>= t (λ x → tt , x) == t
  errorMonadLaw2 (tt , snd) = refl
  errorMonadLaw2 (ff , snd) = refl

  errorMonadLaw3 :  {X Y Z : Set} (f : X → Y + E) (g : Y → Z + E) (t : X + E) →
                     errorMonad>>= (errorMonad>>= t f) g == errorMonad>>= t
                    (λ x → errorMonad>>= (f x) g)
  errorMonadLaw3 f g (tt , snd) = refl
  errorMonadLaw3 f g (ff , snd) = refl

----------------------------------------------------------------------------
-- ??? 3.3 the environment monad transformer                   (score: 1 / 1)
----------------------------------------------------------------------------

-- show that any monad can be adapted to thread some environment information
-- as well as whatever else it already managed

envMonad : (G : Set){M : Set -> Set} -> Monad M ->
           Monad \ V -> G -> M V      -- "computation in an environment"
envMonad G {M} MM = record { return = λ {X} z _ → Monad.return MM z
                       ; _>>=_ = λ {X} {Y} z z₁ z₂ → (MM Monad.>>= z z₂)
                                   (λ z₃ → z₁ z₃ z₂)
                       ; law1 = λ {X Y} x f → envMonadLaw1 f x
                       ; law2 = envMonadLaw2
                       ; law3 = envMonadLaw3
                       } where
  open Monad MM

  envMonadLaw1 : {X Y : Set} (f  : X → G → M Y) -> (x  : X) ->
                 (λ z₂ → return x >>= (λ z₃ → f z₃ z₂)) == f x
  envMonadLaw1 f x = ext (λ g → Monad.law1 MM x (λ z → f z g))

  envMonadLaw2 : {X : Set} (t : G → M X) →
                 (λ z₂ → t z₂ >>= (λ z₃ → return z₃)) == t
  envMonadLaw2 x = ext (λ a → Monad.law2 MM (x a))

  envMonadLaw3 : {X Y Z : Set} (f : X → G → M Y) (g : Y → G → M Z) (t : G → M X) →(λ z₂ → (t z₂ >>= (λ z₃ → f z₃ z₂)) >>= (λ z₃ → g z₃ z₂)) == (λ z₂ → t z₂ >>= (λ z₃ → f z₃ z₂ >>= (λ z₄ → g z₄ z₂)))
  envMonadLaw3 f g t = ext (λ a → Monad.law3 MM (λ z → f z a) (λ z → g z a) (t a))
----------------------------------------------------------------------------
-- ??? 3.4 interpreting Hutton's Razor                        (score: 1.5 / 3)
----------------------------------------------------------------------------

-- Implement an interpreter for Hutton's Razor.
-- You will need to construct a type to represent possible run-time errors.
-- Ensure that addition and comparison act on numbers, not Booleans.
-- Ensure that the condition in an "if" is a Boolean, not a number.

data InterpretError : Set where
  NotTwo               : InterpretError
  NotNat               : InterpretError

-- helpful things to build

Env : Set -> Set    -- an environment for a given set of variables
Env X = X -> HVal

Compute : Set{- variables -} -> Set{- values -} -> Set
Compute X V = Env X -> V + InterpretError  -- how to compute a V

computeMonad : {Z : Set} -> Monad (Compute Z)
computeMonad {Z} = envMonad (Env Z) (errorMonad InterpretError)

 -- build this from the above parts

-- This operation should explain how to get the value of a variable
-- from the environment.
varVal : {X : Set} -> X -> Compute X HVal
varVal x y = tt , (y x)
--  tt , (y x)

-- These operations should ensure that you get the sort of value
-- that you want, in order to ensure that you don't do bogus
-- computation.
mustBeNat : {X : Set} -> HVal -> Compute X Nat
mustBeNat (tt , tt) x = ff , NotNat        -- Conor: more case analysis than needed
mustBeNat (tt , ff) x = ff , NotNat
mustBeNat (ff , zero) x = tt , zero        -- Conor: more case analysis than needed
mustBeNat (ff , suc snd) x = tt , suc snd

mustBeTwo : {X : Set} -> HVal -> Compute X Two
mustBeTwo (tt , tt) x = tt , tt            -- Conor: more case analysis than needed
mustBeTwo (tt , ff) x = tt , ff
mustBeTwo (ff , zero) x = ff , NotTwo     -- Conor: more case analysis than needed
mustBeTwo (ff , suc snd) x = ff , NotTwo

-- Now, you're ready to go. Don't introduce the environment explicitly.
-- Use the monad to thread it.

interpret : {X : Set} -> HExp X -> Compute X HVal
interpret {X} = go where
  open Monad (computeMonad {X})
  go : HExp X -> Compute X HVal
  -- Conor: you already have the right kit, so it's counterproductive to go down to this detail
  go (var x) E = tt , (E x)
  -- Conor: go (var x) = varVal x
  go (val x) E = tt , x
  go (t +H t₁) E = {!!}
  -- Conor: go (l +H r) =
  --   go l >>= \ lv ->               -- lv : HVal
  --   mustBeNat lv >>= \ ln ->       -- ln : Nat
  --   go r >>= \ rv ->               -- rv : HVal
  --   mustBeNat rv >>= \ rn ->       -- rn : Nat
  --   return (ln +N rn)
  go (t >=H t₁) E = {!!}
  go (ifH t then t₁ else t₂) E = {!!}
----------------------------------------------------------------------------
-- ??? 3.5 Typed Hutton's Razor                               (score: 0.5 / 1)
----------------------------------------------------------------------------

-- Labelling the expressions with their types gives strong guarantees
-- sooner (we can make it grammatically incorrect to add a bool to a
-- nat) and makes some things easier as (if we're adding them then we
-- know they are nats).

-- some names for the types we will use in our typed syntax.

data HType : Set where hTwo hNat : HType

-- mapping names for types to real types.

THVal : HType -> Set
THVal hTwo = Two
THVal hNat = Nat

-- A syntax for types expressions, indexed by typed variables. Compare
-- with the untyped HExp and fill in the missing expression formers,
-- we have shown you the way with _+H_. think: what can be guaranteed?

data THExp (X : HType -> Set) : HType -> Set where
  var : forall {T} -> X T -> THExp X T
  val : forall {T} -> THVal T -> THExp X T
  _+H_ : THExp X hNat -> THExp X hNat -> THExp X hNat
  _>=H_ :  THExp X hNat -> THExp X  hTwo {- Conor: hNat here! -} -> THExp X hTwo
  ifH_then_else_ : forall {T} -> THExp X hTwo -> THExp X T -> THExp X T -> THExp X T

  -- ??? fill in the other two constructs, typed appropriately
  -- (remember that "if then else" can compute values at any type)

----------------------------------------------------------------------------
-- ??? 3.6 Well Typed Programs Don't Go Wrong                 (score: 0 / 1)
----------------------------------------------------------------------------

-- notation for functions betweeen indexed sets (e.g. indexed by types)

_-:>_ : {I : Set}(S T : I -> Set) -> I -> Set
(S -:> T) i = S i -> T i
infixr 3 _-:>_

-- notation for indexed sets

[_] : {I : Set}(X : I -> Set) -> Set
[ X ] = forall {i} -> X i

-- We can put the two together to make types like
--    [ S -:> T ]
--  = forall {i} -> S i -> T i
-- which is the type of functions which work at any index
-- and respect that index. That'll be very useful in just a moment.

-- An evaluator for typed terms, it takes an environment for
-- variables (a function mapping variables to values) and a expression
-- and returns a value). Written as below it looks like it lifts a
-- function from variables to values to a function from terms to
-- values.

eval : {X : HType -> Set} -> [ X -:> THVal ] -> [ THExp X -:> THVal ]
eval g (var x) = g x
eval g (val x) = x
eval g (t +H t1) = eval g t +N eval g t
eval g (t >=H t1) = eval g t >=2 eval g t
eval g (ifH t then t1 else t2) = if eval g t then eval g t1 else eval g t1
  -- Conor: all three step cases get the wrong answers

-- Note that the environment is an *index-respecting* function from
-- variables to values. The index is the type of the variable: you're
-- making sure that when you look up a variable of a given type, you
-- get a value of that type. As a result, you can deliver a *type-safe*
-- evaluator: when an expression has a given type, its value must have
-- that type.

----------------------------------------------------------------------------
-- ??? 3.7 Variable Contexts                                  (score: 1 / 1)
----------------------------------------------------------------------------

-- backwards lists.

data Bwd (X : Set) : Set where
  []   : Bwd X
  _/_  : Bwd X -> X -> Bwd X

-- Our datatype for type indexed expressions is very liberal about
-- variables, they can be any set indexed by types. Here we build
-- something more structured, that nevertheless satisfies the specification

-- We will not use names for variables only numbers.

-- Hence, a context is just a list of types.

Context : Set
Context = Bwd HType

-- Well scoped and well typed variables, top = 0, pop = suc.
-- top is the variable on the right hand end of a non-empty context.
-- pop takes a variable and extends puts it into a longer context.

data Var : (G : Context)(T : HType) -> Set where
  top : {G : Context}{T : HType} -> Var (G / T) T
  pop : {G : Context}{T S : HType} -> Var G T -> Var (G / S) T

-- We can also represent environments as stacks, as opposed to functions.
-- You can read a variable as the sequence of instructions for extracting
-- a value from a stack: you keep popping stuff off until the value you
-- want is the the one at the top.

Stack : Context -> Set
Stack [] = One
Stack (G / S) = Stack G * THVal S

-- Looking up a value for a variable in an an environment or fetching
-- something from a stack given a sequence of pop and top
-- instructions. It's all the same to us!

fetch : {G : Context} -> Stack G -> [ Var G -:> THVal ]
fetch g top = snd g
fetch g (pop v) = fetch (fst g) v

-- An evaluator for expression with more structured variables. We
-- already know how to evaluate, we just have to explain how to deal
-- with manage the different style of environment.

evalStack : {G : Context}{T : HType} ->
            Stack G -> [ THExp (Var G) -:> THVal ]
evalStack g = eval (fetch g)

----------------------------------------------------------------------------
-- ??? 3.8 Terms-With-One-Hole                                (score: 1 / 1)
----------------------------------------------------------------------------

-- Next, we build some kit that we'll use to present type errors.

-- Here we represent an expression with a bit missing. Addition can have
-- have a bit missing (a hole) on the right or on the left. What about
-- the other expression formers?

data HExp' (X : Set) : Set where
  []+H_ _+H[] : HExp X -> HExp' X
  []>=H_ _>=H[] :  HExp X -> HExp' X
  ifH[]then_else_ ifH_then[]else_ ifH_then_else[] : HExp X -> HExp X -> HExp' X

  -- ??? more constructors here
  -- specifically, you will need a constructor for each way that a
  -- subexpression can fit inside an expression;
  -- we use the naming convention of showing where the "hole" is
  -- by putting [] in the corresponding place in the name.

-- take a expression with a hole, and a expression to plug in and plug
-- it in!
_[]<-_ : forall {X} -> HExp' X -> HExp X -> HExp X
([]+H r) []<- t = t +H r
(l +H[]) []<- t = l +H t
([]>=H r) []<- t = t >=H r
(l >=H[]) []<- t = l >=H t
(ifH[]then l else r) []<- t = ifH t then l else r
(ifH e then[]else r) []<- t = ifH e then t else r
(ifH e then l else[]) []<- t = ifH e then l else t
-- ??? more cases here

{-
data List (X : Set) : Set where  -- X scopes over the whole declaration...
  []    : List X                 -- ...so you can use it here...
  _::_  : X -> List X -> List X  -- ...and here.
infixr 3 _::_
-}
-- As we descend down into a term we can keep the pieces we pass along
-- the way in a list, this is a zipper. For example, given the
-- expression 3 + (4 + 5) we could go down by going right and right
-- again to reach 5. In our list we would have [ 4 + [] , 3 + [] ].

-- we need an operation that takes us back up to the root of the tree,
-- restoring the expression to its former state (e.g. 3 + (4 + 5)).

rootToHole : forall {X} -> List (HExp' X) -> HExp X -> HExp X
rootToHole [] t = t
rootToHole (t' :: t's) t = t' []<- rootToHole t's t

-- The idea is that the pair of a List (HExp' X) and a single
-- HExp X together represent a term with a designated subterm "in focus".
-- The list of one-hole terms represents the *path* to the designated
-- subterm, together with the *other stuff* hanging off to either side
-- of that path.

----------------------------------------------------------------------------
-- ??? 3.9 Forgetting Types                                   (score: 0.5 / 1)
----------------------------------------------------------------------------

-- SUSPICION: why would we want to?

-- Given a typed term (THEXP X T) we can forget the types to obtain an
-- untyped term (HExp Y) if we know how to forget types from variables
-- (varFog).

termFog : {X : HType -> Set}{Y : Set}(varFog : {T : HType} -> X T -> Y) ->
          {T : HType} -> THExp X T -> HExp Y
termFog vF (var x) = var (vF x)
termFog vF (val x) = {!x!}  -- Conor: need to find the type
termFog vF (t +H t1) = (termFog vF t) +H (termFog vF t1)
termFog vF (t >=H t1) = (termFog vF t) >=H (termFog vF t1)
termFog vF (ifH t then t1 else t2) = ifH (termFog vF t) then (termFog vF t1) else (termFog vF t2)

-- Note that it's a local naming convention to call functions which
-- forget information "fog". When it is foggy, you can see less.

-- Our purpose in writing a function to throw away information is to
-- *specify* what it means to *obtain* information.

----------------------------------------------------------------------------
-- ??? 3.10 A Typechecking View                               (score: 0 / 3)
----------------------------------------------------------------------------

-- We finish by building a typechecker which will allow us to detect
-- when an untyped Hutton's Razor term can be converted into a typed
-- term (and evaluated safely without rechecking). We make use of
-- your solution to part 3.9 to express the idea that
--    an untyped term is the forgetful image of a typed term we know
-- and your solution to part 3.8 to express the idea that
--    an untyped term can be focused at a place where there is a type error

--  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --
-- But first, we need to build you a wee bit more kit. Typechecking relies
-- on checking that the type you want is the type you've got, which sometimes
-- means testing *equality* of types. It's not enough to have a function
--   htypeEq : HType -> HType -> Two
-- because we need to convince *Agda* of the equality, not just write a function
-- that happens to say yes to equal things.

-- a set with one element removed, i.e. X -[ x ] is the pair of some y in X and
-- a proof that y isn't x

_-[_] : (X : Set) -> X -> Set
X -[ x ] = Sg X \ y -> x == y -> Zero

-- a view for comparing types for equality

data HTypeComparable (T : HType) : HType -> Set where
  same : HTypeComparable T T
  diff : (SnT : HType -[ T ]) -> HTypeComparable T (fst SnT)

-- the above view type presents is two options, and in both of them, we
-- have to come through with enough evidence to convince Agda

-- implementing the view

hTypeCompare : (S T : HType) -> HTypeComparable S T
hTypeCompare hTwo hTwo = same
hTypeCompare hTwo hNat = diff (hNat , \ ())
hTypeCompare hNat hTwo = diff (hTwo , \ ())
hTypeCompare hNat hNat = same

-- we write the obvious four cases; in the "same" cases, the types really
-- do match; in the "diff" cases, Agda can rule out the equation hTwo == hNat
-- (or vice versa) because it knows the constructors of datatypes differ
--  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --

-- But back to our typechecker. To make things easier, we'll assume
-- that our supplier has already been kind enough to do *scope* checking,
-- so that all the variables written by the programmer have been looked
-- up and turned into typed references.

-- a reference: a pair of a type and a variable of that type.

Ref : Context -> Set
Ref G = Sg HType (Var G)

-- making a reference

ref : forall {G S} -> Var G S -> Ref G
ref {G}{S} v = S , v

-- ??? At last, your bit! Show that the following view type covers all
-- untyped terms:
--   either things go well and get the 'ok' and a well typed term
--   or something went wrong down in the expression tree somewhere,
--     so we can explain where that is.

data Checkable (G : Context)  -- the context of typed variables in scope
               (T : HType)    -- the type we expect
               :
               HExp (Ref G)   -- the untyped term we hope has type T
               -> Set where   -- one of two situations applies
  -- either
  ok    : (t : THExp (Var G) T)              -- we have a term of type T
          -> Checkable G T (termFog ref t)   -- and it's what we're checking
  -- or
  err   : (t's : List (HExp' (Ref G)))  -- there's some surroundings
          (s : HExp (Ref G))            -- and a subterm of interest
          -> Checkable G T (rootToHole t's s)  -- in what we're checking

check : (G : Context)(T : HType)(h : HExp (Ref G)) -> Checkable G T h
check G T h = {!!}

-- Now, this isn't quite the whole story, but it's pretty good. We've
-- guaranteed that
--   * if we say yes, it's because we've found the typed version
--     of the untyped input
--   * if we say no, we can point to a place where (we say that) there's a
--     problem
-- So we're *sound* (we never say yes to bad things), but not necessarily
-- *complete* (we can say no to good things). Nothing stops us reporting a
-- bogus type error at the subterm of our choosing! We could work harder
-- and define, in the same way as the typed terms, the "terrors", being
-- the terms containing at least one type error. The canny way to do that
-- is to try writing the typechecker, then grow the datatype that describes
-- all the failure cases.

------------------------------------------------------------------------------
--
-- If you want to read around this topic, you may be interested in
--
--   The Zipper
--   Gerard Huet
--   Journal of Functional Programming, 1997.
--
--   Monadic presentations of lambda terms using generalized inductive types
--   Thorsten Altenkirch and Bernhard Reus
--   Computer Science Logic, 1999.
--
--   An exercise in dependent types: A well-typed interpreter
--   Lennart Augustsson and Magnus Carlsson
--   Workshop on Dependent Types in Programming, Gothenburg, 1999.
--
--   The view from the left
--   Conor McBride and James McKinna
--   Journal of Functional Programming, 2004.


module Dave.Algebra.Naturals.Multiplication where
  open import Dave.Algebra.Naturals.Addition public

  _*_ : ℕ → ℕ → ℕ
  zero * b = zero
  suc a * b = (a * b) + b

  infixl 7 _*_

  *-zero : ∀ (m : ℕ) → m * zero ≡ zero
  *-zero zero = refl
  *-zero (suc m) = begin
    suc m * zero ≡⟨⟩
    m * zero + zero ≡⟨ +-right-identity (m * zero) ⟩
    m * zero ≡⟨ *-zero m ⟩
    zero ∎

  {- Identity -}
  ℕ-*-right-identity : ∀ (m : ℕ) → m * 1 ≡ m
  ℕ-*-right-identity zero = refl
  ℕ-*-right-identity (suc m) = begin
    suc m * 1 ≡⟨⟩
    m * 1 + 1 ≡⟨ cong (λ a → a + 1) (ℕ-*-right-identity m) ⟩
    m + 1 ≡⟨ +-add1ᵣ m ⟩
    suc m ∎

  ℕ-*-left-identity : ∀ (m : ℕ) → 1 * m ≡ m
  ℕ-*-left-identity m = refl

  ℕ-*-HasIdentity : Identity _*_ 1
  Identity.left ℕ-*-HasIdentity = ℕ-*-left-identity
  Identity.right ℕ-*-HasIdentity = ℕ-*-right-identity

  {- Distributivity -}
  *-distrib-+ᵣ : ∀ (m n p : ℕ) → (m + n) * p ≡ m * p + n * p
  *-distrib-+ᵣ zero n p = refl
  *-distrib-+ᵣ (suc m) n p = begin
    (suc m + n) * p ≡⟨⟩
    suc (m + n) * p ≡⟨⟩
    (m + n) * p + p ≡⟨ cong (λ a → a + p) (*-distrib-+ᵣ m n p) ⟩
    m * p + n * p + p ≡⟨ IsCommutativeMonoid.swap021 ℕ-+-IsCommutativeMonoid (m * p) (n * p) p ⟩
    m * p + p + n * p ≡⟨ cong (λ a → m * p + a + n * p) (ℕ-*-left-identity p) ⟩
    m * p + 1 * p + n * p ≡⟨ cong (λ a → a + n * p) (sym (*-distrib-+ᵣ m 1 p)) ⟩
    (m + 1) * p + n * p ≡⟨ cong (λ a → a * p + n * p) (+-add1ᵣ m) ⟩
    suc m * p + n * p ∎

  *-distrib1ᵣ-+ᵣ : ∀ (m p : ℕ) → (m + 1) * p ≡ m * p + p
  *-distrib1ᵣ-+ᵣ m p = begin
    (m + 1) * p ≡⟨ *-distrib-+ᵣ m 1 p ⟩
    m * p + 1 * p ≡⟨⟩
    m * p + p ∎

  *-distrib1ₗ-+ᵣ : ∀ (m p : ℕ) → (1 + m) * p ≡ p + m * p
  *-distrib1ₗ-+ᵣ m p = begin
    (1 + m) * p ≡⟨ *-distrib-+ᵣ 1 m p ⟩
    1 * p + m * p ≡⟨⟩
    p + m * p ∎

  *-distrib-+ₗ : ∀ (m n p : ℕ) → m * (n + p) ≡ m * n + m * p
  *-distrib-+ₗ zero n p = refl
  *-distrib-+ₗ (suc m) n p = begin
    suc m * (n + p) ≡⟨⟩
    m * (n + p) + (n + p) ≡⟨ cong (λ a → a + (n + p)) (*-distrib-+ₗ m n p) ⟩
    m * n + m * p + (n + p) ≡⟨ IsCommutativeMonoid.swap021 ℕ-+-IsCommutativeMonoid (m * n) (m * p) (n + p) ⟩
    m * n + (n + p) + m * p ≡⟨ cong (λ a → a + m * p) (sym (IsSemigroup.assoc ℕ-+-IsSemigroup (m * n) n p)) ⟩
    m * n + n + p + m * p ≡⟨ IsCommutativeMonoid.swap021 ℕ-+-IsCommutativeMonoid (m * n + n) p (m * p) ⟩
    m * n + n + m * p + p ≡⟨⟩
    m * n + 1 * n + m * p + 1 * p ≡⟨ cong (λ a → a + m * p + 1 * p) (sym (*-distrib-+ᵣ m 1 n)) ⟩
    (m + 1) * n + m * p + 1 * p ≡⟨ IsSemigroup.assoc ℕ-+-IsSemigroup ((m + 1) * n) (m * p) (1 * p) ⟩
    (m + 1) * n + (m * p + 1 * p) ≡⟨ cong (λ a → (m + 1) * n + a) (sym (*-distrib-+ᵣ m 1 p)) ⟩
    (m + 1) * n + (m + 1) * p ≡⟨ cong (λ a → a * n + (m + 1) * p) (+-add1ᵣ m) ⟩
    suc m * n + (m + 1) * p ≡⟨ cong (λ a → suc m * n + a * p) (+-add1ᵣ m) ⟩
    suc m * n + suc m * p ∎

  {- Semigroup -}
  *-assoc : associative _*_
  *-assoc zero n p = refl
  *-assoc (suc m) n p = begin
    (suc m * n) * p ≡⟨⟩
    (m * n + n) * p ≡⟨ *-distrib-+ᵣ (m * n) n p ⟩
    m * n * p + n * p ≡⟨⟩
    m * n * p + 1 * (n * p) ≡⟨ cong (λ a → a + 1 * (n * p)) (*-assoc m n p) ⟩
    m * (n * p) + 1 * (n * p) ≡⟨ sym (*-distrib-+ᵣ m 1 (n * p)) ⟩
    (m + 1) * (n * p) ≡⟨ cong (λ a → a * (n * p)) (+-add1ᵣ m) ⟩
    suc m * (n * p) ∎

  ℕ-*-IsSemigroup : IsSemigroup _*_
  IsSemigroup.assoc ℕ-*-IsSemigroup = *-assoc

  ℕ-*-Semigroup : Semigroup
  Semigroup.Carrier ℕ-*-Semigroup = ℕ
  Semigroup._·_ ℕ-*-Semigroup = _*_
  Semigroup.isSemigroup ℕ-*-Semigroup = ℕ-*-IsSemigroup

  {- Monoid -}
  ℕ-*-IsMonoid : IsMonoid _*_ 1
  IsMonoid.semigroup ℕ-*-IsMonoid = ℕ-*-IsSemigroup
  IsMonoid.identity ℕ-*-IsMonoid = ℕ-*-HasIdentity

  ℕ-*-Monoid : Monoid
  Monoid.Carrier ℕ-*-Monoid = ℕ
  Monoid._·_ ℕ-*-Monoid = _*_
  Monoid.e ℕ-*-Monoid = 1
  Monoid.isMonoid ℕ-*-Monoid = ℕ-*-IsMonoid

  {- Commutative Monoid -}
  ℕ-*-comm : commutative _*_
  ℕ-*-comm zero n = begin
    zero * n ≡⟨⟩
    zero ≡⟨ sym (*-zero n) ⟩
    n * zero ∎
  ℕ-*-comm (suc m) n = begin
    suc m * n ≡⟨⟩
    m * n + n ≡⟨ +-comm (m * n) n ⟩
    n + m * n ≡⟨ sym (cong (λ a → a + m * n) (ℕ-*-right-identity n)) ⟩
    n * 1 + m * n ≡⟨ cong (λ a → n * 1 + a) (ℕ-*-comm m n) ⟩
    n * 1 + n * m ≡⟨ sym (*-distrib-+ₗ n 1 m) ⟩
    n * (1 + m) ≡⟨⟩
    n * suc m ∎

  ℕ-*-IsCommutativeMonoid : IsCommutativeMonoid _*_ 1
  IsCommutativeMonoid.isSemigroup ℕ-*-IsCommutativeMonoid = ℕ-*-IsSemigroup
  IsCommutativeMonoid.leftIdentity ℕ-*-IsCommutativeMonoid = ℕ-*-left-identity
  IsCommutativeMonoid.comm ℕ-*-IsCommutativeMonoid = ℕ-*-comm

  ℕ-*-CommutativeMonoid : CommutativeMonoid
  CommutativeMonoid.Carrier ℕ-*-CommutativeMonoid = ℕ
  CommutativeMonoid._·_ ℕ-*-CommutativeMonoid = _*_
  CommutativeMonoid.e ℕ-*-CommutativeMonoid = 1
  CommutativeMonoid.isCommutativeMonoid ℕ-*-CommutativeMonoid = ℕ-*-IsCommutativeMonoid

  *-distrib1ᵣ-+ₗ : ∀ (m p : ℕ) → m * (p + 1) ≡ m * p + m
  *-distrib1ᵣ-+ₗ m p = begin
    m * (p + 1) ≡⟨ ℕ-*-comm m (p + 1) ⟩
    (p + 1) * m ≡⟨ *-distrib1ᵣ-+ᵣ p m ⟩
    p * m + m ≡⟨ cong (λ a → a + m) (ℕ-*-comm p m) ⟩
    m * p + m ∎

  *-distrib1ₗ-+ₗ : ∀ (m p : ℕ) → m * (1 + p) ≡ m + m * p
  *-distrib1ₗ-+ₗ m p = begin
    m * (1 + p) ≡⟨ ℕ-*-comm m (1 + p) ⟩
    (1 + p) * m ≡⟨ *-distrib1ₗ-+ᵣ p m ⟩
    m + p * m ≡⟨ cong (λ a → m + a) (ℕ-*-comm p m) ⟩
    m + m * p ∎


{-# OPTIONS --cubical --safe #-}

module Cardinality.Finite.SplitEnumerable.Isomorphism where

open import Prelude
open import Container
open import Container.List
open import Data.Fin
open import Container.Membership (ℕ , Fin)
open import Path.Reasoning
open import Data.Sigma.Properties
open import Function.Surjective.Properties
open import Data.Fin.Properties
import Data.List.Membership as 𝕃
open import Container.List.Isomorphism
open import Data.Nat.Properties
open import Data.List using (_∷_; []; List)
import Cardinality.Finite.SplitEnumerable.Container as ℒ
import Cardinality.Finite.SplitEnumerable.Inductive as 𝕃

∈ℒ⇒∈𝕃 : ∀ (x : A) (xs : ⟦ ℕ , Fin ⟧ A) → x ∈ xs → x 𝕃.∈ ℒ→𝕃 xs
∈ℒ⇒∈𝕃 x (suc l , xs) (f0   , p) = f0 , p
∈ℒ⇒∈𝕃 x (suc l , xs) (fs n , p) = 𝕃.push (∈ℒ⇒∈𝕃 x (l , xs ∘ fs) (n , p))

𝕃⇔ℒ⟨ℰ!⟩ : 𝕃.ℰ! A ⇔ ℒ.ℰ! A
𝕃⇔ℒ⟨ℰ!⟩ .fun (sup , cov) = 𝕃→ℒ sup , cov
𝕃⇔ℒ⟨ℰ!⟩ .inv (sup , cov) = ℒ→𝕃 sup , λ x → ∈ℒ⇒∈𝕃 x sup (cov x)
𝕃⇔ℒ⟨ℰ!⟩ .rightInv (sup , cov) i .fst = 𝕃⇔ℒ .rightInv sup i
𝕃⇔ℒ⟨ℰ!⟩ .rightInv (sup , cov) i .snd x = ∈ℒ⇒∈𝕃-rightInv sup (cov x) i
  where
  ∈ℒ⇒∈𝕃-rightInv : ∀ xs x∈xs →
    PathP (λ i → x ∈ 𝕃⇔ℒ .rightInv xs i)
      (∈ℒ⇒∈𝕃 x xs x∈xs) x∈xs
  ∈ℒ⇒∈𝕃-rightInv (suc l , xs) (f0   , x∈xs) i = f0 , x∈xs
  ∈ℒ⇒∈𝕃-rightInv (suc l , xs) (fs n , x∈xs) i =
    let m , q = ∈ℒ⇒∈𝕃-rightInv (l , xs ∘ fs) (n , x∈xs) i
    in fs m , q
𝕃⇔ℒ⟨ℰ!⟩ .leftInv (sup , cov) i .fst = 𝕃⇔ℒ .leftInv sup i
𝕃⇔ℒ⟨ℰ!⟩ .leftInv (sup , cov) i .snd x = ∈ℒ⇒∈𝕃-leftInv sup (cov x) i
  where
  ∈ℒ⇒∈𝕃-leftInv : ∀ xs x∈xs →
    PathP (λ i → x 𝕃.∈ 𝕃→ℒ→𝕃 xs i)
      (∈ℒ⇒∈𝕃 x (𝕃→ℒ xs) x∈xs) x∈xs
  ∈ℒ⇒∈𝕃-leftInv (_ ∷ xs) (f0   , x∈xs) i = f0 , x∈xs
  ∈ℒ⇒∈𝕃-leftInv (_ ∷ xs) (fs n , x∈xs) i =
    let m , p = ∈ℒ⇒∈𝕃-leftInv xs (n , x∈xs) i
    in fs m , p


{-# OPTIONS --without-K --safe #-}

-- We do not parameterize this module since we do not have access to _+_ or _*_
-- for the fields that we want (real numbers)

module FLA.Algebra.Structures where

open import Level using (Level) renaming (suc to lsuc)
open import Data.Sum using (_⊎_)
open import Relation.Binary.PropositionalEquality using (_≡_; _≢_)

private
  variable
    ℓ : Level
    A : Set ℓ

record Field (A : Set ℓ) : Set ℓ where

  infixl 6 _+_
  infixl 7 _*_
  infixl 9 -_
  infixl 10 _⁻¹

  field
    _+_ : A → A → A
    _*_ : A → A → A

    0ᶠ   : A
    1ᶠ   : A
    -_   : A → A -- + inverse
    _⁻¹  : A → A -- * inverse

    +-assoc   : (a b c : A) → a + (b + c) ≡ (a + b) + c
    +-comm    : (a b : A)   → a + b ≡ b + a
    +0ᶠ       : (a : A)     → a + 0ᶠ ≡ a
    +-inv     : (a : A)     → - a + a ≡ 0ᶠ
    *-assoc   : (a b c : A) → a * (b * c) ≡ (a * b) * c
    *-comm    : (a b : A)   → a * b ≡ b * a
    *1ᶠ       : (a : A)     → a * 1ᶠ ≡ a
    *-inv     : (a : A)     → (a ≢ 0ᶠ) → a ⁻¹ * a ≡ 1ᶠ
    *-distr-+ : (a b c : A) → a * (b + c) ≡ a * b + a * c

record TotalOrder (A : Set ℓ) : Set (lsuc ℓ) where

  infixl 5 _≤_

  field
    _≤_ : A → A → Set ℓ

    total : (x y : A) → x ≤ y ⊎ y ≤ x
    trans : (x y z : A) → x ≤ y → y ≤ z → x ≤ z
    anti-sym : (x y : A) → x ≤ y → y ≤ x → x ≡ y

record TotalOrderField (A : Set ℓ) : Set (lsuc ℓ) where

  field
    ⦃ F ⦄ : Field A
    ⦃ TO ⦄ : TotalOrder A

  open Field F public
  open TotalOrder TO public

  field
    x≤y→z+x≤z+y : (x y z : A) → x ≤ y → z + x ≤ z + y
    0≤x→0≤y→0≤x*y : (x y : A) → 0ᶠ ≤ x → 0ᶠ ≤ y → 0ᶠ ≤ x * y

-- record CompleteOrderedField (A : Set ℓ) : Set (lsuc ℓ) where
--   field
--     ⦃ TOF ⦄ : TotalOrderField A

--   open LinearlyOrderedField LinOrdField
--
--   UpperBound : Data.Set.NonEmpty A → Data.Set
--   Supremum : (∀ (v : UpperBound(A)) → ∃ [u ∈ UpperBound(A)] [u ≤ v])
--   completeness : (S : Data.Set.NonEmpty A)
--                → (ub: ∃ [y ∈ A] (λ x ∈ S → x ≤ y))
--                → Supremum A
--   completeness : every non-empty subset of F, bounded above, has a
--     supremum in F. If A is a non-empty subset of R, and if A has an upper
--     bound, then A has a least upper bound u, such that for every upper
--     bound v of A, u ≤ v.
--   apparently completeness implies the archimedian principle.

{-
If we want the reals, we need a few things

Linearly ordered set on F, ∀ x, y, z ∈ F
  Totality : x ≤ y or y ≤ x
  Transitivity : if x ≤ y and y ≤ z then x ≤ z
  Anti-symmetry : if x ≤ y and y ≤ x, then x ≡ y

Linearly ordered field (F, +, *, ≤) if
  x ≤ y then z + x ≤ z + y
  0 ≤ x and 0 ≤ y then 0 ≤ x*y

Complete ordered field (F, +, *, ≤) if
  (F, +, *, ≤) is a linearly ordered field
  Completeness : every non-empty subset of F, bounded above, has a supremum in F

Archimedian property:
  (F, +, *, ≤) is a complete ordered field
  r, s ∈ F. r > 0 ∃ n ∈ ℕ. s < r + ... + r (n times)

  Roughly speaking, it is the property of having no infinitely large or
  infinitely small elements

  Axiom of Archimedies is a formulation of this property for ordered fields:
    ∀ ε ∈ F. ε > 0 → ∃ n ∈ ℕ . 1/n < ε

Cauchy sequences representation of the reals and the Dedekind representation
of the reals are both Archimedian completely ordered fields and hence
isomorphic to the reals. Note there is a proof that all Archimedian complete
ordered fields are isomorphic.

References:
[1]: https://www.cs.swan.ac.uk/~csetzer/articlesFromOthers/chiMing/chiMingChuangExtractionOfProgramsForExactRealNumberComputation.pdf
[2]: https://en.wikipedia.org/wiki/Axiomatic_theory_of_real_numbers
-}


{-# OPTIONS --without-K --safe #-}

-- | Functions for the composition of binary relations.
module Dodo.Binary.Composition where

-- Stdlib imports
open import Level using (Level; _⊔_)
open import Data.Product using (_,_; _×_; ∃-syntax)
open import Function.Base using (flip)
open import Relation.Binary using (Rel; REL)
open import Relation.Binary using (Transitive; Symmetric)
-- Local imports
open import Dodo.Binary.Equality

-- # Definitions

infixl 10 _⨾_ ⨾:

-- | Composition of binary relations.
--
-- The composition of two relations P and Q, is mathematically denoted as `P⨾Q`. It is
-- inhabited as `(P⨾Q) x y`, iff `P x z` and `Q z y` are inhabited, for some `z`.
--
-- The notation used here is:
-- > Pxz ⨾[ z ]⨾ Qzy : (P ⨾ Q) x y
--
-- Where the corresponding types are:
-- > Pxz : P x z
-- > Qzy : Q z y
--
-- That notation is syntactic sugar for the constructor below:
-- > ⨾: z Pxz Qzy
--
--
-- # Design decision: Syntactic sugar with explicit `z`
--
-- The syntactic sugar /cannot/ be the actual constructor, as `Pxz` depends on `z`; Meaning
-- that Agda requires to know `z` before `Pxz`. Alternatively, `z` could be /implicit/
-- (as it is in the standard library). For instance:
-- > Pxz ⨾ Qzy : (P ⨾ Q) x y
--
-- The disadvantage of that approach is that `z` may not always be inferred from `Pxz` and `Qzy`.
-- After all, `Pxz` and `Qzy` may be beta-reduced, for which different types of `P`, `Q`, `x`, `y`,
-- and `z` hold; Which introduces ambiguity during type checking. This, in turn, requires making
-- the implicit argument /explicit/ in practice; namely:
-- > _⨾_  {z} Pxz Qzy
--
-- Which is syntactically even less desirable. Hence, in the definition below, `z` is represented
-- explicitly.
data _⨾_ {a b c ℓ₁ ℓ₂ : Level} {A : Set a} {B : Set b} {C : Set c} (L : REL A B ℓ₁) (R : REL B C ℓ₂)
    (i : A) (j : C) : Set (a ⊔ b ⊔ c ⊔ ℓ₁ ⊔ ℓ₂) where
  ⨾: : (k : B) → L i k → R k j → (L ⨾ R) i j

syntax ⨾: k Pik Qkj = Pik ⨾[ k ]⨾ Qkj

-- # Properties

module _ {a b c d ℓ₁ ℓ₂ ℓ₃ : Level} {A : Set a} {B : Set b} {C : Set c} {D : Set d}
    {P : REL A B ℓ₁} {Q : REL B C ℓ₂} {R : REL C D ℓ₃} where

  -- | Composition of binary relations is associative.
  ⨾-assoc : (P ⨾ Q) ⨾ R ⇔₂ P ⨾ (Q ⨾ R)
  ⨾-assoc = ⇔: ⊆-proof ⊇-proof
    where
    ⊆-proof : (P ⨾ Q) ⨾ R ⊆₂' P ⨾ (Q ⨾ R)
    ⊆-proof _ _ ((Pxb ⨾[ b ]⨾ Qbc) ⨾[ c ]⨾ Rcy) = Pxb ⨾[ b ]⨾ (Qbc ⨾[ c ]⨾ Rcy)
    ⊇-proof : P ⨾ (Q ⨾ R) ⊆₂' (P ⨾ Q) ⨾ R
    ⊇-proof _ _ (Pxb ⨾[ b ]⨾ (Qbc ⨾[ c ]⨾ Rcy)) = (Pxb ⨾[ b ]⨾ Qbc) ⨾[ c ]⨾ Rcy

module _ {a ℓ : Level} {A : Set a} {R : Rel A ℓ} where

  -- | The composition of a transitive relation with itself is a subset of itself.
  ⨾-trans-⊆₂ : Transitive R → (R ⨾ R) ⊆₂ R
  ⨾-trans-⊆₂ transR = ⊆: lemma
    where
    lemma : (R ⨾ R) ⊆₂' R
    lemma _ _ (Rxz ⨾[ z ]⨾ Rzy) = transR Rxz Rzy

-- # Operations

module _ {a b c ℓ₁ ℓ₂ ℓ₃ : Level} {A : Set a} {B : Set b} {C : Set c}
    {P : REL A B ℓ₁} {Q : REL A B ℓ₂} {R : REL B C ℓ₃} where

  ⨾-substˡ-⊆₂ : P ⊆₂ Q → (P ⨾ R) ⊆₂ (Q ⨾ R)
  ⨾-substˡ-⊆₂ (⊆: P⊆Q) = ⊆: ⊆-proof
    where
    ⊆-proof : (P ⨾ R) ⊆₂' (Q ⨾ R)
    ⊆-proof x y (Pxz ⨾[ z ]⨾ Rzy) = P⊆Q x z Pxz ⨾[ z ]⨾ Rzy

module _ {a b c ℓ₁ ℓ₂ ℓ₃ : Level} {A : Set a} {B : Set b} {C : Set c}
    {P : REL A B ℓ₁} {Q : REL A B ℓ₂} {R : REL B C ℓ₃} where

  ⨾-substˡ : P ⇔₂ Q → (P ⨾ R) ⇔₂ (Q ⨾ R)
  ⨾-substˡ = ⇔₂-compose ⨾-substˡ-⊆₂ ⨾-substˡ-⊆₂

module _ {a b c ℓ₁ ℓ₂ ℓ₃ : Level} {A : Set a} {B : Set b} {C : Set c}
    {P : REL B C ℓ₁} {Q : REL B C ℓ₂} {R : REL A B ℓ₃} where

  ⨾-substʳ-⊆₂ : P ⊆₂ Q → (R ⨾ P) ⊆₂ (R ⨾ Q)
  ⨾-substʳ-⊆₂ (⊆: P⊆Q) = ⊆: ⊆-proof
    where
    ⊆-proof : (R ⨾ P) ⊆₂' (R ⨾ Q)
    ⊆-proof x y (Rxz ⨾[ z ]⨾ Pzy) = Rxz ⨾[ z ]⨾ P⊆Q z y Pzy

module _ {a b c ℓ₁ ℓ₂ ℓ₃ : Level} {A : Set a} {B : Set b} {C : Set c}
    {P : REL B C ℓ₁} {Q : REL B C ℓ₂} {R : REL A B ℓ₃} where

  ⨾-substʳ : P ⇔₂ Q → (R ⨾ P) ⇔₂ (R ⨾ Q)
  ⨾-substʳ = ⇔₂-compose ⨾-substʳ-⊆₂ ⨾-substʳ-⊆₂

module _ {a b c ℓ₁ ℓ₂ : Level} {A : Set a} {B : Set b} {C : Set c}
    {P : REL A B ℓ₁} {Q : REL B C ℓ₂} where

  -- | Expanded definition of flipping the composition of two relations.
  ⨾-flip : flip (P ⨾ Q) ⇔₂ flip Q ⨾ flip P
  ⨾-flip = ⇔: ⊆-proof ⊇-proof
    where
    ⊆-proof : flip (P ⨾ Q) ⊆₂' flip Q ⨾ flip P
    ⊆-proof x y (Pyz ⨾[ z ]⨾ Qzx) = (Qzx ⨾[ z ]⨾ Pyz)
    ⊇-proof : flip Q ⨾ flip P ⊆₂' flip (P ⨾ Q)
    ⊇-proof x y (Qzy ⨾[ z ]⨾ Pxz) = (Pxz ⨾[ z ]⨾ Qzy)


{-# OPTIONS --without-K #-}
module NTypes.Contractible where

open import Equivalence
open import Types

isContr : ∀ {a} → Set a → Set _
isContr A = Σ A λ x → (y : A) → x ≡ y

contr→eq-⊤ : ∀ {a} {A : Set a} → isContr A → A ≃ ⊤
contr→eq-⊤ h
  = (λ _ → _)
  , ((λ _ → π₁ h) , λ _ → refl)
  , ((λ _ → π₁ h) , π₂ h)

eq-⊤→contr : ∀ {a} {A : Set a} → A ≃ ⊤ → isContr A
eq-⊤→contr (f , (g , α) , (h , β)) = h _ , β

pp-space-contr : ∀ {a} {A : Set a} (x : A) →
  isContr (Σ A λ y → x ≡ y)
pp-space-contr {A = A} x
  = (x , refl)
  , λ y → J
      (λ x y p → Id (Σ A (λ y → x ≡ y)) (x , refl) (y , p))
      (λ _ → refl) _ _ (π₂ y)


{-# OPTIONS --without-K --safe #-}

module Categories.Category.Equivalence where

-- Strong equivalence of categories.  Same as ordinary equivalence in Cat.
-- May not include everything we'd like to think of as equivalences, namely
-- the full, faithful functors that are essentially surjective on objects.

open import Level
open import Relation.Binary using (IsEquivalence; Setoid)

open import Categories.Category.Core using (Category)
open import Categories.Functor renaming (id to idF)
open import Categories.NaturalTransformation.NaturalIsomorphism as ≃
  using (NaturalIsomorphism ; unitorˡ; unitorʳ; associator; _ⓘᵥ_; _ⓘˡ_; _ⓘʳ_)

private
  variable
    o ℓ e : Level
    C D E : Category o ℓ e

record WeakInverse (F : Functor C D) (G : Functor D C) : Set (levelOfTerm F ⊔ levelOfTerm G) where
  field
    F∘G≈id : NaturalIsomorphism (F ∘F G) idF
    G∘F≈id : NaturalIsomorphism (G ∘F F) idF

  module F∘G≈id = NaturalIsomorphism F∘G≈id
  module G∘F≈id = NaturalIsomorphism G∘F≈id

record StrongEquivalence {o ℓ e o′ ℓ′ e′} (C : Category o ℓ e) (D : Category o′ ℓ′ e′) : Set (o ⊔ ℓ ⊔ e ⊔ o′ ⊔ ℓ′ ⊔ e′) where
  field
    F            : Functor C D
    G            : Functor D C
    weak-inverse : WeakInverse F G

  open WeakInverse weak-inverse public

refl : StrongEquivalence C C
refl = record
  { F            = idF
  ; G            = idF
  ; weak-inverse = record
    { F∘G≈id = unitorˡ
    ; G∘F≈id = unitorˡ
    }
  }

sym : StrongEquivalence C D → StrongEquivalence D C
sym e = record
  { F            = G
  ; G            = F
  ; weak-inverse = record
    { F∘G≈id     = G∘F≈id
    ; G∘F≈id     = F∘G≈id
    }
  }
  where open StrongEquivalence e

trans : StrongEquivalence C D → StrongEquivalence D E → StrongEquivalence C E
trans {C = C} {D = D} {E = E} e e′ = record
  { F            = e′.F ∘F e.F
  ; G            = e.G ∘F e′.G
  ; weak-inverse = record
    { F∘G≈id = let module S = Setoid (≃.Functor-NI-setoid E E)
               in S.trans (S.trans (associator (e.G ∘F e′.G) e.F e′.F)
                                   (e′.F ⓘˡ (unitorˡ ⓘᵥ (e.F∘G≈id ⓘʳ e′.G) ⓘᵥ ≃.sym (associator e′.G e.G e.F))))
                          e′.F∘G≈id
    ; G∘F≈id = let module S = Setoid (≃.Functor-NI-setoid C C)
               in S.trans (S.trans (associator (e′.F ∘F e.F) e′.G e.G)
                                   (e.G ⓘˡ (unitorˡ ⓘᵥ (e′.G∘F≈id ⓘʳ e.F) ⓘᵥ ≃.sym (associator e.F e′.F e′.G))))
                          e.G∘F≈id
    }
  }
  where module e  = StrongEquivalence e
        module e′ = StrongEquivalence e′

isEquivalence : ∀ {o ℓ e} → IsEquivalence (StrongEquivalence {o} {ℓ} {e})
isEquivalence = record
  { refl  = refl
  ; sym   = sym
  ; trans = trans
  }

setoid : ∀ o ℓ e → Setoid _ _
setoid o ℓ e = record
  { Carrier       = Category o ℓ e
  ; _≈_           = StrongEquivalence
  ; isEquivalence = isEquivalence
  }


{-# OPTIONS --safe #-}
module Cubical.Data.FinData.Order where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Function

import Cubical.Data.Empty as ⊥
open import Cubical.Data.Nat using (ℕ)
open import Cubical.Data.Nat.Order
open import Cubical.Data.Bool.Base
open import Cubical.Relation.Nullary
open import Cubical.Relation.Binary

open import Cubical.Data.FinData.Base
open import Cubical.Data.FinData.Properties

private
  variable
    ℓ : Level

-- Order relation:
_≤Fin_ : {n : ℕ} → Fin n → Fin n → Type
i ≤Fin j = (toℕ i) ≤ (toℕ j)

_<Fin_ : {n : ℕ} → Fin n → Fin n → Type
i <Fin j = (suc i) ≤Fin (weakenFin j)

open BinaryRelation
≤FinIsPropValued : ∀ {n : ℕ} → isPropValued (_≤Fin_ {n})
≤FinIsPropValued _ _ = isProp≤

-- inductive version
_≤'Fin_ : {n : ℕ} → Fin n → Fin n → Type
i ≤'Fin j = (toℕ i) ≤' (toℕ j)

_<'Fin_ : {n : ℕ} → Fin n → Fin n → Type
i <'Fin j = (suc i) ≤'Fin (weakenFin j)

open BinaryRelation
≤'FinIsPropValued : ∀ {n : ℕ} → isPropValued (_≤'Fin_ {n})
≤'FinIsPropValued _ _ = ≤'IsPropValued _ _


{-# OPTIONS --safe #-}
module Cubical.Algebra.AbGroup.Instances.Direct-Sum where

open import Cubical.Foundations.Prelude

open import Cubical.Algebra.AbGroup

open import Cubical.Algebra.Direct-Sum.Base
open import Cubical.Algebra.Direct-Sum.Properties

open import Cubical.Algebra.Polynomials.Multivariate.Base

private variable
  ℓ ℓ' : Level

module _ (Idx : Type ℓ) (P : Idx → Type ℓ') (AGP : (r : Idx) → AbGroupStr (P r)) where

  open AbGroupStr

  ⊕-AbGr : AbGroup (ℓ-max ℓ ℓ')
  fst ⊕-AbGr = ⊕ Idx P AGP
  0g (snd ⊕-AbGr) = neutral
  _+_ (snd ⊕-AbGr) = _add_
  - snd ⊕-AbGr = inv Idx P AGP
  isAbGroup (snd ⊕-AbGr) = makeIsAbGroup trunc addAssoc addRid (rinv Idx P AGP) addComm


module Extra where

open import Prelude

fromTo : Nat -> List Nat
fromTo n = f n
  where
    f : Nat -> List Nat
    f 0 = []
    f (suc i) = n - i ∷ f i

downFrom : Nat -> List Nat
downFrom 0 = []
downFrom (suc n) = (suc n) ∷ downFrom n

fromTo' : Nat -> List Nat
fromTo' = f []
  where
    f : List Nat -> Nat -> List Nat
    f xs 0 = xs
    f xs (suc x) = seq xs (f ( suc x ∷ xs ) x)

downFrom' : Nat -> List Nat
downFrom' n = f [] n
  where
    f : List Nat -> Nat -> List Nat
    f xs 0 = xs
    f xs (suc x) = seq xs (f ((n - x) ∷ xs) x)

blumblumshub : Nat -> (m : Nat) -> {{ nz : NonZero m }} -> Nat
blumblumshub xn M = natMod M (xn ^ 2)

downFromBbs' : Nat -> Nat -> List Nat
downFromBbs' seed = f seed []
  where
  f : Nat -> List Nat -> Nat -> List Nat
  f _ acc 0       = acc
  f x acc (suc l) = seq acc $ f (blumblumshub x M) ( x ∷ acc ) l
    where
      M M17 M31 : Nat
      M           = M17 * M31
      M17         = 2971
      M31         = 4111

fromToBbs : Nat -> Nat -> List Nat
fromToBbs _ 0 = []
fromToBbs xi (suc n) = xi ∷ fromToBbs (blumblumshub xi m) n
  where
    p = 2971
    q = 4111
    m = p * q


------------------------------------------------------------------------
-- Properties satisfied by posets
------------------------------------------------------------------------

open import Relation.Binary

module Relation.Binary.Props.Poset (p : Poset) where

open Relation.Binary.Poset p hiding (trans)
import Relation.Binary.NonStrictToStrict as Conv
open Conv _≈_ _≤_

------------------------------------------------------------------------
-- Posets can be turned into strict partial orders

strictPartialOrder : StrictPartialOrder
strictPartialOrder = record
  { isStrictPartialOrder = record
    { isEquivalence = isEquivalence
    ; irrefl        = irrefl
    ; trans         = trans isPartialOrder
    ; <-resp-≈      = <-resp-≈ isEquivalence ≤-resp-≈
    }
  }

open StrictPartialOrder strictPartialOrder


module Problem4 where

infixr 40 _::_

data List (A : Set) : Set where
  []   : List A
  _::_ : A -> List A -> List A

-- 4.1

map : {A B : Set} -> (A -> B) -> List A -> List B
map f []        = []
map f (x :: xs) = f x :: map f xs

infixr 40 _++_

_++_ : {A : Set} -> List A -> List A -> List A
[]        ++ ys = ys
(x :: xs) ++ ys = x :: (xs ++ ys)

-- 4.2

infixr 40 _▹_

data All {A : Set}(P : A -> Set) : List A -> Set where
  ∅   : All P []
  _▹_ : {x : A} -> P x -> {xs : List A} -> All P xs -> All P (x :: xs)

-- 4.3

data Some {A : Set}(P : A -> Set) : List A -> Set where
  hd : {x : A} -> P x -> {xs : List A}   -> Some P (x :: xs)
  tl : {x : A}{xs : List A} -> Some P xs -> Some P (x :: xs)

-- 4.4

-- We need composition at a higher universe here.

_∘¹_ : {A B : Set}{C : B -> Set1}(f : (x : B) -> C x)
       (g : A -> B)(x : A) -> C (g x)
(f ∘¹ g) x = f (g x)

-- You might have to give f explictly when applying this theorem.

all-map : {A B : Set}{P : A -> Set}{Q : B -> Set}{f : A -> B}{xs : List A} ->
          ({x : A} -> P x -> Q (f x)) ->
          All P xs -> All Q (map f xs)
all-map h ∅        = ∅
all-map h (p ▹ ps) = h p ▹ all-map h ps

all-++ : {A : Set}{P : A -> Set}{xs ys : List A} ->
         All P xs -> All P ys -> All P (xs ++ ys)
all-++ ∅        qs = qs
all-++ (p ▹ ps) qs = p ▹ (all-++ ps qs)

some-map : {A B : Set}{P : A -> Set}{Q : B -> Set}{f : A -> B}{xs : List A} ->
           ({x : A} -> P x -> Q (f x)) ->
           Some P xs -> Some Q (map f xs)
some-map h (hd p)  = hd (h p)
some-map h (tl ps) = tl (some-map h ps)

some-++-left : {A : Set}{P : A -> Set}{xs ys : List A} ->
               Some P xs -> Some P (xs ++ ys)
some-++-left (hd p)  = hd p
some-++-left (tl ps) = tl (some-++-left ps)

-- Here we can't expect to infer xs, so we make it explicit

some-++-right : {A : Set}{P : A -> Set}(xs : List A){ys : List A} ->
               Some P ys -> Some P (xs ++ ys)
some-++-right []        p = p
some-++-right (x :: xs) p = tl (some-++-right xs p)

-- 4.5

data _==_ {A : Set}(x : A) : A -> Set where
  refl : x == x

_∈_ : {A : Set} -> A -> List A -> Set
x ∈ xs = Some (_==_ x) xs

-- 4.6

record True : Set where

tt : True
tt = record {}

Nat  = List True

zero : Nat
zero = []

suc : Nat -> Nat
suc n = tt :: n

Vec : Set -> Nat -> Set
Vec A n = All (\_ -> A) n

Fin : Nat -> Set
Fin n = Some (\_ -> True) n

-- 4.7

infixr 5 _,_

data _×_ (A : Set)(B : A -> Set) : Set where
  _,_ : (x : A) -> B x -> A × B

_∧_ : Set -> Set -> Set
A ∧ B = A × (\_ -> B)

_!_ : {A : Set}{P : A -> Set}{Q : A -> Set}{xs : List A} ->
      All P xs -> Some Q xs -> A × (\z -> P z ∧ Q z)
∅ ! ()
(p ▹ ps) ! hd q = (_ , p , q)
(p ▹ ps) ! tl q = ps ! q

-- 4.8

data False : Set where

¬_ : Set -> Set
¬ A = A -> False

data _∨_ (A B : Set) : Set where
  inl : A -> A ∨ B
  inr : B -> A ∨ B

data Bool : Set where
  true  : Bool
  false : Bool

data IsTrue : Bool -> Set where
  isTrue : IsTrue true

Holds : {A : Set} -> (A -> Bool) -> A -> Set
Holds p x = IsTrue (p x)

false-isn't-true : ¬ IsTrue false
false-isn't-true ()

decide : {A : Set}(p : A -> Bool)(x : A) ->
         Holds p x ∨ ¬ Holds p x
decide p x with p x
...        | true  = inl isTrue
...        | false = inr false-isn't-true

all : {A : Set}(p : A -> Bool)(xs : List A) ->
      All (Holds p) xs ∨ Some (\x -> ¬ Holds p x) xs
all p [] = inl ∅
all p (x :: xs) with decide p x
... | inr npx = inr (hd npx)
... | inl px  with all p xs
...   | inr npxs = inr (tl npxs)
...   | inl pxs  = inl (px ▹ pxs)


------------------------------------------------------------------------
-- Vectors, defined as functions from finite sets
------------------------------------------------------------------------

{-# OPTIONS --without-K --safe #-}

open import Equality

module Vec.Function
  {reflexive} (eq : ∀ {a p} → Equality-with-J a p reflexive) where

open import Prelude

open import Bijection eq using (_↔_)
open Derived-definitions-and-properties eq
open import Function-universe eq hiding (id; _∘_)
open import List eq using (length)
open import Surjection eq using (_↠_; ↠-≡)

private
  variable
    a   : Level
    A B : Type a
    n   : ℕ
    k   : Kind

------------------------------------------------------------------------
-- The type

-- Vectors.

Vec : ∀ {a} → Type a → ℕ → Type a
Vec A n = Fin n → A

-- Nil.

nil : Vec A zero
nil = λ ()

-- Cons.

cons : A → Vec A n → Vec A (suc n)
cons x xs = [ const x , xs ]

------------------------------------------------------------------------
-- Some simple functions

-- Finds the element at the given position.

index : Vec A n → Fin n → A
index = id

-- Applies the function to every element in the vector.

map : (A → B) → Vec A n → Vec B n
map f xs = f ∘ xs

-- Constructs a vector containing a certain number of copies of the
-- given element.

replicate : A → Vec A n
replicate = const

-- The head of the vector.

head : Vec A (suc n) → A
head = _$ fzero

-- The tail of the vector.

tail : Vec A (suc n) → Vec A n
tail = _∘ fsuc

-- Updates the element at the given position.

infix 3 _[_≔_]

_[_≔_] : Vec A n → Fin n → A → Vec A n
_[_≔_] {n = zero}  _  ()       _
_[_≔_] {n = suc _} xs fzero    x = cons x (tail xs)
_[_≔_] {n = suc _} xs (fsuc i) x = cons (head xs) (tail xs [ i ≔ x ])

------------------------------------------------------------------------
-- Some properties

-- Empty lists are equal to nil (assuming extensionality).

empty≡nil :
  {A : Type a} →
  Extensionality lzero a →
  {xs : Vec A zero} →
  xs ≡ nil
empty≡nil ext = apply-ext ext λ ()

-- A non-empty list is equal to its head consed onto its tail
-- (assuming extensionality).

non-empty≡cons-head-tail :
  {A : Type a} →
  Extensionality lzero a →
  (xs : Vec A (suc n)) →
  xs ≡ cons (head xs) (tail xs)
non-empty≡cons-head-tail ext _ =
  apply-ext ext [ (λ _ → refl _) , (λ _ → refl _) ]

------------------------------------------------------------------------
-- Conversions to and from lists

-- Vectors can be converted to lists.

to-list : Vec A n → List A
to-list {n = zero}  _  = []
to-list {n = suc _} xs = head xs ∷ to-list (tail xs)

-- Lists can be converted to vectors.

from-list : (xs : List A) → Vec A (length xs)
from-list []       = nil
from-list (x ∷ xs) = cons x (from-list xs)

-- There is a split surjection from ∃ (Vec A) to List A.

∃Vec↠List : ∃ (Vec A) ↠ List A
∃Vec↠List {A = A} = record
  { logical-equivalence = record
    { to   = to-list ∘ proj₂
    ; from = λ xs → length xs , from-list xs
    }
  ; right-inverse-of = to∘from
  }
  where
  to∘from : (xs : List A) → to-list (from-list xs) ≡ xs
  to∘from []       = refl _
  to∘from (x ∷ xs) = cong (x ∷_) (to∘from xs)

-- ∃ (Vec A) is equivalent to List A (assuming extensionality).

∃Vec≃List :
  {A : Type a} →
  ∃ (Vec A) ↝[ lzero ∣ a ] List A
∃Vec≃List {a = a} {A = A} =
  generalise-ext?
    (_↠_.logical-equivalence ∃Vec↠List)
    (λ ext →
         _↠_.right-inverse-of ∃Vec↠List
       , uncurry (from∘to ext))
  where
  tail′ : A → ∃ (Vec A) ↠ ∃ (Vec A)
  tail′ y = record
    { logical-equivalence = record
      { to   = λ where
                 (suc n , xs)  → n , tail xs
                 xs@(zero , _) → xs
      ; from = Σ-map suc (cons y)
      }
    ; right-inverse-of = refl
    }

  from∘to :
    Extensionality lzero a →
    ∀ n (xs : Vec A n) →
    (length (to-list xs) , from-list (to-list xs)) ≡ (n , xs)
  from∘to ext zero xs =
    (length {A = A} [] , nil)  ≡⟨ cong (zero ,_) $ sym $ empty≡nil ext ⟩∎
    (zero , xs)                ∎

  from∘to ext (suc n) xs =                                        $⟨ from∘to ext n (tail xs) ⟩
    (length (to-list (tail xs)) , from-list (to-list (tail xs)))
      ≡
    (n , tail xs)                                                 ↝⟨ _↠_.from $ ↠-≡ (tail′ (head xs)) ⟩

    (length (to-list xs) , from-list (to-list xs))
      ≡
    (suc n , cons (head xs) (tail xs))                            ↝⟨ ≡⇒↝ _ $ cong (λ ys → (_ , from-list (to-list xs)) ≡ (_ , ys)) $ sym $
                                                                     non-empty≡cons-head-tail ext xs ⟩□
    (length (to-list xs) , from-list (to-list xs))
      ≡
    (suc n , xs)                                                  □


module muRec where

open import Data.Nat
open import Data.Sum
open import Relation.Binary.PropositionalEquality as PE
open import PropsAsTypes

mutual
  record D (A : Set) : Set where
    coinductive
    field
      step : Dμ A

  data Dμ (A : Set) : Set where
    now   : A   → Dμ A
    later : D A → Dμ A

open D public

coiter-D : {A X : Set} → (X → A ⊎ X) → X → D A
coiter-D c x .step with c x
... | inj₁ a  = now a
... | inj₂ x' = later (coiter-D c x')

compute-D₁ : {A X : Set} (c : X → A ⊎ X) (x : X) →
            ∀ a → c x ≡ inj₁ a → coiter-D c x .step ≡ now a
compute-D₁ c x a p with c x
compute-D₁ c x .a refl | inj₁ a = refl
compute-D₁ c x  a ()   | inj₂ y

compute-D₂ : {A X : Set} (c : X → A ⊎ X) (x : X) →
             ∀ x' → c x ≡ inj₂ x' →
                    coiter-D c x .step ≡ later (coiter-D c x')
compute-D₂ c x x' p with c x
compute-D₂ c x  x' ()   | inj₁ a
compute-D₂ c x .x' refl | inj₂ x' = refl

compute-D-inv₁ : {A X : Set} (c : X → A ⊎ X) (x : X) →
                 ∀ a → coiter-D c x .step ≡ now a → c x ≡ inj₁ a
compute-D-inv₁ c x a p with c x
compute-D-inv₁ c x .a refl | inj₁ a = refl
compute-D-inv₁ c x  a ()   | inj₂ y

compute-D-inv₂ : {A X : Set} (c : X → A ⊎ X) (x : X) →
                 ∀ x' → c x ≡ inj₂ x' →
                        coiter-D c x .step ≡ later (coiter-D c x')
compute-D-inv₂ c x x' p with c x
compute-D-inv₂ c x  x' ()   | inj₁ a
compute-D-inv₂ c x .x' refl | inj₂ x' = refl

ifIsZero_then_else_ : {A : Set} → ℕ → A → A → A
ifIsZero zero    then x else y = x
ifIsZero (suc _) then x else y = y

isZeroProof : ∀{m n k : ℕ} →
              ifIsZero m then inj₁ n else inj₂ k ≡ inj₁ n →
              m ≡ 0
isZeroProof {zero}  p = refl
isZeroProof {suc m} ()

μ-rec'-coalg : (ℕ → ℕ) → ℕ → ℕ ⊎ ℕ
μ-rec'-coalg f n = ifIsZero f n then inj₁ n else inj₂ (suc n)

μ-rec' : (ℕ → ℕ) → ℕ → D ℕ
μ-rec' f = coiter-D (μ-rec'-coalg f)

μ-rec : (ℕ → ℕ) → D ℕ
μ-rec f = μ-rec' f 0

data _↓_ {A : Set} (d : D A) (a : A) : Prop where
  stopped :              d .step ≡ now a             → d ↓ a
  delayed : {d' : D A} → d .step ≡ later d' → d' ↓ a → d ↓ a

μ-rec'-coalg-correct : (f : ℕ → ℕ) (n : ℕ) → μ-rec'-coalg f n ≡ inj₁ n → f n ≡ 0
μ-rec'-coalg-correct f n p = isZeroProof p

μ-rec'-correct : (f : ℕ → ℕ) (m n : ℕ) → (μ-rec' f m ↓ n) → f (m + n) ≡ 0
μ-rec'-correct f m n (stopped p) =
  let q = compute-D-inv₁ (μ-rec'-coalg f) m n p
  in {!!}
μ-rec'-correct f m n (delayed d' p) = {!!}

μ-rec-correct : (f : ℕ → ℕ) (n : ℕ) → (μ-rec f ↓ n) → f n ≡ 0
μ-rec-correct f n (stopped p) =
  let q = compute-D-inv₁ (μ-rec'-coalg f) 0 n p
  in μ-rec'-coalg-correct f n {!!}
μ-rec-correct f n (delayed d' p) = {!!}


{-# OPTIONS --cubical --safe --guardedness #-}

module Data.PolyP.Currying where

open import Prelude hiding (_⟨_⟩_)
open import Data.Vec.Iterated

open import Data.PolyP.Universe

Curriedⁿ : ℕ → Type₁
Curriedⁿ zero    = Type
Curriedⁿ (suc n) = Type → Curriedⁿ n

_~_ : ∀ {n} → (Params n → Type) → Curriedⁿ n
_~_ {n = zero}  f = f []
_~_ {n = suc n} f A = _~_ {n = n} (f ∘ (A ∷_))


import      Lvl
open import Structure.Category
open import Structure.Setoid
open import Type

module Structure.Category.Morphism.Transport
  {ℓₒ ℓₘ ℓₑ : Lvl.Level}
  (cat : CategoryObject{ℓₒ}{ℓₘ}{ℓₑ})
  where

import      Functional.Dependent as Fn
import      Function.Equals
open        Function.Equals.Dependent
open import Logic
open import Logic.Propositional
open import Logic.Predicate
open import Relator.Equals using ([≡]-intro) renaming (_≡_ to _≡ₑ_)
open import Relator.Equals.Proofs
import      Structure.Category.Morphism.IdTransport as IdTransport
import      Structure.Categorical.Names as Names
open import Structure.Categorical.Properties
open import Structure.Function
open import Structure.Relator.Properties

open CategoryObject(cat)
open Category(category)
open Category.ArrowNotation(category)
open Morphism.OperModule ⦃ morphism-equiv ⦄ (\{x} → _∘_ {x})
open Morphism.IdModule   ⦃ morphism-equiv ⦄ (\{x} → _∘_ {x})(id)

module _ {P : Object → Object} (p : ∀{x} → (x ⟶ P(x))) ⦃ isomorphism : ∀{x} → Isomorphism(p{x}) ⦄ where
  private variable a b c : Object

  transport : (a ≡ₑ b) → (P(a) ⟶ P(b))
  transport ab = p ∘ IdTransport.transport(cat) ab ∘ inv(p)


{-# OPTIONS --without-K --safe #-}

open import Categories.Category
open import Categories.Functor

module Categories.Diagram.Limit.Properties
       {o ℓ e} {o′ ℓ′ e′} {C : Category o ℓ e} {J : Category o′ ℓ′ e′}  where

open import Function.Equality using (Π)
open import Relation.Binary using (Setoid)

open import Categories.Category.Instance.Setoids
open import Categories.Diagram.Cone.Properties
open import Categories.Functor.Hom
open import Categories.NaturalTransformation.NaturalIsomorphism using (NaturalIsomorphism; _≃_; module ≃)
open import Categories.Morphism.Reasoning C
open import Categories.Morphism C

import Categories.Category.Construction.Cones as Con
import Categories.Diagram.Limit as Lim

open Π
open Hom C

private
  module J = Category J
  module C = Category C

  open C
  variable
    X Y Z : Obj
    f g h : X ⇒ Y

open HomReasoning

-- Hom functor preserves limits in C
module _ (W : Obj) {F : Functor J C} (lim : Lim.Limit F) where
  private
    module F  = Functor F
    module LF = Lim F
    module CF = Con F
    module lim = LF.Limit lim
    open lim
    HomF : Functor J (Setoids ℓ e)
    HomF = Hom[ W ,-] ∘F F

    module LHomF = Lim HomF
    module CHomF = Con HomF

  hom-resp-limit : LHomF.Limit
  hom-resp-limit = record
    { terminal = record
      { ⊤        = ⊤
      ; !        = !
      ; !-unique = !-unique
      }
    }
    where ⊤ : CHomF.Cone
          ⊤ = record
            { N    = hom-setoid {W} {apex}
            ; apex = record
              { ψ       = λ X → record
                { _⟨$⟩_ = λ f → proj X ∘ f
                ; cong  = ∘-resp-≈ʳ
                }
              ; commute = λ {X Y} f {h i} eq → begin
                F.F₁ f ∘ (proj X ∘ h) ∘ C.id ≈⟨ center⁻¹ (limit-commute f) identityʳ ⟩
                proj Y ∘ h                   ≈⟨ refl⟩∘⟨ eq ⟩
                proj Y ∘ i                   ∎
              }
            }

          KW : (K : CHomF.Cone) → Setoid.Carrier (CHomF.Cone.N K) → CF.Cone
          KW K x = record
            { N    = W
            ; apex = record
              { ψ       = λ X → K.ψ X ⟨$⟩ x
              ; commute = λ f → sym (∘-resp-≈ʳ identityʳ) ○ K.commute f (Setoid.refl K.N)
              }
            }
            where module K = CHomF.Cone K

          ! : ∀ {K : CHomF.Cone} → CHomF.Cones [ K , ⊤ ]
          ! {K} = record
            { arr     = record
              { _⟨$⟩_ = λ x → rep (KW K x)
              ; cong  = λ {x y} eq → LF.ψ-≈⇒rep-≈ W (CF.Cone.apex (KW K x)) (CF.Cone.apex (KW K y)) lim
                                                  (λ A → cong (K.ψ A) eq)
              }
            ; commute = λ {X} {x y} eq → begin
              proj X ∘ rep (KW K x) ≈⟨ lim.commute ⟩
              K.ψ X ⟨$⟩ x           ≈⟨ cong (K.ψ X) eq ⟩
              K.ψ X ⟨$⟩ y           ∎
            }
            where module K = CHomF.Cone K

          !-unique : ∀ {K : CHomF.Cone} (f : CHomF.Cones [ K , ⊤ ]) → CHomF.Cones [ ! ≈ f ]
          !-unique {K} f {x} {y} x≈y = begin
            rep (KW K x) ≈⟨ terminal.!-unique f′ ⟩
            f.arr ⟨$⟩ x  ≈⟨ cong f.arr x≈y ⟩
            f.arr ⟨$⟩ y  ∎
            where module K = CHomF.Cone K
                  module f = CHomF.Cone⇒ f

                  f′ : CF.Cones [ KW K x , limit ]
                  f′ = record
                    { arr     = f.arr ⟨$⟩ x
                    ; commute = f.commute (Setoid.refl K.N)
                    }

-- natural isomorphisms respects limits
module _ {F G : Functor J C} (F≃G : F ≃ G) where
  private
    module F  = Functor F
    module G  = Functor G
    module LF = Lim F
    module LG = Lim G
    open NaturalIsomorphism F≃G

  ≃-resp-lim : LF.Limit → LG.Limit
  ≃-resp-lim L = record
    { terminal = record
      { ⊤        = record
        { apex = record
          { ψ       = λ j → ⇒.η j ∘ proj j
          ; commute = λ {X Y} f → begin
            G.F₁ f ∘ ⇒.η X ∘ proj X   ≈⟨ pullˡ (⇒.sym-commute f) ⟩
            (⇒.η Y ∘ F.F₁ f) ∘ proj X ≈⟨ pullʳ (limit-commute f) ⟩
            ⇒.η Y ∘ proj Y            ∎
          }
        }
      ; !        = λ {A} → record
        { arr     = rep (nat-map-Cone F⇐G A)
        ; commute = λ {j} → assoc ○ ⟺ (switch-tofromˡ (record { iso = iso j }) (⟺ commute))
        }
      ; !-unique = λ {K} f →
        let module f = Con.Cone⇒ G f
        in terminal.!-unique record
          { arr     = f.arr
          ; commute = λ {j} → switch-fromtoˡ (record { iso = iso j }) (sym-assoc ○ f.commute)
          }
      }
    }
    where open LF.Limit L

  ≃⇒Cone⇒ : ∀ (Lf : LF.Limit) (Lg : LG.Limit) → Con.Cones G [ LG.Limit.limit (≃-resp-lim Lf) , LG.Limit.limit Lg ]
  ≃⇒Cone⇒ Lf Lg = rep-cone (LG.Limit.limit (≃-resp-lim Lf))
    where open LG.Limit Lg

≃⇒lim≅ : ∀ {F G : Functor J C} (F≃G : F ≃ G) (Lf : Lim.Limit F) (Lg : Lim.Limit G) → Lim.Limit.apex Lf ≅ Lim.Limit.apex Lg
≃⇒lim≅ {F = F} {G} F≃G Lf Lg = record
  { from = arr (≃⇒Cone⇒ F≃G Lf Lg)
  ; to   = arr (≃⇒Cone⇒ (≃.sym F≃G) Lg Lf)
  ; iso  = record
    { isoˡ = Lf.terminal.⊤-id record
      { commute = λ {j} → begin
        Lf.proj j ∘ arr (≃⇒Cone⇒ (≃.sym F≃G) Lg Lf) ∘ arr (≃⇒Cone⇒ F≃G Lf Lg) ≈⟨ pullˡ (⇒-commute (≃⇒Cone⇒ (≃.sym F≃G) Lg Lf)) ⟩
        (⇐.η j ∘ Lg.proj j) ∘ arr (≃⇒Cone⇒ F≃G Lf Lg)                         ≈⟨ pullʳ (⇒-commute (≃⇒Cone⇒ F≃G Lf Lg)) ⟩
        ⇐.η j ∘ ⇒.η j ∘ Lf.proj j                                             ≈⟨ cancelˡ (iso.isoˡ j) ⟩
        Lf.proj j                                                             ∎
      }
    ; isoʳ = Lg.terminal.⊤-id record
      { commute = λ {j} → begin
        Lg.proj j ∘ arr (≃⇒Cone⇒ F≃G Lf Lg) ∘ arr (≃⇒Cone⇒ (≃.sym F≃G) Lg Lf) ≈⟨ pullˡ (⇒-commute (≃⇒Cone⇒ F≃G Lf Lg)) ⟩
        (⇒.η j ∘ Lf.proj j) ∘ arr (≃⇒Cone⇒ (≃.sym F≃G) Lg Lf)                 ≈⟨ pullʳ (⇒-commute (≃⇒Cone⇒ (≃.sym F≃G) Lg Lf)) ⟩
        ⇒.η j ∘ ⇐.η j ∘ Lg.proj j                                             ≈⟨ cancelˡ (iso.isoʳ j) ⟩
        Lg.proj j                                                             ∎
      }
    }
  }
  where open Con.Cone⇒ renaming (commute to ⇒-commute)
        module Lf = Lim.Limit Lf
        module Lg = Lim.Limit Lg
        open NaturalIsomorphism F≃G


module Loc (K : Set) where

open import Basics
open import Pr
open import Nom

data Loc : Set where
  EL : Loc
  _*_ : Loc -> K -> Loc

infixl 50 _*_

data _!_ : Loc -> K -> Set where
  top : {L : Loc}{S : K} -> (L * S) ! S
  pop : {L : Loc}{S T : K} -> L ! S -> (L * T) ! S

_<*_ : K -> Loc -> Loc
S <* EL = EL * S
S <* (L * T) = (S <* L) * T

max : {S : K}(L : Loc) -> (S <* L) ! S
max EL = top
max (L * T) = pop (max L)

_<_ : (S : K){L : Loc}{T : K} -> L ! T -> (S <* L) ! T
S < top = top
S < pop x = pop (S < x)

data MaxV (S : K)(L : Loc) : {T : K} -> (S <* L) ! T -> Set where
  isMax : MaxV S L (max L)
  isLow : {T : K}(x : L ! T) -> MaxV S L (S < x)

maxV : (S : K)(L : Loc){T : K}(x : (S <* L) ! T) -> MaxV S L x
maxV S EL top = isMax
maxV S EL (pop ())
maxV S (L * T) top = isLow top
maxV S (L * T) (pop x) with maxV S L x
maxV S (L * T) (pop .(max L)) | isMax = isMax
maxV S (L * T) (pop .(S < x)) | isLow x = isLow (pop x)

_bar_ : (L : Loc){S : K} -> L ! S -> Loc
EL bar ()
(L * S) bar top = L
(L * S) bar (pop v) = (L bar v) * S

infixl 50 _bar_

_thin_ : {L : Loc}{S T : K}(x : L ! S) -> (L bar x) ! T -> L ! T
top thin y = pop y
(pop x) thin top = top
(pop x) thin (pop y) = pop (x thin y)

data VarQV {L : Loc}{S : K}(x : L ! S) : {T : K} -> (L ! T) -> Set where
  vSame : VarQV x x
  vDiff : {T : K}(y : (L bar x) ! T) -> VarQV x (x thin y)

varQV : {L : Loc}{S T : K}(x : L ! S)(y : L ! T) -> VarQV x y
varQV top top = vSame
varQV top (pop y) = vDiff y
varQV (pop x) top = vDiff top
varQV (pop x) (pop y) with varQV x y
varQV (pop x) (pop .x) | vSame = vSame
varQV (pop x) (pop .(x thin y)) | vDiff y = vDiff (pop y)


{-# OPTIONS --without-K #-}
open import Type
open import Type.Identities
open import Level.NP
open import Data.Two
open import Function
open import Function.Extensionality
open import Data.Product
open import Data.Sum
open import Data.Fin
open import Relation.Binary.PropositionalEquality.NP using (_≡_; refl; !_; _∙_)
open import Relation.Binary.Sum
open import HoTT

open import Explore.Sum
open import Explore.Core
open import Explore.Properties
open import Explore.Explorable

module Explore.Two where

module _ {ℓ} where
    𝟚ᵉ : Explore ℓ 𝟚
    𝟚ᵉ _ _∙_ f = f 0₂ ∙ f 1₂

    𝟚ⁱ : ∀ {p} → ExploreInd p 𝟚ᵉ
    𝟚ⁱ _ _ _P∙_ Pf = Pf 0₂ P∙ Pf 1₂

module _ {ℓ₁ ℓ₂ ℓᵣ} {R : 𝟚 → 𝟚 → ★₀} {r0 : R 0₂ 0₂}{r1 : R 1₂ 1₂} where
    ⟦𝟚ᵉ⟧ : ⟦Explore⟧ {ℓ₁} {ℓ₂} ℓᵣ R 𝟚ᵉ 𝟚ᵉ
    ⟦𝟚ᵉ⟧ _ _ _∙ᵣ_ fᵣ = fᵣ r0 ∙ᵣ fᵣ r1

module 𝟚ⁱ = FromExploreInd 𝟚ⁱ
open 𝟚ⁱ public using ()
  renaming (sum to 𝟚ˢ
           ;product to 𝟚ᵖ
           ;reify to 𝟚ʳ
           ;unfocus to 𝟚ᵘ
           )

module _ {ℓ} where
    module _ {{_ : UA}}{{_ : FunExt}} where
        Σᵉ𝟚-ok : Adequate-Σ {ℓ} (Σᵉ 𝟚ᵉ)
        Σᵉ𝟚-ok _ = ! Σ𝟚-⊎

        Πᵉ𝟚-ok : Adequate-Π {ℓ} (Πᵉ 𝟚ᵉ)
        Πᵉ𝟚-ok _ = ! Π𝟚-×

    𝟚ˡ : Lookup {ℓ} 𝟚ᵉ
    𝟚ˡ = proj

    𝟚ᶠ : Focus {ℓ} 𝟚ᵉ
    𝟚ᶠ (0₂ , x) = inj₁ x
    𝟚ᶠ (1₂ , x) = inj₂ x

explore𝟚     = 𝟚ᵉ
explore𝟚-ind = 𝟚ⁱ
lookup𝟚      = 𝟚ˡ
reify𝟚       = 𝟚ʳ
focus𝟚       = 𝟚ᶠ
unfocus𝟚     = 𝟚ᵘ
sum𝟚         = 𝟚ˢ

module _ {{_ : UA}}{{_ : FunExt}} where
    open Adequacy _≡_
    𝟚ˢ-ok : Adequate-sum 𝟚ˢ
    𝟚ˢ-ok f = ! (Σ𝟚-⊎ ∙ Fin-⊎-+)


------------------------------------------------------------------------
-- The Agda standard library
--
-- The extensional sublist relation over setoid equality.
------------------------------------------------------------------------

{-# OPTIONS --without-K --safe #-}

open import Relation.Binary

module Data.List.Relation.Binary.Subset.Setoid
  {c ℓ} (S : Setoid c ℓ) where

open import Data.List using (List)
open import Data.List.Membership.Setoid S using (_∈_)
open import Level using (_⊔_)
open import Relation.Nullary using (¬_)

open Setoid S renaming (Carrier to A)

------------------------------------------------------------------------
-- Definitions

infix 4 _⊆_ _⊈_

_⊆_ : Rel (List A) (c ⊔ ℓ)
xs ⊆ ys = ∀ {x} → x ∈ xs → x ∈ ys

_⊈_ : Rel (List A) (c ⊔ ℓ)
xs ⊈ ys = ¬ xs ⊆ ys


{-# OPTIONS --safe #-}
module Cubical.Categories.Instances.Rings where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Function

open import Cubical.Algebra.Ring

open import Cubical.Categories.Category

open Category
open RingHoms

RingsCategory : ∀ {ℓ} → Category (ℓ-suc ℓ) ℓ
ob RingsCategory       = Ring _
Hom[_,_] RingsCategory = RingHom
id RingsCategory {R}   = idRingHom R
_⋆_ RingsCategory      = compRingHom
⋆IdL RingsCategory     = compIdRingHom
⋆IdR RingsCategory     = idCompRingHom
⋆Assoc RingsCategory   = compAssocRingHom
isSetHom RingsCategory = isSetRingHom _ _


{-# OPTIONS --without-K #-}
-- Lifting the dependent axiom of choice to sums and products

open import Type
open import Function.NP
open import Function.Extensionality
open import Data.Nat
open import Data.Product
open import Data.Fin using (Fin)
open import Explore.Core
open import Explore.Properties
open import Relation.Binary.PropositionalEquality.NP
open import Type.Identities
open import HoTT
open ≡-Reasoning
open Adequacy _≡_

module Explore.BigDistr
                {A B          : ★₀}
                {Πᴬ           : Product A}
                (adequate-Πᴬ  : Adequate-product Πᴬ)
                {Σᴮ           : Sum B}
                (adequate-Σᴮ  : Adequate-sum Σᴮ)
                {Σᴬᴮ          : Sum (A → B)}
                (adequate-Σᴬᴮ : Adequate-sum Σᴬᴮ)
                (F            : A → B → ℕ)
                {{_           : UA}}
                {{_           : FunExt}}
                where

big-distr : Πᴬ (Σᴮ ∘ F) ≡ Σᴬᴮ λ f → Πᴬ (F ˢ f)
big-distr =
  Fin-injective
  ( Fin (Πᴬ (Σᴮ ∘ F))
  ≡⟨ adequate-Πᴬ (Σᴮ ∘ F) ⟩
    Π A (Fin ∘ Σᴮ ∘ F)
  ≡⟨ Π=′ A (adequate-Σᴮ ∘ F) ⟩
    Π A (λ x → Σ B (Fin ∘ F x))
  ≡⟨ ΠΣ-comm ⟩
    Σ (Π A (const B)) (λ f → Π A (λ x → Fin (F x (f x))))
  ≡⟨ Σ=′ (A → B) (λ f → ! (adequate-Πᴬ (F ˢ f))) ⟩
    Σ (A → B) (λ f → Fin (Πᴬ (F ˢ f)))
  ≡⟨ ! (adequate-Σᴬᴮ (λ f → Πᴬ (F ˢ f))) ⟩
    Fin (Σᴬᴮ (λ f → Πᴬ (F ˢ f)))
  ∎)


-- {-# OPTIONS --show-implicit #-}
-- {-# OPTIONS -v term:20 -v term.function:30 #-}

-- Andreas, 2015-05-31, issue reported by Bruno Bianchi

module Issue1530 {A : Set}(_<=_ : A -> A -> Set) where

open import Common.List
open import Issue1530.Bound _<=_

data OList : Bound -> Set where
  nil : {b : Bound} -> OList b
  cons : {b : Bound}{x : A} -> LeB b (val x) -> OList (val x) -> OList b

forget : {b : Bound} -> OList b -> List A
forget nil = []
forget (cons {x = x} _ xs) = x ∷ forget xs

data Sorted : List A -> Set where
  empty : Sorted []
  single : (x : A) -> Sorted (x ∷ [])
  step : {x y : A}{ys : List A} -> x <= y -> Sorted (y ∷ ys) -> Sorted (x ∷ y ∷ ys)

olist-sorted : {b : Bound}(xs : OList b) -> Sorted (forget xs)
olist-sorted nil = empty
olist-sorted (cons {x = x} _ nil) = single x
olist-sorted (cons b<=x (cons (lexy x<=y) ys)) = step x<=y (olist-sorted (cons (lexy x<=y) ys))

-- should termination check

-- problem WAS: TermCheck did not *deeply* resolve constructor names on rhs in last clause.
-- TermCheck.reduceCon did not reduce the lexy on the rhs, as it was deep inside.


------------------------------------------------------------------------------
-- Discussion about the inductive approach
------------------------------------------------------------------------------

{-# OPTIONS --exact-split              #-}
{-# OPTIONS --no-sized-types           #-}
{-# OPTIONS --no-universe-polymorphism #-}
{-# OPTIONS --without-K                #-}

-- Andrés: From our discussion about the inductive approach, can I
-- conclude that it is possible to rewrite the proofs using pattern
-- matching on _≡_, by proofs using only subst, because the types
-- associated with these proofs haven't proof terms?

-- Peter: Yes, provided the RHS of the definition does not refer to the
-- function defined, i e, there is no recursion.

module FOT.FOTC.InductiveApproach.Recursion where

open import Common.FOL.Relation.Binary.EqReasoning

open import FOTC.Base
open import FOTC.Base.PropertiesI
open import FOTC.Data.Nat
open import FOTC.Data.Nat.PropertiesI

------------------------------------------------------------------------------

-- foo is recursive and we pattern matching on _≡_.
foo : ∀ {m n} → N m → m ≡ n → N (m + n)
foo nzero refl = subst N (sym (+-leftIdentity zero)) nzero
foo (nsucc {m} Nm) refl = subst N helper (nsucc (nsucc (foo Nm refl)))
  where
  helper : succ₁ (succ₁ (m + m)) ≡ succ₁ m + succ₁ m
  helper =
    succ₁ (succ₁ (m + m)) ≡⟨ succCong (sym (+-Sx m m))  ⟩
    succ₁ (succ₁ m + m)   ≡⟨ succCong (+-comm (nsucc Nm) Nm) ⟩
    succ₁ (m + succ₁ m)   ≡⟨ sym (+-Sx m (succ₁ m)) ⟩
    succ₁ m + succ₁ m     ∎

-- foo' is recursive and we only use subst.
foo' : ∀ {m n} → N m → m ≡ n → N (m + n)
foo' {n = n} nzero h = subst N (sym (+-leftIdentity n)) (subst N h nzero)
foo' {n = n} (nsucc {m} Nm) h = subst N helper (nsucc (nsucc (foo' Nm refl)))
  where
  helper : succ₁ (succ₁ (m + m)) ≡ succ₁ m + n
  helper =
    succ₁ (succ₁ (m + m)) ≡⟨ succCong (sym (+-Sx m m))  ⟩
    succ₁ (succ₁ m + m)   ≡⟨ succCong (+-comm (nsucc Nm) Nm) ⟩
    succ₁ (m + succ₁ m)   ≡⟨ sym (+-Sx m (succ₁ m)) ⟩
    succ₁ m + succ₁ m     ≡⟨ +-rightCong h ⟩
    succ₁ m + n           ∎


------------------------------------------------------------------------
-- The Agda standard library
--
-- Any predicate transformer for fresh lists
------------------------------------------------------------------------

{-# OPTIONS --without-K --safe #-}

module Data.List.Fresh.Relation.Unary.Any where

open import Level using (Level; _⊔_; Lift)
open import Data.Empty
open import Data.Product using (∃; _,_; -,_)
open import Data.Sum.Base using (_⊎_; [_,_]′; inj₁; inj₂)
open import Function.Equivalence using (_⇔_; equivalence)
open import Relation.Nullary
import Relation.Nullary.Decidable as Dec
open import Relation.Nullary.Sum using (_⊎-dec_)
open import Relation.Unary  as U
open import Relation.Binary as B using (Rel)

open import Data.List.Fresh using (List#; []; cons; _∷#_; _#_)

private
  variable
    a p q r : Level
    A : Set a

module _ {A : Set a} {R : Rel A r} (P : Pred A p) where

  data Any : List# A R → Set (p ⊔ a ⊔ r) where
    here  : ∀ {x xs pr} → P x → Any (cons x xs pr)
    there : ∀ {x xs pr} → Any xs → Any (cons x xs pr)

module _ {R : Rel A r} {P : Pred A p} {x} {xs : List# A R} {pr} where

  head : ¬ Any P xs → Any P (cons x xs pr) → P x
  head ¬tail (here p)   = p
  head ¬tail (there ps) = ⊥-elim (¬tail ps)

  tail : ¬ P x → Any P (cons x xs pr) → Any P xs
  tail ¬head (here p)   = ⊥-elim (¬head p)
  tail ¬head (there ps) = ps

  toSum : Any P (cons x xs pr) → P x ⊎ Any P xs
  toSum (here p) = inj₁ p
  toSum (there ps) = inj₂ ps

  fromSum : P x ⊎ Any P xs → Any P (cons x xs pr)
  fromSum = [ here , there ]′

  ⊎⇔Any : (P x ⊎ Any P xs) ⇔ Any P (cons x xs pr)
  ⊎⇔Any = equivalence fromSum toSum

module _ {R : Rel A r} {P : Pred A p} {Q : Pred A q} where

  map : {xs : List# A R} → ∀[ P ⇒ Q ] → Any P xs → Any Q xs
  map p⇒q (here p)  = here (p⇒q p)
  map p⇒q (there p) = there (map p⇒q p)

module _ {R : Rel A r} {P : Pred A p} where

  witness : {xs : List# A R} → Any P xs → ∃ P
  witness (here p)   = -, p
  witness (there ps) = witness ps

  remove   : (xs : List# A R) → Any P xs → List# A R
  remove-# : ∀ {x} {xs : List# A R} p → x # xs → x # (remove xs p)

  remove (_ ∷# xs)      (here _)  = xs
  remove (cons x xs pr) (there k) = cons x (remove xs k) (remove-# k pr)

  remove-# (here x)  (p , ps) = ps
  remove-# (there k) (p , ps) = p , remove-# k ps

infixl 4 _─_
_─_ = remove

module _ {R : Rel A r} {P : Pred A p} (P? : Decidable P) where

  any? : (xs : List# A R) → Dec (Any P xs)
  any? []        = no (λ ())
  any? (x ∷# xs) = Dec.map ⊎⇔Any (P? x ⊎-dec any? xs)


module Metalogic.Classical.Propositional.ProofSystem {ℓₚ} (Proposition : Set(ℓₚ)) where

import      Lvl
open import Data hiding (empty)
import      Data.List
open        Data.List using (List ; ∅ ; _⊰_ ; _++_ ; [_ ; _])
open        Data.List.Notation
import      Data.List.Relation.Membership
import      Data.List.Relation.Membership.Proofs
open import Metalogic.Classical.Propositional.Syntax(Proposition)
open import Functional

open        Data.List.Relation.Membership{ℓₚ} {Formula}
open        Data.List.Relation.Membership.Proofs{ℓₚ} {Formula}

module Meta where
  data _⊢_ : List(Formula) → Formula → Set(ℓₚ) where -- TODO: Reduce the number of rules
    [⊤]-intro : ∀{Γ} → (Γ ⊢ ⊤)

    [⊥]-intro : ∀{Γ}{φ} → (φ ∈ Γ) → ((¬ φ) ∈ Γ) → (Γ ⊢ ⊥)
    [⊥]-elim  : ∀{Γ}{φ} → (⊥ ∈ Γ) → (Γ ⊢ φ)

    [¬]-intro : ∀{Γ}{φ} → ((φ ∈ Γ) → (Γ ⊢ ⊥)) → (Γ ⊢ (¬ φ))
    [¬]-elim  : ∀{Γ}{φ} → (((¬ φ) ∈ Γ) → (Γ ⊢ ⊥)) → (Γ ⊢ φ)

    [∧]-intro : ∀{Γ}{φ₁ φ₂} → (φ₁ ∈ Γ) → (φ₂ ∈ Γ) → (Γ ⊢ (φ₁ ∧ φ₂))
    [∧]-elimₗ  : ∀{Γ}{φ₁ φ₂} → ((φ₁ ∧ φ₂) ∈ Γ) → (Γ ⊢ φ₁)
    [∧]-elimᵣ  : ∀{Γ}{φ₁ φ₂} → ((φ₁ ∧ φ₂) ∈ Γ) → (Γ ⊢ φ₂)

    [∨]-introₗ : ∀{Γ}{φ₁ φ₂} → (φ₁ ∈ Γ) → (Γ ⊢ (φ₁ ∨ φ₂))
    [∨]-introᵣ : ∀{Γ}{φ₁ φ₂} → (φ₂ ∈ Γ) → (Γ ⊢ (φ₁ ∨ φ₂))
    [∨]-elim   : ∀{Γ}{φ₁ φ₂ φ₃} → ((φ₁ ∈ Γ) → (Γ ⊢ φ₃)) → ((φ₂ ∈ Γ) → (Γ ⊢ φ₃)) → ((φ₁ ∨ φ₂) ∈ Γ) → (Γ ⊢ φ₃)

    [⇒]-intro : ∀{Γ}{φ₁ φ₂} → ((φ₁ ∈ Γ) → (Γ ⊢ φ₂)) → (Γ ⊢ (φ₁ ⇒ φ₂))
    [⇒]-elim  : ∀{Γ}{φ₁ φ₂} → ((φ₁ ⇒ φ₂) ∈ Γ) → (φ₁ ∈ Γ) → (Γ ⊢ φ₂)

  [¬¬]-elim : ∀{Γ}{φ} → ((¬ ¬ φ) ∈ Γ) → (Γ ⊢ φ)
  [¬¬]-elim{Γ}{φ} ([¬¬φ]-in) =
    ([¬]-elim{Γ}{φ} ([¬φ]-in ↦
      ([⊥]-intro{Γ}{¬ φ}
        ([¬φ]-in)
        ([¬¬φ]-in)
      )
    ))

  _⊬_ : List(Formula) → Formula → Set(ℓₚ)
  _⊬_ Γ φ = (_⊢_ Γ φ) → Empty{ℓₚ}

  -- Consistency
  Inconsistent : List(Formula) → Set(ℓₚ)
  Inconsistent Γ = (Γ ⊢ ⊥)

  Consistent : List(Formula) → Set(ℓₚ)
  Consistent Γ = (Γ ⊬ ⊥)

module TruthTables where
  open Meta

-- Natural deduction proof trees.
-- This is a proof system that should reflect the semantic truth of formulas.
module NaturalDeduction where
  -- A `Tree` is associated with a formula.
  -- [Proposition without assumptions (Axiom)]
  --   Represented by the type `Tree(φ)`.
  --   It means that a tree with the formula φ at the bottom exists (is constructable).
  --   This represents that the formula φ is provable in the natural deduction proof system.
  -- [Proposition with an assumption]
  --   Represented by the type `Tree(φ₁) → Tree(φ₂)`.
  --   It means that a tree with φ₁ as a leaf and φ₂ at the bottom exists (is constructable).
  --   This represents that the formula φ₂ is provable if one can assume the formula φ₁.
  -- The constructors of `Tree` are all the possible ways to construct a natural deduction proof tree.
  -- If a tree with a certain formula cannot be constructed, then it means that the formula is not provable.
  {-# NO_POSITIVITY_CHECK #-} -- TODO: Could this be a problem? Maybe not? Classical logic is supposed to be consistent, but maybe that does not have anything to do with this?
  data Tree : Formula → Set(ℓₚ) where
    [⊤]-intro : Tree(⊤)

    [⊥]-intro : ∀{φ} → Tree(φ) → Tree(¬ φ) → Tree(⊥)

    [¬]-intro : ∀{φ} → (Tree(φ) → Tree(⊥)) → Tree(¬ φ)
    [¬]-elim  : ∀{φ} → (Tree(¬ φ) → Tree(⊥)) → Tree(φ)

    [∧]-intro : ∀{φ₁ φ₂} → Tree(φ₁) → Tree(φ₂) → Tree(φ₁ ∧ φ₂)
    [∧]-elimₗ  : ∀{φ₁ φ₂} → Tree(φ₁ ∧ φ₂) → Tree(φ₁)
    [∧]-elimᵣ  : ∀{φ₁ φ₂} → Tree(φ₁ ∧ φ₂) → Tree(φ₂)

    [∨]-introₗ : ∀{φ₁ φ₂} → Tree(φ₁) → Tree(φ₁ ∨ φ₂)
    [∨]-introᵣ : ∀{φ₁ φ₂} → Tree(φ₂) → Tree(φ₁ ∨ φ₂)
    [∨]-elim  : ∀{φ₁ φ₂ φ₃} → (Tree(φ₁) → Tree(φ₃)) → (Tree(φ₂) → Tree(φ₃)) → Tree(φ₁ ∨ φ₂) → Tree(φ₃)

    [⇒]-intro : ∀{φ₁ φ₂} → (Tree(φ₁) → Tree(φ₂)) → Tree(φ₁ ⇒ φ₂)
    [⇒]-elim  : ∀{φ₁ φ₂} → Tree(φ₁ ⇒ φ₂) → Tree(φ₁) → Tree(φ₂)

  -- Double negated proposition is positive.
  [¬¬]-elim : ∀{φ} → Tree(¬ (¬ φ)) → Tree(φ)
  [¬¬]-elim nna = [¬]-elim(na ↦ [⊥]-intro na nna)

  -- A contradiction can derive every formula.
  [⊥]-elim : ∀{φ} → Tree(⊥) → Tree(φ)
  [⊥]-elim bottom = [¬]-elim(_ ↦ bottom)

  -- List of natural deduction proof trees.
  -- A `Trees` is associated with a list of formulas.
  -- If all formulas in the list can be constructed, then all the formulas in the list are provable.
  -- This is used to express (⊢) using the usual conventions in formal logic.
  -- Trees(Γ) is the statement that all formulas in Γ have proof trees.
  Trees : List(Formula) → Set(ℓₚ)
  Trees(Γ) = (∀{γ} → (γ ∈ Γ) → Tree(γ))

  module Trees where
    tree : ∀{Γ}{φ} → Trees(Γ) → (φ ∈ Γ) → Tree(φ)
    tree f(x) = f(x)

    empty : Trees(∅)
    empty ()

    singleton : ∀{φ} → Tree(φ) → Trees([ φ ])
    singleton (φ-tree) (use) = φ-tree
    singleton (φ-tree) (skip ())

    -- TODO: This could possibly be generalized to: ∀{Γ₁ Γ₂}{F : T → Set} → (∀{a} → (a ∈ Γ₁) → (a ∈ Γ₂)) → ((∀{γ} → (γ ∈ Γ₂) → F(γ)) → (∀{γ} → (γ ∈ Γ₁) → F(γ)))
    from-[∈] : ∀{Γ₁ Γ₂} → (∀{a} → (a ∈ Γ₁) → (a ∈ Γ₂)) → (Trees(Γ₂) → Trees(Γ₁))
    from-[∈] (f) (Γ₂-trees) {γ} = liftᵣ (f{γ}) (Γ₂-trees)

    push : ∀{Γ}{φ} → Tree(φ) → Trees(Γ) → Trees(φ ⊰ Γ)
    push (φ-tree) (Γ-tree) (use) = φ-tree
    push (φ-tree) (Γ-tree) (skip membership) = Γ-tree (membership)

    pop : ∀{Γ}{φ} → Trees(φ ⊰ Γ) → Trees(Γ)
    pop = from-[∈] (skip)

    first : ∀{Γ}{φ} → Trees(φ ⊰ Γ) → Tree(φ)
    first(f) = f(use)

    -- TODO: Could be removed because liftᵣ is easier to use. ALthough a note/tip should be written for these purposes.
    -- formula-weaken : ∀{ℓ}{T : Set(ℓ)}{Γ₁ Γ₂} → (Trees(Γ₁) → Trees(Γ₂)) → (Trees(Γ₂) → T) → (Trees(Γ₁) → T)
    -- formula-weaken = liftᵣ

    [++]-commute : ∀{Γ₁ Γ₂} → Trees(Γ₁ ++ Γ₂) → Trees(Γ₂ ++ Γ₁)
    [++]-commute {Γ₁}{Γ₂} (trees) = trees ∘ ([∈][++]-commute{_}{Γ₂}{Γ₁})

    [++]-left : ∀{Γ₁ Γ₂} → Trees(Γ₁ ++ Γ₂) → Trees(Γ₁)
    [++]-left {Γ₁}{Γ₂} (trees) ([∈]-[Γ₁]) = trees ([∈][++]-expandᵣ {_}{Γ₁}{Γ₂} [∈]-[Γ₁])

    [++]-right : ∀{Γ₁ Γ₂} → Trees(Γ₁ ++ Γ₂) → Trees(Γ₂)
    [++]-right {Γ₁}{Γ₂} (trees) ([∈]-[Γ₂]) = trees ([∈][++]-expandₗ {_}{Γ₁}{Γ₂} [∈]-[Γ₂])

    [++]-deduplicate : ∀{Γ} → Trees(Γ ++ Γ) → Trees(Γ)
    [++]-deduplicate {Γ} (trees) {γ} = liftᵣ([∈][++]-expandₗ {γ}{Γ}{Γ})(trees{γ})

    [⊰]-reorderₗ : ∀{Γ₁ Γ₂}{φ} → Trees(Γ₁ ++ (φ ⊰ Γ₂)) → Trees(φ ⊰ (Γ₁ ++ Γ₂))
    [⊰]-reorderₗ {Γ₁}{Γ₂}{φ} (Γ₁φΓ₂-trees) = push (φ-tree) ([++]-commute{Γ₂}{Γ₁} Γ₂Γ₁-trees) where
      φΓ₂Γ₁-trees : Trees(φ ⊰ (Γ₂ ++ Γ₁))
      φΓ₂Γ₁-trees = [++]-commute{Γ₁}{φ ⊰ Γ₂} (Γ₁φΓ₂-trees)

      φ-tree : Tree(φ)
      φ-tree = first{Γ₂ ++ Γ₁}{φ} (φΓ₂Γ₁-trees)

      Γ₂Γ₁-trees : Trees(Γ₂ ++ Γ₁)
      Γ₂Γ₁-trees = pop{Γ₂ ++ Γ₁}{φ} (φΓ₂Γ₁-trees)
    -- Γ₁ ++ (φ ⊰ Γ₂) //assumption
    -- (φ ⊰ Γ₂) ++ Γ₁ //Trees.[++]-commute
    -- φ ⊰ (Γ₂ ++ Γ₁) //Definition: (++)
    -- φ ⊰ (Γ₁ ++ Γ₂) //Trees.[++]-commute

    push-many : ∀{Γ₁ Γ₂} → Trees(Γ₁) → Trees(Γ₂) → Trees(Γ₁ ++ Γ₂)
    push-many{∅}     {Γ₂} ([∅]-trees)    ([Γ₂]-trees) = [Γ₂]-trees
    push-many{φ ⊰ Γ₁}{Γ₂} ([φ⊰Γ₁]-trees) ([Γ₂]-trees) = [⊰]-reorderₗ{Γ₁}{Γ₂}{φ} (push-many{Γ₁}{φ ⊰ Γ₂} (pop [φ⊰Γ₁]-trees) (push (first [φ⊰Γ₁]-trees) [Γ₂]-trees))

  -- Derivability
  -- Proof of: If there exists a tree for every formula in Γ, then there exists a tree for the formula φ.
  data _⊢_ (Γ : List(Formula)) (φ : Formula) : Set(ℓₚ) where
    [⊢]-construct : (Trees(Γ) → Tree(φ)) → (Γ ⊢ φ)

  module Theorems where
    [⊢]-from-trees : ∀{Γ₁ Γ₂}{φ} → (Trees(Γ₂) → Trees(Γ₁)) → (Γ₁ ⊢ φ) → (Γ₂ ⊢ φ)
    [⊢]-from-trees (trees-fn) ([⊢]-construct (Γ₁⊢φ)) = [⊢]-construct ((Γ₁⊢φ) ∘ (trees-fn))

    [⊢]-formula-weaken : ∀{Γ}{φ₁ φ₂} → (Γ ⊢ φ₁) → ((φ₂ ⊰ Γ) ⊢ φ₁)
    [⊢]-formula-weaken {_}{_}{φ₂} = [⊢]-from-trees (Trees.pop {_}{φ₂})

    [⊢]-weakenₗ : ∀{Γ₂}{φ} → (Γ₂ ⊢ φ) → ∀{Γ₁} → ((Γ₁ ++ Γ₂) ⊢ φ)
    [⊢]-weakenₗ {_} {_} (Γ₂⊢φ) {∅}       = (Γ₂⊢φ)
    [⊢]-weakenₗ {Γ₂}{φ} (Γ₂⊢φ) {φ₂ ⊰ Γ₁} = [⊢]-formula-weaken {Γ₁ ++ Γ₂} ([⊢]-weakenₗ (Γ₂⊢φ) {Γ₁})

    [⊢]-reorder-[++] : ∀{Γ₁ Γ₂}{φ} → ((Γ₁ ++ Γ₂) ⊢ φ) → ((Γ₂ ++ Γ₁) ⊢ φ)
    [⊢]-reorder-[++] {Γ₁}{Γ₂} = [⊢]-from-trees (Trees.[++]-commute {Γ₂}{Γ₁})

    [⊢]-apply : ∀{Γ}{φ} → (Γ ⊢ φ) → Trees(Γ) → Tree(φ)
    [⊢]-apply ([⊢]-construct [Γ]⊢[φ]) (Γ-trees) = ([Γ]⊢[φ]) (Γ-trees)

    [⊢]-apply-first : ∀{Γ}{φ₁ φ₂} → Tree(φ₁) → ((φ₁ ⊰ Γ) ⊢ φ₂) → (Γ ⊢ φ₂)
    [⊢]-apply-first ([φ₁]-tree) ([⊢]-construct ([φ₁⊰Γ]⊢[φ₂])) =
      [⊢]-construct([Γ]⊢[φ₂] ↦
        ([φ₁⊰Γ]⊢[φ₂]) (Trees.push ([φ₁]-tree) (\{γ} → [Γ]⊢[φ₂] {γ}))
      )

    [⊢]-apply-many : ∀{Γ₁ Γ₂}{φ} → Trees(Γ₁) → ((Γ₁ ++ Γ₂) ⊢ φ) → (Γ₂ ⊢ φ)
    [⊢]-apply-many ([Γ₁]-trees) ([⊢]-construct ([Γ₁++Γ₂]⊢[φ])) =
      [⊢]-construct([Γ₂]⊢[φ] ↦
        ([Γ₁++Γ₂]⊢[φ]) (Trees.push-many ([Γ₁]-trees) (\{γ} → [Γ₂]⊢[φ] {γ}))
      )

    [⊢]-reorderᵣ-[⊰] : ∀{Γ₁ Γ₂}{φ₁ φ₂} → ((Γ₁ ++ (φ₁ ⊰ Γ₂)) ⊢ φ₂) ← ((φ₁ ⊰ (Γ₁ ++ Γ₂)) ⊢ φ₂)
    [⊢]-reorderᵣ-[⊰] {Γ₁}{Γ₂}{φ₁} = [⊢]-from-trees (Trees.[⊰]-reorderₗ {Γ₁}{Γ₂}{φ₁})

    [⊢]-id : ∀{Γ}{φ} → (φ ∈ Γ) → (Γ ⊢ φ)
    [⊢]-id (φ-in) = [⊢]-construct ([∈]-proof ↦ [∈]-proof (φ-in))
    -- ((A → B) → B) → C
    -- f(g ↦ g(x))

    -- Almost like that cut rule.
    [⊢]-compose : ∀{Γ}{φ₁ φ₂} → (Γ ⊢ φ₁) → ((φ₁ ⊰ Γ) ⊢ φ₂) → (Γ ⊢ φ₂)
    [⊢]-compose ([Γ]⊢[φ₁]) ([φ₁Γ]⊢[φ₂]) =
      [⊢]-construct ([Γ]-trees ↦
        let [φ₁]-tree = [⊢]-apply ([Γ]⊢[φ₁]) (\{γ} → [Γ]-trees {γ})
            [Γ]⊢[φ₂]  = [⊢]-apply-first ([φ₁]-tree) ([φ₁Γ]⊢[φ₂])
        in [⊢]-apply ([Γ]⊢[φ₂]) (\{γ} → [Γ]-trees {γ})
      )
    -- TODO: ∀{Γ}{φ₁ φ₂} → (Γ ⊢ φ₁) → ((φ₁ ∈ Γ) → (Γ ⊢ φ₂)) → (Γ ⊢ φ₂) Provable?

    [⊢]-membership-precedingₗ : ∀{Γ}{φ₁ φ₂} → ((φ₁ ∈ Γ) → (Γ ⊢ φ₂)) ← ((φ₁ ⊰ Γ) ⊢ φ₂)
    [⊢]-membership-precedingₗ{Γ}{φ₁}{φ₂} ([φ₁Γ]⊢[φ₂]) ([φ₁]-in) =
      [⊢]-construct([Γ]-trees ↦
        [⊢]-apply ([φ₁Γ]⊢[φ₂]) (Trees.push ([Γ]-trees [φ₁]-in) (\{γ} → [Γ]-trees {γ}))
      )

    {- TODO: Maybe this is unprovable? And also false?
    postulate a : ∀{b : Set(ℓₚ)} → b
    [⊢]-membership-precedingᵣ : ∀{Γ}{φ₁ φ₂} → ((φ₁ ∈ Γ) → (Γ ⊢ φ₂)) → ((φ₁ ⊰ Γ) ⊢ φ₂)
    [⊢]-membership-precedingᵣ{Γ}{φ₁}{φ₂} (incl) =
      [⊢]-construct ([φ₁Γ]-trees ↦
        let [Γ]-trees = Trees.pop (\{γ} → [φ₁Γ]-trees {γ})
            [φ₁]-in = a
            [Γ]⊢[φ₂] = incl([φ₁]-in)
        in [⊢]-apply ([Γ]⊢[φ₂]) ([Γ]-trees)
      )
    -}

    [⊢][⊤]-intro : ∀{Γ} → (Γ ⊢ ⊤)
    [⊢][⊤]-intro =
      [⊢]-construct(const [⊤]-intro)

    [⊢][⊥]-intro : ∀{Γ}{φ} → (φ ∈ Γ) → ((¬ φ) ∈ Γ) → (Γ ⊢ ⊥)
    [⊢][⊥]-intro ([φ]-in) ([¬φ]-in) =
      [⊢]-construct(assumption-trees ↦
        ([⊥]-intro
          (assumption-trees ([φ]-in))
          (assumption-trees ([¬φ]-in))
        )
      )

    [⊢][⊥]-elim : ∀{Γ}{φ} → (⊥ ∈ Γ) → (Γ ⊢ φ)
    [⊢][⊥]-elim ([⊥]-in) =
      [⊢]-construct(assumption-trees ↦
        ([⊥]-elim
          (assumption-trees ([⊥]-in))
        )
      )

    [⊢][¬]-intro : ∀{Γ}{φ} → ((φ ⊰ Γ) ⊢ ⊥) → (Γ ⊢ (¬ φ))
    [⊢][¬]-intro ([⊢]-construct φΓ⊢⊥) =
      [⊢]-construct(assumption-trees ↦
        ([¬]-intro (φ-tree ↦
          (φΓ⊢⊥) (Trees.push (φ-tree) (\{γ} → assumption-trees {γ}))
        ))
      )

    [⊢][¬]-elim : ∀{Γ}{φ} → (((¬ φ) ⊰ Γ) ⊢ ⊥) → (Γ ⊢ φ)
    [⊢][¬]-elim ([⊢]-construct ¬φΓ⊢⊥) =
      [⊢]-construct(assumption-trees ↦
        ([¬]-elim (φ-tree ↦
          (¬φΓ⊢⊥) (Trees.push (φ-tree) (\{γ} → assumption-trees {γ}))
        ))
      )

    [⊢][∧]-intro : ∀{Γ}{φ₁ φ₂} → (φ₁ ∈ Γ) → (φ₂ ∈ Γ) → (Γ ⊢ (φ₁ ∧ φ₂))
    [⊢][∧]-intro ([φ₁]-in) ([φ₂]-in) =
      [⊢]-construct(assumption-trees ↦
        ([∧]-intro
          (assumption-trees ([φ₁]-in))
          (assumption-trees ([φ₂]-in))
        )
      )

    [⊢][∧]-elimₗ : ∀{Γ}{φ₁ φ₂} → ((φ₁ ∧ φ₂) ∈ Γ) → (Γ ⊢ φ₁)
    [⊢][∧]-elimₗ ([φ₁∧φ₂]-in) =
      [⊢]-construct(assumption-trees ↦
        [∧]-elimₗ (assumption-trees ([φ₁∧φ₂]-in))
      )

    [⊢][∧]-elimᵣ : ∀{Γ}{φ₁ φ₂} → ((φ₁ ∧ φ₂) ∈ Γ) → (Γ ⊢ φ₂)
    [⊢][∧]-elimᵣ ([φ₁∧φ₂]-in) =
      [⊢]-construct(assumption-trees ↦
        [∧]-elimᵣ (assumption-trees ([φ₁∧φ₂]-in))
      )

    [⊢][∨]-introₗ : ∀{Γ}{φ₁ φ₂} → (φ₁ ∈ Γ) → (Γ ⊢ (φ₁ ∨ φ₂))
    [⊢][∨]-introₗ ([φ₁]-in) =
      [⊢]-construct(assumption-trees ↦
        [∨]-introₗ (assumption-trees ([φ₁]-in))
      )

    [⊢][∨]-introᵣ : ∀{Γ}{φ₁ φ₂} → (φ₂ ∈ Γ) → (Γ ⊢ (φ₁ ∨ φ₂))
    [⊢][∨]-introᵣ ([φ₂]-in) =
      [⊢]-construct(assumption-trees ↦
        [∨]-introᵣ (assumption-trees ([φ₂]-in))
      )

    [⊢][∨]-elim : ∀{Γ₁ Γ₂}{φ₁ φ₂ φ₃} → ((φ₁ ⊰ Γ₁) ⊢ φ₃) → ((φ₂ ⊰ Γ₂) ⊢ φ₃) → (((φ₁ ∨ φ₂) ⊰ (Γ₁ ++ Γ₂)) ⊢ φ₃)
    [⊢][∨]-elim {Γ₁}{Γ₂} ([⊢]-construct φ₁Γ⊢φ₃) ([⊢]-construct φ₂Γ⊢φ₃) = [⊢]-construct
      (assumption-trees ↦ [∨]-elim
        (φ₁-tree ↦ (φ₁Γ⊢φ₃) (Trees.push (φ₁-tree) (Trees.[++]-left  {Γ₁}{Γ₂} (Trees.pop (\{γ} → assumption-trees {γ})))))
        (φ₂-tree ↦ (φ₂Γ⊢φ₃) (Trees.push (φ₂-tree) (Trees.[++]-right {Γ₁}{Γ₂} (Trees.pop (\{γ} → assumption-trees {γ})))))
        (assumption-trees (use))
      )

    [⊢][⇒]-intro : ∀{Γ}{φ₁ φ₂} → ((φ₁ ⊰ Γ) ⊢ φ₂) → (Γ ⊢ (φ₁ ⇒ φ₂))
    [⊢][⇒]-intro ([⊢]-construct φ₁Γ⊢φ₂) = [⊢]-construct
      (assumption-trees ↦ [⇒]-intro
        (φ-tree ↦ (φ₁Γ⊢φ₂) (Trees.push (φ-tree) (\{γ} → assumption-trees {γ})))
      )

    [⊢][⇒]-elim : ∀{Γ}{φ₁ φ₂} → ((φ₁ ⇒ φ₂) ∈ Γ) → (φ₁ ∈ Γ) → (Γ ⊢ φ₂)
    [⊢][⇒]-elim ([φ₁⇒φ₂]-in) ([φ₁]-in) =
      [⊢]-construct(assumption-trees ↦
        [⇒]-elim
          (assumption-trees ([φ₁⇒φ₂]-in))
          (assumption-trees ([φ₁]-in))
      )

    -- [⊢]-metaᵣ : ∀{a}{b} → _⊢_ a b → Meta._⊢_ a b
    -- [⊢]-metaₗ : ∀{a}{b} → _⊢_ a b ← Meta._⊢_ a b

    {-[⊢]-rules : Meta.[⊢]-rules (_⊢_)
    [⊢]-rules =
      record{
        [⊤]-intro  = \{Γ} → [⊢][⊤]-intro {Γ} ;
        [⊥]-intro  = \{Γ}{φ} → [⊢][⊥]-intro {Γ}{φ} ;
        [⊥]-elim   = \{Γ}{φ} → [⊢][⊥]-elim {Γ}{φ} ;
        [¬]-intro  = \{Γ}{φ} → [⊢][¬]-intro {Γ}{φ} ;
        [¬]-elim   = \{Γ}{φ} → [⊢][¬]-elim {Γ}{φ} ;
        [∧]-intro  = \{Γ}{φ₁}{φ₂} → [⊢][∧]-intro {Γ}{φ₁}{φ₂} ;
        [∧]-elimₗ  = \{Γ}{φ₁}{φ₂} → [⊢][∧]-elimₗ {Γ}{φ₁}{φ₂} ;
        [∧]-elimᵣ  = \{Γ}{φ₁}{φ₂} → [⊢][∧]-elimᵣ {Γ}{φ₁}{φ₂} ;
        [∨]-introₗ = \{Γ}{φ₁}{φ₂} → [⊢][∨]-introₗ {Γ}{φ₁}{φ₂} ;
        [∨]-introᵣ = \{Γ}{φ₁}{φ₂} → [⊢][∨]-introᵣ {Γ}{φ₁}{φ₂} ;
        [∨]-elim   = \{Γ₁}{Γ₂}{φ₁ φ₂ φ₃} → [⊢][∨]-elim {Γ₁}{Γ₂}{φ₁}{φ₂}{φ₃} ;
        [⇒]-intro  = \{Γ}{φ₁}{φ₂} → [⊢][⇒]-intro {Γ}{φ₁}{φ₂} ;
        [⇒]-elim   = \{Γ}{φ₁}{φ₂} → [⊢][⇒]-elim {Γ}{φ₁}{φ₂}
      }
    -}


{-# OPTIONS --safe #-}
module Cubical.Categories.Functor.Base where

open import Cubical.Foundations.Prelude

open import Cubical.Data.Sigma

open import Cubical.Categories.Category

private
  variable
    ℓC ℓC' ℓD ℓD' : Level

record Functor (C : Category ℓC ℓC') (D : Category ℓD ℓD') :
         Type (ℓ-max (ℓ-max ℓC ℓC') (ℓ-max ℓD ℓD')) where
  no-eta-equality

  open Category

  field
    F-ob  : C .ob → D .ob
    F-hom : {x y : C .ob} → C [ x , y ] → D [ F-ob x , F-ob y ]
    F-id  : {x : C .ob} → F-hom (C .id) ≡ D .id {x = F-ob x}
    F-seq : {x y z : C .ob} (f : C [ x , y ]) (g : C [ y , z ])
          → F-hom (f ⋆⟨ C ⟩ g) ≡ (F-hom f) ⋆⟨ D ⟩ (F-hom g)

  isFull = (x y : _) (F[f] : D [ F-ob x , F-ob y ]) → ∃[ f ∈ C [ x , y ] ] F-hom f ≡ F[f]
  isFaithful = (x y : _) (f g : C [ x , y ]) → F-hom f ≡ F-hom g → f ≡ g
  isEssentiallySurj = (d : D .ob) → Σ[ c ∈ C .ob ] CatIso D (F-ob c) d

private
  variable
    ℓ ℓ' : Level
    C D E : Category ℓ ℓ'

open Category
open Functor

-- Helpful notation

-- action on objects
infix 30 _⟅_⟆
_⟅_⟆ : (F : Functor C D)
     → C .ob
     → D .ob
_⟅_⟆ = F-ob

-- action on morphisms
infix 30 _⟪_⟫ -- same infix level as on objects since these will never be used in the same context
_⟪_⟫ : (F : Functor C D)
     → ∀ {x y}
     → C [ x , y ]
     → D [(F ⟅ x ⟆) , (F ⟅ y ⟆)]
_⟪_⟫ = F-hom

-- Functor constructions

𝟙⟨_⟩ : ∀ (C : Category ℓ ℓ') → Functor C C
𝟙⟨ C ⟩ .F-ob x    = x
𝟙⟨ C ⟩ .F-hom f   = f
𝟙⟨ C ⟩ .F-id      = refl
𝟙⟨ C ⟩ .F-seq _ _ = refl

-- functor composition
funcComp : ∀ (G : Functor D E) (F : Functor C D) → Functor C E
(funcComp G F) .F-ob c    = G ⟅ F ⟅ c ⟆ ⟆
(funcComp G F) .F-hom f   = G ⟪ F ⟪ f ⟫ ⟫
(funcComp G F) .F-id      = cong (G ⟪_⟫) (F .F-id) ∙ G .F-id
(funcComp G F) .F-seq f g = cong (G ⟪_⟫) (F .F-seq _ _) ∙ G .F-seq _ _

infixr 30 funcComp
syntax funcComp G F = G ∘F F

_^opF : Functor C D → Functor (C ^op) (D ^op)
(F ^opF) .F-ob      = F .F-ob
(F ^opF) .F-hom     = F .F-hom
(F ^opF) .F-id      = F .F-id
(F ^opF) .F-seq f g = F .F-seq g f


module WithInParModule (A : Set) where

data Nat : Set where
  zero : Nat
  suc  : Nat -> Nat

data Bool : Set where
  true : Bool
  false : Bool

isZero : Nat -> Bool
isZero zero = true
isZero (suc _) = false

f : Nat -> Nat
f n with isZero n
f n | true = zero
f n | false = suc zero

g : Nat -> Nat
g zero = zero
g (suc n) with g n
g (suc n) | zero  = n
g (suc n) | suc _ = n

data T : Set where
  tt : T

module A (x : T) where
  h : T
  h with x
  h | y = y

postulate
  C : T -> Set

test : C (A.h tt) -> C tt
test x = x


{-# OPTIONS --without-K --safe #-}
module Cats.Category.Constructions.Terminal where

open import Data.Product using (proj₁ ; proj₂)
open import Level

open import Cats.Category.Base

import Cats.Category.Constructions.Iso as Iso
import Cats.Category.Constructions.Unique as Unique

module Build {lo la l≈} (Cat : Category lo la l≈) where

  open Category Cat
  open Iso.Build Cat
  open Unique.Build Cat

  IsTerminal : Obj → Set (lo ⊔ la ⊔ l≈)
  IsTerminal ⊤ = ∀ X → ∃! X ⊤

  record HasTerminal : Set (lo ⊔ la ⊔ l≈) where
    field
      ⊤ : Obj
      isTerminal : IsTerminal ⊤

    ! : ∀ X → X ⇒ ⊤
    ! X = ∃!′.arr (isTerminal X)

    !-unique : ∀ {X} (f : X ⇒ ⊤) → ! X ≈ f
    !-unique {X} f = ∃!′.unique (isTerminal X) _

    ⇒⊤-unique : ∀ {X} (f g : X ⇒ ⊤) → f ≈ g
    ⇒⊤-unique f g = ≈.trans (≈.sym (!-unique f)) (!-unique g)

    ⊤-unique : ∀ {X} → IsTerminal X → X ≅ ⊤
    ⊤-unique {X} term = record
      { forth = ! X
      ; back = term ⊤ .∃!′.arr
      ; back-forth = ≈.trans (≈.sym (term X .∃!′.unique _)) (term X .∃!′.unique _)
      ; forth-back = ⇒⊤-unique _ _
      }

  terminal-unique : ∀ {X Y} → IsTerminal X → IsTerminal Y → X ≅ Y
  terminal-unique X-term Y-term
    = HasTerminal.⊤-unique (record { isTerminal = Y-term }) X-term

open Build public using (HasTerminal)

private
  open module Build′ {lo la l≈} {C : Category lo la l≈}
    = Build C public using (IsTerminal ; terminal-unique)


module Relation.Ternary.Separation.Morphisms where

open import Level
open import Function
open import Relation.Unary
open import Relation.Binary.PropositionalEquality
open import Relation.Ternary.Separation
open import Data.Product
open import Function using (_∘_)

record Morphism {a b} (A : Set a) (B : Set b)
  {{r : RawSep A}} {u} {{s₁ : IsUnitalSep r u}}
  {{rb : RawSep B}} : Set (a ⊔ suc b) where

  field
    j    : A → B

    -- j "commutes" with _⊎_
    j-⊎  : ∀ {Φ₁ Φ₂ Φ} → Φ₁ ⊎ Φ₂ ≣ Φ → j Φ₁ ⊎ j Φ₂ ≣ j Φ
    j-⊎⁻ : ∀ {Φ₁ Φ₂ Φ} → j Φ₁ ⊎ j Φ₂ ≣ Φ → ∃ λ Φ' → Φ ≡ j Φ' × Φ₁ ⊎ Φ₂ ≣ Φ'

  instance _ = s₁

  infixr 8 _⇒ⱼ_
  _⇒ⱼ_ : ∀ {p q} → Pred A p → Pred B q → Pred A _
  P ⇒ⱼ Q = P ⇒ (Q ∘ j)

  infixr 8 _─✴ⱼ_
  _─✴ⱼ_ : ∀ {p q} → Pred A p → Pred B q → Pred B _
  P ─✴ⱼ Q = P ─✴[ j ] Q

  {- Such a morphism on SAs induces a functor on SA-predicates -}
  module _ where

    data J {p} (P : Pred A p) : Pred B (a ⊔ p) where
      inj : ∀ {Φ} → P Φ → J P (j Φ)

    jstar : ∀ {p q} {P : Pred A p} {Q : Pred A q} → ∀[ J (P ✴ Q) ⇒ J P ✴ J Q ]
    jstar (inj (p ×⟨ σ ⟩ q)) = inj p ×⟨ j-⊎ σ ⟩ inj q

    jmap : ∀ {p q} {P : Pred A p} {Q : Pred A q} → ∀[ (P ─✴ Q) ⇒ⱼ (J P ─✴ J Q) ]
    app (jmap f) (inj px) σ with j-⊎⁻ σ
    ... | _ , refl , σ' = inj (app f px σ')

  wanditⱼ : ∀ {p q} {P : Pred A p} {Q : Pred B q} → ∀[ P ⇒ⱼ Q ] → (P ─✴ⱼ Q) (j u)
  app (wanditⱼ f) px σ with j-⊎⁻ σ
  ... | _ , refl , σ' with ⊎-id⁻ˡ σ'
  ... | refl = f px

{- identity morphism -}
module _ {a} {A : Set a} {{r : RawSep A}} {u} {{s₁ : IsUnitalSep r u}} where

  instance id-morph : Morphism A A
  id-morph = record
    { j   = id
    ; j-⊎ = id
    ; j-⊎⁻ = λ x → -, refl , x
    }


module Oscar.Data.Term.ThickAndThin.internal {𝔣} (FunctionName : Set 𝔣) where

open import Oscar.Class.AlphaConversion
import Oscar.Class.ThickAndThin as ⋆
open import Oscar.Data.Term FunctionName
open import Oscar.Data.Term.Injectivity FunctionName
open import Oscar.Data.Term.AlphaConversion FunctionName
open import Oscar.Data.Equality
open import Oscar.Data.Equality.properties
open import Oscar.Data.Fin
open import Oscar.Data.Fin.ThickAndThin
open import Oscar.Data.Nat
open import Oscar.Data.Vec
open import Oscar.Data.Vec.Injectivity
open import Oscar.Function

private

  Fin⋆thin = ⋆.ThickAndThin.thin ThickAndThinFin
  Fin⋆thick = ⋆.ThickAndThin.thick ThickAndThinFin

thin : ∀ {m} → Fin (suc m) → Term m → Term (suc m)
thin = _◂_ ∘ Fin⋆thin

thins : ∀ {m} → Fin (suc m) → ∀ {N} → Vec (Term m) N → Vec (Term (suc m)) N
thins y = Fin⋆thin y ◂_ -- TODO: make point-free

mutual

  thin-injective : ∀ {m} (y : Fin (suc m)) {τ₁ τ₂ : Term m} → thin y τ₁ ≡ thin y τ₂ → τ₁ ≡ τ₂
  thin-injective y {i _} {i _} eq
    rewrite ⋆.thin-injective y (Term-i-inj eq)
    = refl
  thin-injective y {_ fork _} {_ fork _} eq
    rewrite thin-injective y (Term-forkLeft-inj eq)
          | thin-injective y (Term-forkRight-inj eq)
    = refl
  thin-injective _ {leaf} {leaf} _ = refl
  thin-injective y {function _ _} {function _ _} eq rewrite Term-functionName-inj eq with Term-functionArity-inj eq
  … | refl with Term-functionTerms-inj eq
  … | eq' rewrite (thins-injective y eq') = refl
  thin-injective _ {i _} {leaf} ()
  thin-injective _ {i _} {_ fork _} ()
  thin-injective _ {i _} {function _ _} ()
  thin-injective _ {leaf} {i _} ()
  thin-injective _ {leaf} {_ fork _} ()
  thin-injective _ {leaf} {function _ _} ()
  thin-injective _ {_ fork _} {i _} ()
  thin-injective _ {_ fork _} {leaf} ()
  thin-injective _ {_ fork _} {function _ _} ()
  thin-injective _ {function _ _} {i _} ()
  thin-injective _ {function _ _} {leaf} ()
  thin-injective _ {function _ _} {_ fork _} ()

  thins-injective : ∀ {m} (f : Fin (suc m)) → ∀ {N} {τs₁ τs₂ : Vec (Term m) N} → thins f τs₁ ≡ thins f τs₂ → τs₁ ≡ τs₂
  thins-injective y {_} {[]} {[]} x₁ = refl
  thins-injective y {_} {_ ∷ _} {_ ∷ _} eq
    rewrite thin-injective y (Vec-head-inj eq)
          | thins-injective y (Vec-tail-inj eq)
    = refl

thick : ∀ {m} → Term (suc m) → Fin m → Term m
thick τ = flip _◂_ τ ∘ flip Fin⋆thick

thicks : ∀ {m N} → Terms N (suc m) → Fin m → Terms N m
thicks τ = flip _◂_ τ ∘ flip Fin⋆thick


record category { ℓ₀ ℓ₁ } { k : Set ℓ₀ } (_⇒_ : k → k → Set ℓ₁) : Set (lsuc lzero ⊔ ℓ₀ ⊔ ℓ₁)
record groupoid { ℓ₀ ℓ₁ } { k : Set ℓ₀ } (_≈_ : k → k → Set ℓ₁) : Set (lsuc lzero ⊔ ℓ₀ ⊔ ℓ₁)
record groupoid { ℓ₀ ℓ₁ } { k } _≈_
  where
  coinductive
  field
    ⦃ cat ⦄ : category _≈_
  field
    symmetry : ∀ { x y } → x ≈ y → y ≈ x
record category { ℓ₀ ℓ₁ } { k } _⇒_
  where
  coinductive
  field
    id  : ∀ { a } → a ⇒ a
    _∘_ : ∀ { a b c } → b ⇒ c → a ⇒ b → a ⇒ c
  field
    _≈_ : { a b : k } → (f g : a ⇒ b) → Set
    ⦃ hom ⦄ : { a b : k } → groupoid { k = a ⇒ b } _≈_
  field
    lunit : ∀ { a b }     { x : a ⇒ b }                             → (id ∘ x) ≈ x
    runit : ∀ { a b }     { x : a ⇒ b }                             → (x ∘ id) ≈ x
    assoc : ∀ { a b c d } { x : c ⇒ d } { y : b ⇒ c } { z : a ⇒ b } → (x ∘ (y ∘ z)) ≈ ((x ∘ y) ∘ z)
open category ⦃ ... ⦄
open groupoid ⦃ ... ⦄


-- Mergesort

{-# OPTIONS --without-K --safe #-}

module Algorithms.List.Sort.Merge where

-- agda-stdlib
open import Level
open import Data.Bool using (true; false)
open import Data.List
import      Data.List.Properties as Listₚ
open import Data.Product as Prod
import      Data.Nat as ℕ
open import Data.Nat.Induction as Ind
open import Function.Base
open import Relation.Binary as B
open import Relation.Binary.PropositionalEquality as P using (_≡_)
open import Relation.Nullary
open import Relation.Unary as U
import      Relation.Unary.Properties as Uₚ
open import Induction.WellFounded

private
  variable
    a p r : Level

module Mergesort {A : Set a} {_≤_ : Rel A r} (_≤?_ : B.Decidable _≤_) where
  merge′ : A → A → List A → List A → List A
  merge′ x y xs ys with x ≤? y
  merge′ x y []        ys | true  because _ = x ∷ y ∷ ys
  merge′ x y (x₁ ∷ xs) ys | true  because _ = x ∷ merge′ x₁ y xs ys
  merge′ x y xs []        | false because _ = y ∷ x ∷ xs
  merge′ x y xs (y₁ ∷ ys) | false because _ = y ∷ merge′ x y₁ xs ys

  merge : List A → List A → List A
  merge []       ys       = ys
  merge (x ∷ xs) []       = x ∷ xs
  merge (x ∷ xs) (y ∷ ys) = merge′ x y xs ys

  _L<_ : Rel (List A) _
  _L<_ = ℕ._<_ on length

  L<-wf : WellFounded _L<_
  L<-wf = InverseImage.wellFounded length Ind.<-wellFounded

  split : List A → (List A × List A)
  split []           = [] , []
  split (x ∷ [])     = [ x ] , []
  split (x ∷ y ∷ xs) = Prod.map (x ∷_) (y ∷_) (split xs)
{-
  sort : List A → List A
  sort [] = []
  sort (x ∷ xs) = {!   !}
  sort xs = split xs
  ys , zs = merge (sort ys) (sort zs)
-}