import Relation.Binary.PropositionalEquality as PE

module x01 where

import Data.Empty.Polymorphic as DEP
open import Data.Unit.Polymorphic
import Data.Maybe as Maybe

{-
Dependent Types at Work
Ana Bove and Peter Dybjer
Chalmers University of Technology, Göteborg, Sweden

intro to functional programming with dependent types

- simply typed functional programming in style of Haskell and ML
- discuss differences between Agda’s type system and Hindley-Milner type system of Haskell/ML
- how to use dependent types for programming
- explain ideas behind type-checking dependent types
- explain Curry-Howard identification of propositions and types
- show a method to encode partial/general recursive functions
  as total functions using dependent types

Dependent types are types that depend on elements of other types (e.g., Vec of length n)
Invariants can be expressed with dependent type (e.g., sorted list)

Agda's dependent type system is an extension of Martin-Löf type theory [20,21,22,25].

Parametrised types
- e.g., [A]
- usually not called dependent types
- they are are families of types indexed by other types
  - NOT families of types indexed by ELEMENTS of another type
- however, in dependent type theories, there is a type of small types (a universe),
  so that we have a type [A] of lists of elements of a given small type A.
- FORTRAN could define arrays of a given dimension, so a form a dependent type on small type
- simply typed lambda calculus and the Hindley-Milner type system,
  (e.g., Haskell/ML) do not include dependent types, only parametrised types
- polymorphic lambda calculus System F [15] has types like
  ∀X.A where X ranges over all types,
  but no quantification over ELEMENTS of other types

development of dependently typed programming languages has
origins in Curry-Howard isomorphism between propositions and types

1930’s : Curry noticed similarity between axioms of implicational logic
   P ⊃ Q ⊃ P                                (P ⊃ Q ⊃ R) ⊃ (P ⊃ Q) ⊃ P ⊃ R
and types of combinators
   K              and                       S
   A → B → A                                (A → B → C) → (A → B) → A → C

K can be viewed as a witness (i.e., proof object) of the truth of P ⊃ Q ⊃ P
S can be viewed as a withness of the truth of (P ⊃ Q ⊃ R) ⊃ (P ⊃ Q) ⊃ P ⊃ R

typing rule for application
corresponds to the inference rule modus ponens: from P ⊃ Q and P conclude Q
- f : A → B
- a : A
- f a → B

product types correspond to conjunctions
sum types (disjoint unions) to disjunctions

to extend this correspondence to predicate logic
Howard and de Bruijn introduced dependent types A(x) corresponding to predicates P(x).
They formed
indexed products Π x : D.A(x) : corresponding to universal   quantifications ∀x : D.P (x)
indexed sums     Σ x : D.A(x) :                  existential quantifications ∃x : D.P (x)

this gives a Curry-Howard interpretation of intuitionistic predicate logic

one-to-one correspondence between propositions and types
in a type system with dependent types

one-to-one correspondence between
- proofs of a certain proposition in constructive predicate logic, and
- terms of the corresponding type

To accommodate equality in predicate logic
introduce the type a == b of proofs that a and b are equal.
Gives a Curry-Howard interpretation of predicate logic with equality.

Can go further and add natural numbers with addition and multiplication
to obtain a Curry-Howard version of Heyting (intuitionistic) arithmetic.

Curry-Howard interpretation was basis for Martin-Löf’s intuitionistic type theory [20,21,22].
- propositions and types are identified
- intended to be a foundational system for constructive mathematics
- can also be used as a programming language [21]
- from 1980’s and onwards, systems implementing variants of Martin-Löf type theory
  - NuPRL [6] : implementing an extensional version of the theory
  - Coq [33]  : implementing an intensional impredicative version
  - Agda      : implements   an intensional predicative extension

About these Notes

Agda's type-checking algorithm
- Section 5
- ideas first presented by Coquand [7]
- more information
  - Norell’s thesis [26]
  - Abel, Coquand, and Dybjer [1,2]
  - Coquand, Kinoshita, Nordström, and Takeyama [8].

more complete understanding of dependent type theory
- read a book about Martin-Löf type theory and related systems
- Martin-Löf’s “Intuitionistic Type Theory” [22] is a classic
  - be aware that it describes an extensional version of the theory
- Nordström, Petersson, and Smith [25]
  description of the later intensional theory on which Agda is based
- variants of dependent type theory are
  - Thompson [34]
  - Constable et al     [6] : NuPRL
  - Bertot and Casteran [4] : Coq
- lecture notes (Agda wiki [3]) by Norell
  - collection of advanced examples of how to use Agda for dependently typed programming
- Geuvers’ lecture notes : introduction to type theory
  - including Barendregt’s pure type systems and important meta-theoretic properties
- Bertot’s notes : how dependent types (in Coq) can be used for
  implementing a number of concepts of programming language theory
  - focus on abstract interpretation
- Barthe, Grégoire, and Riba’s notes
  - method for making termination-checkers

Section 2 : ordinary functional programming
Section 3 : basic dependent types and shows how to use them
Section 4 : Curry-Howard isomorphism
Section 5 : aspects of type-checking and pattern matching with dependent types
Section 6 : use Agda as a programming logic
Section 7 : how to represent general recursion and partial functions as total functions in Agda

------------------------------------------------------------------------------
2 Simply Typed Functional Programming in Agda

2.1 Truth Values
-}
data Bool : Set where
  true  : Bool
  false : Bool
{-
states
- Bool is a data type with the two (no-arg) constructors
- “:” denotes type membership
- Bool is a member of the type Set
- Set
  - the type of sets (using a terminology introduced by Martin-Löf [22])
    or small types (mentioned in the introduction).
- Bool is a small type, but Set itself is not, it is a large type.
- allowing Set : Set, would make the system inconsistent
-}
not : Bool -> Bool
not true  = false
not false = true
{-
Agda cannot generally infer types.
Because Agda type-checking done via normalisation (simplification)
- without the normality restriction it may not terminate
-}
equiv : Bool -> Bool -> Bool
equiv false false = true
equiv  true false = false
equiv false  true = false
equiv  true  true = true

_||_ : Bool -> Bool -> Bool
true  || _     = true
false || true  = true
false || false = false
infixl 60 _||_

-- EXERCISE: Define some more truth functions, such as conjunction and implication/TODO.

_&&_ : Bool -> Bool -> Bool
false && _     = false
true  && false = false
true  && true  = true
-- infixl ?? _&&_

-- 2.2 Natural Numbers

-- such data types known as recursive in Haskell
-- in constructive type theory, referred to as inductive types or inductively defined types

data Nat : Set where
  zero : Nat
  succ : Nat -> Nat
{-# BUILTIN NATURAL Nat #-}

pred : Nat -> Nat
pred  zero    = zero
pred (succ n) = n

_+_ : Nat -> Nat -> Nat
zero + m = m
succ n + m = succ (n + m)
{-# BUILTIN NATPLUS _+_ #-}

_*_ : Nat -> Nat -> Nat
zero   * n = zero
succ n * m = (n * m) + m
{-# BUILTIN NATTIMES _*_ #-}

infixl 6  _+_  _∸_
infixl 7  _*_

_ : (2 * 3) + 10 PE.≡ 16
_ = PE.refl
{-
+ and * are defined by PRIMITIVE RECURSION in FIRST ARG.
  - PRIMITIVE RECURSIION :
    - upper bound of number of iterations of every loop can be determined before entering loop
    - time complexity of PR functions bounded by the input size
- base case for zero
- step case for non zero defined recursively

Given a first order data type
- distinguish between
  - canonical form
    - built by constructors only
    - e.g., true, false,  zero, succ zero, succ (succ zero), ...
  - non-canonical forms
    - whereas non-canonical elements might contain defined functions
    - e.g., not true, zero + zero, zero * zero, succ (zero + zero)

Martin-Löf [21] instead considers lazy canonical forms
- a term begins with a constructor to be considered a canonical form
- e.g., succ (zero + zero) is a lazy canonical form, but not a “full” canonical form
- Lazy canonical forms are appropriate for lazy functional programming languages (e.g., Haskell)

-- EXERCISE: Write the cut-off subtraction function
-- the function on natural numbers, which returns 0
-- if the second argument is greater than or equal to the first.
-}
_∸_ : Nat -> Nat -> Nat
zero   ∸    _   = zero
succ n ∸ zero   = succ n
succ n ∸ succ m = n ∸ m

_ :  0 ∸  0 PE.≡  0
_ = PE.refl
_ :  0 ∸ 10 PE.≡  0
_ = PE.refl
_ : 10 ∸  0 PE.≡ 10
_ = PE.refl
_ : 10 ∸ 10 PE.≡  0
_ = PE.refl
_ : 10 ∸  9 PE.≡  1
_ = PE.refl

-- EXERCISE: Also write some more numerical functions like < or <=

_<_ : Nat -> Nat -> Bool
zero   < zero   = false
zero   < succ _ = true
succ _ < zero   = false
succ n < succ m = n < m

_ :  9 < 10 PE.≡ true
_ = PE.refl
_ : 10 < 10 PE.≡ false
_ = PE.refl

_<=_ : Nat -> Nat -> Bool
zero   <= zero   = true
zero   <= succ _ = true
succ _ <= zero   = false
succ n <= succ m = n <= m

_ :  9 <= 10 PE.≡ true
_ = PE.refl
_ : 10 <= 10 PE.≡ true
_ = PE.refl

_>_ : Nat -> Nat -> Bool
zero   > zero   = false
zero   > succ _ = false
succ _ > zero   = true
succ n > succ m = n > m

{-
2.3 Lambda Notation and Polymorphism

Agda is based on the typed lambda calculus.

APPLICATION is juxtaposition

lambda ABSTRACTION written either
- Curry-style (without a type label)
   \x -> e
- Church-style (with a type label)
  \(x : A) -> e

a family of identity functions, one for each small type:
-}
-- idE(xplicit)
idE :  (A : Set) ->       A  -> A
idE = \(A : Set) -> \(x : A) -> x
{-
apply this “generic” identity function id to a type argument A
to obtain the identity function from A to A

1st use of dependent types: type A -> A depends on variable A : Set
- ranging over the small types

Agda’s notation for dependent function types
- if A is a type and B[x] is a type which depends on (is indexed by) (x : A)
  then (x : A) -> B[x] is the type of functions f mapping arguments (x : A) to values f x : B[x].

If the type-checker can figure out value of an arg, can use a wild card character:
id _ x : A

K and S combinators in Agda (uses telescopic notation (A B : Set)
-}
K : (A B : Set) -> A -> B -> A
K _ _ x _     = x

S : (A B C : Set) -> (A -> B -> C) -> (A -> B) -> A -> C
S _ _ _ f g x = f x (g x)
{-
2.4 Implicit Arguments : declared by enclosing typings in curly brackets
-}

id : {A : Set} -> A -> A
id x = x

-- implicit arguments are omitted in applications
_ : id       zero PE.≡ zero
_ = PE.refl
-- can explicitly write an implicit argument by using curly brackets if needed
_ : id {Nat} zero PE.≡ zero
_ = PE.refl
_ : id {_}   zero PE.≡ zero
_ = PE.refl
{-
2.5 Gödel System T is a subsystem of Agda.

System of primitive recursive functionals [16]
- important in logic and a precursor to Martin-Löf type theory

recursion restricted to primitive recursion : to make sure programs terminate

Gödel System T
- based on simply typed lambda calculus
- with two base types : truth values and natural numbers
  - (some formulations code truth values as 0 and 1)
- constants for constructors true, false, zero
- conditional and primitive recursion combinators
-}

-- conditional
if_then_else_ : {C : Set} -> Bool -> C -> C -> C
if true  then x else _ = x
if false then _ else y = y

-- primitive recursion combinator for natural numbers
-- natrec is a higher-order function defined by primitive recursion with args
-- - (implicit) return type
-- - c     : element returned in base case
-- - 𝕟→c→c : step function
-- - the Nat on which to perform the recursion
natrec : {C : Set} -> C -> (Nat -> C -> C) -> Nat -> C
natrec c 𝕟→c→c  zero    = c
natrec c 𝕟→c→c (succ n) = 𝕟→c→c n (natrec c 𝕟→c→c n)

-- natrec definitions of addition and multiplication
_plus_ : Nat -> Nat -> Nat
_plus_ n m = natrec m    (\x y -> succ y)   n

_mult_ : Nat -> Nat -> Nat
_mult_ n m = natrec zero (\x y -> y plus m) n

-- compare plus/mult to _+_/_*_

_ : succ (succ  zero) + 2   PE.≡
    succ (succ  zero  + 2)
_ = PE.refl
_ : succ (succ  zero  + 2)  PE.≡
    succ (succ (zero  + 2))
_ = PE.refl
_ : succ (succ (zero  + 2)) PE.≡
    succ (succ          2 )
_ = PE.refl

_ :                                                      succ (succ  zero) plus 2                     PE.≡
                                                         natrec 2 (\x y -> succ y) (succ (succ zero))
_ = PE.refl
_ :                                                      natrec 2 (\x y -> succ y) (succ (succ zero)) PE.≡
    (\x y -> succ y) (succ zero)                        (natrec 2 (\x y -> succ y)       (succ zero))
_ = PE.refl
_ : (\x y -> succ y) (succ zero)                        (natrec 2 (\x y -> succ y)       (succ zero)) PE.≡
    (\  y -> succ y)                                    (natrec 2 (\x y -> succ y)       (succ zero))
_ = PE.refl
_ : (\  y -> succ y)                                    (natrec 2 (\x y -> succ y)       (succ zero)) PE.≡
             succ                                       (natrec 2 (\x y -> succ y)       (succ zero))
_ = PE.refl
_ :          succ                                       (natrec 2 (\x y -> succ y)       (succ zero)) PE.≡
             succ                ((\x y -> succ y) zero (natrec 2 (\x y -> succ y)             zero))
_ = PE.refl
_ :          succ                ((\x y -> succ y) zero (natrec 2 (\x y -> succ y)             zero)) PE.≡
             succ                ((\  y -> succ y)      (natrec 2 (\x y -> succ y)             zero))
_ = PE.refl
_ :          succ                ((\  y -> succ y)      (natrec 2 (\x y -> succ y)             zero)) PE.≡
             succ                (         succ         (natrec 2 (\x y -> succ y)             zero))
_ = PE.refl
_ :          succ                (         succ         (natrec 2 (\x y -> succ y)             zero)) PE.≡
             succ                (         succ                 2                                   )
_ = PE.refl

{-
to stay entirely in Gödel system T
- use terms built up by variables, application, lambda abstraction,
- and constants true, false, zero, succ, if_then_else_, natrec

Gödel system T has unusual property (for a programming language)
- all typable programs terminate

not only do terms in base types Bool and Nat terminate whatever reduction is chosen,
but also terms of function type terminate

reduction rules
- β-reduction
- defining equations for if_then_else and natrec

reductions can be performed anywhere in a term,
so there may be several ways to reduce a term

therefore, Gödel system T is strongly normalising
- any typable term reaches a normal form whatever reduction strategy is chosen

Even in this restrictive Gödel system T, possible to define many numerical functions.
Can define all primitive recursive functions
(in the usual sense without higher-order functions).

Can also define functions which are not primitive recursive, such as the Ackermann function.

Gödel system T is important in the history of ideas that led to
the Curry-Howard isomorphism and Martin-Löf type theory.

Gödel system T
- is the simply typed kernel of Martin-Löf’s constructive type theory,
Martin-Löf type theory
- is the foundational system from which Agda grew

relationship between Agda and Martin-Löf type theory is much like the
relationship between Haskell and the simply typed lambda calculus.
Or compare it with the relationship between Haskell and Plotkin’s PCF [30].

Like Gödel system T,
PCF is based on the simply typed lambda calculus with truth values and natural numbers.
But PCF includes a fixed point combinator
- used for encoding arbitrary general recursive definitions
  - therefore can define non-terminating functions in PCF.
-}

-- Exercise/TODO: Define all functions previously given in the text in Gödel System T.

-- induction principle for booleans (from Mimram)
Bool-rec : (P : Bool → Set) → P false → P true → (b : Bool) → P b
Bool-rec P Pf Pt false = Pf
Bool-rec P Pf Pt true  = Pt

not' : Bool -> Bool
not' b = Bool-rec (λ bool → Bool) true false b

_ : not' false PE.≡ not false
_ = PE.refl
_ : not' true  PE.≡ not true
_ = PE.refl

equiv' : Bool -> Bool -> Bool
equiv' b false = Bool-rec (λ bool → Bool) true false b
equiv' b true  = Bool-rec (λ bool → Bool) false true b

_ : equiv' false false PE.≡ equiv false false
_ = PE.refl
_ : equiv'  true false PE.≡ equiv  true false
_ = PE.refl
_ : equiv' false  true PE.≡ equiv false  true
_ = PE.refl
_ : equiv'  true  true PE.≡ equiv  true  true
_ = PE.refl

_||'_ : Bool -> Bool -> Bool
b ||' false = Bool-rec (λ bool → Bool)      b  b b
b ||'  true = Bool-rec (λ bool → Bool) (not b) b b

_ : false ||' false PE.≡ false || false
_ = PE.refl
_ : false ||'  true PE.≡ false ||  true
_ = PE.refl
_ :  true ||' false PE.≡  true || false
_ = PE.refl
_ :  true ||'  true PE.≡  true ||  true
_ = PE.refl

_&&'_ : Bool -> Bool -> Bool
b &&' false = Bool-rec (λ bool → Bool) false false b
b &&'  true = Bool-rec (λ bool → Bool) b     b     b

_ : false &&' false PE.≡ false && false
_ = PE.refl
_ : false &&'  true PE.≡ false &&  true
_ = PE.refl
_ :  true &&' false PE.≡  true && false
_ = PE.refl
_ :  true &&'  true PE.≡  true &&  true
_ = PE.refl

pred' : Nat -> Nat
pred'  zero    = natrec zero (\x y -> zero) zero
pred' (succ n) = natrec zero (\_ _ -> n)    n
-- test after List def

{-
2.6 Parametrised Types

Haskell parametrised types : [a]
-}
data List (A : Set) : Set where
  []   :                List A
  _::_ : A -> List A -> List A
infixr 5 _::_
{-# BUILTIN LIST List #-}
{-
(A : Set) to the left of the colon
- tells Agda that A is a parameter
- and it becomes an implicit argument to the constructors:
  []   : {A : Set} -> List A
  _::_ : {A : Set} -> A -> List A -> List A

def only allows defining lists with elements in arbitrary SMALL types
- cannot define lists of sets using this definition, since sets form a large type
-}

-- test for pred'
_ : (pred' 0 :: pred' 1 :: pred' 2 :: pred' 101 :: []) PE.≡ (pred 0 :: pred 1 :: pred 2 :: pred 101 :: [])
_ = PE.refl

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

_ : mapL succ (1 :: 2 :: []) PE.≡ (2 :: 3 :: [])
_ = PE.refl

-- Exercise: Define some more list combinators like for example foldl or filter.
filter : {A : Set} -> (A -> Bool) -> List A -> List A
filter f       []  = []
filter f (x :: xs) = let tail = filter f xs in if f x then x :: tail else tail

_ : filter (_< 4) (5 :: 4 :: 3 :: 2 :: 1 :: []) PE.≡ (3 :: 2 :: 1 :: [])
_ = PE.refl

foldl : {A B : Set} -> (B -> A -> B) -> B -> List A -> B
foldl _ b       []  = b
foldl f b (a :: as) = foldl f (f b a) as

foldr : {A B : Set} -> (A -> B -> B) -> B -> List A -> B
foldr f b       []  = b
foldr f b (a :: as) = f a (foldr f b as)

_ : foldl _+_ 0 (3 :: 2 :: 1 :: []) PE.≡ 6
_ = PE.refl

_ : foldl _∸_ 0 (3 :: 1 :: []) PE.≡ 0
_ = PE.refl

_ : foldr _∸_ 0 (3 :: 1 :: []) PE.≡ 2
_ = PE.refl

-- Exercise: define list recursion combinator listrec (like natrec, but for lists)
-- from Mimram
List-rec
  : {A : Set}
  → (P : List A → Set)
  → P []
  → ((x : A) → (xs : List A) → P xs → P (x :: xs))
  → (xs : List A)
  → P xs
List-rec P Pe Pc       []  = Pe
List-rec P Pe Pc (x :: xs) = Pc x xs (List-rec P Pe Pc xs)

mapL' : {A B : Set} -> (A -> B) -> List A -> List B
mapL' {_} {B} f xs = List-rec (λ _ → List B) [] (λ a _ bs → f a :: bs) xs

_ : mapL' succ (1 :: 2 :: []) PE.≡ mapL succ (1 :: 2 :: [])
_ = PE.refl

filter' : {A : Set} -> (A -> Bool) -> List A -> List A
filter' {A} f xs = List-rec (λ _ → List A) [] (λ a _ as' → if f a then a :: as' else as') xs

_ : filter' (_< 4) (5 :: 4 :: 3 :: 2 :: 1 :: []) PE.≡ filter (_< 4) (5 :: 4 :: 3 :: 2 :: 1 :: [])
_ = PE.refl

foldl' : {A B : Set} -> (B -> A -> B) -> B -> List A -> B
foldl' {_} {B} f b as = List-rec (λ _ → B) b (λ a _ b → f b a) as

_ : foldl' _+_ 0 (3 :: 2 :: 1 :: []) PE.≡ foldl _+_ 0 (3 :: 2 :: 1 :: [])
_ = PE.refl

foldr' : {A B : Set} -> (A -> B -> B) -> B -> List A -> B
foldr' {A} {B} f b as = List-rec (λ _ → B) b (λ a as b → f a b) as

_ : foldr' _∸_ 0 (3 :: 1 :: []) PE.≡ foldr _∸_ 0 (3 :: 1 :: [])
_ = PE.refl

-- binary Cartesian product AKA pair
data _X_ (A B : Set) : Set where
  <_,_> : A -> B -> A X B

-- prime because these are never used but are redeinfed later
fst' : {A B : Set} -> A X B -> A
fst' < a , b > = a

snd' : {A B : Set} -> A X B -> B
snd' < a , b > = b

-- Usually want to zip lists of equal length.
-- Can fix that with runtime Maybe or compiletime dependent types (later).
zipL : {A B : Set} -> List A -> List B -> List (A X B)
zipL       []         _  = []
zipL (_ ::  _)       []  = []
zipL (x :: xs) (y :: ys) = < x , y > :: zipL xs ys

-- Exercise: Define the sum A + B of two small types with constructors: inl, inr.
data Either (A B : Set) : Set where
  inl : A -> Either A B
  inr : B -> Either A B

-- Exercise: Define a function from A + B to a small type C by cases.
toC : {A B C : Set} → (A -> C) -> (B -> C) -> (Either A B) -> C
toC ac  _ (inl a) = ac a
toC  _ bc (inr b) = bc b

{-
2.7 Termination-checking

General recursion allowed in most languages.
Therefore possible to define "partial" functions (e.g., divide by 0)

How to ensure functions terminate?
- one solution : restrict recursion to primitive recursion (like in Gödel system T)
  - the approach taken in Martin-Löf type theory
    where primitive recursion as a kind of structural recursion on well-founded data types
    (see Martin-Löf’s book [22] and Dybjer’s schema for inductive definitions [10])
  - but structural recursion (in one argument at a time) is often inconvenient

Agda’s termination-checker
- generalisation of primitive recursion which is practically useful
- enables doing pattern matching on several arguments simultaneously
- and having recursive calls to structurally smaller arguments

But repeated subtraction is rejected by Agda termination-checker although it is terminating.
Because Agda does not recognise the recursive call to (m - n) as structurally smaller.
Because subtraction is not a constructor for natural numbers.
Further reasoning required to deduce the recursive call is on a smaller argument.

Section 7 : describes how partial and general recursive functions can be represented in Agda.
Idea is to replace a partial function by a total function with an extra argument:
a proof that the function terminates on its arguments.

------------------------------------------------------------------------------
3 Dependent Types

3.1 Vectors of a Given Length

2 ways to define
- Recursive family
  - by primitive recursion/induction on n
- Inductive family
  - as a family of data types by declaring its constructors and the types of constructors
  - like def of List, except length info included in types

paper mostly uses inductive families

--------------------------------------------------
Vectors as a Recursive Family

In math : define vectors by induction on n:
  A   0 = 1
  A n+1 = A × An
In Agda (and Martin-Löf type theory):
-}
-- type with only one element
data Unit : Set where
  <> : Unit

VecR : Set -> Nat -> Set
VecR A  zero    = Unit
VecR A (succ n) = A X VecR A n

{-
Up till now, only used primitive recursion for defining functions
where the range is in a given set (in a given small type).
Here, primitive recursion used for defining a family of sets
- a family of elements in a given large type.
-}

zipR : {A B : Set} {n : Nat} -> VecR A n -> VecR B n -> VecR (A X B) n
zipR {n =  zero}          _          _   = <>
zipR {n = succ n'} < a , as > < b , bs > = < < a , b > , zipR {n = n'} as bs >

{-
base case : type-correct since the right hand side has type
            VecR (A X B) zero     = <>
step case : type-correct since the right hand side has type
            VecR (A X B) (succ n) = (A X B) X (VecR (A X B) n)
-}

-- Exercise. Write the functions head, tail, and map for the recursive vectors.

headR : {A : Set} {n : Nat} -> VecR A (succ n) ->      A
headR < x , _ > = x

tailR : {A : Set} {n : Nat} -> VecR A (succ n) -> VecR A n
tailR < _ ,  t > = t

mapR : {A B : Set} {n : Nat} -> (A -> B) -> VecR A n -> VecR B n
mapR {n = zero}    _        _   = <>
mapR {n = succ n'} f < a , as > = < f a , mapR {n = n'} f as >

_ : mapR succ < 1 , < 2 , < 0 , <> > > > PE.≡ < 2 , < 3 , < 1 , <> > > >
_ = PE.refl

--------------------------------------------------
-- Vectors as an Inductive Family

data Vec (A : Set) : Nat -> Set where
  []   :                              Vec A  zero
  _::_ : {n : Nat} -> A -> Vec A n -> Vec A (succ n)

{-
this time no induction on length
instead : constructors generate vectors of different lengths
[] : length 0
:: : length (n + 1)

Definition style called an inductive family, or an inductively defined family of sets.

Terminology comes from constructive type theory,
where data types such as Nat and (List A) are called inductive types.

Remark: Beware of terminological confusion:
- In programming languages one talks about recursive types for such data
  types defined by declaring the constructors with their types.
- May be confusing since the word recursive family was used for a different notion.
- Reason for terminological distinction between data types in ordinary functional languages,
  and data types in languages where all programs terminate.
- In terminating languages, there will not be any non-terminating numbers or non-terminating lists.
- The set-theoretic meaning of such types is therefore simple:
  - build the set inductively generated by the constructors, see [9] for details.
- In non-terminating language, the semantic domains are more complex.
  - One typically considers various kinds of Scott domains which are complete partially orders.

(Vec A n) has two args:
- small type A of the elements
  - A is a parameter in the sense that it remains the same throughout the definition:
    for a given A, define the family Vec A : Nat -> Set
- length n of type Nat.
  - n is not a parameter since it varies in the types of the constructors

Non-parameters are often called INDICES
- say that Vec A is an inductive family indexed by natural numbers

In Agda data type definitions
- parameters are placed to the left of the colon
  and become implicit arguments to the constructors
- indices are placed to the right
-}

zip : {A B : Set} {n : Nat} -> Vec A n -> Vec B n -> Vec (A X B) n
zip {n = zero}          []        []  = []
zip {n = succ n'} (x :: xs) (y :: ys) = < x , y > :: zip {n = n'} xs ys

head : {A : Set} {n : Nat} -> Vec A (succ n) -> A
head (x :: _) = x

tail : {A : Set} {n : Nat} -> Vec A (succ n) -> Vec A n
tail (_ :: xs) = xs

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

--------------------------------------------------
-- 3.2 Finite Sets

data Fin : Nat -> Set where
  fzero : {n : Nat} ->          Fin (succ n)
  fsucc : {n : Nat} -> Fin n -> Fin (succ n)

{-
for each n, Fin n contains n elements
e.g., Fin 3 contains fzero, fsucc fzero and fsucc (fsucc fzero)

useful to access element in a vector
- for Vec A n
- position given by (Fin n)
- ensures accessing an element inside the vector

For empty vector n is zero.
So would use (Fin 0) --- which has no elements.
There is no such case.
Expressed in Agda via "absurd" pattern '()'
-}

_!_ : {A : Set} {n : Nat} -> Vec A n -> Fin n -> A
[]        ! ()
(x ::  _) ! fzero   = x
(_ :: xs) ! fsucc i = xs ! i

-- Exercise: Rewrite the function !! so that it has the following type:
-- This eliminates the empty vector case, but other cases are needed.

_!!_  : {A : Set} {n : Nat} -> Vec A (succ n) -> Fin (succ n) -> A
_!!_  {_}   {zero} (a ::  f_)  fzero    = a
_!!_  {_} {succ n} (a ::  _)  fzero    = a
_!!_  {_} {succ n} (_ :: as) (fsucc f) = _!!_  {n = n} as f

_!!'_ : {A : Set} {n : Nat} -> Vec A (succ n) -> Fin (succ n) -> A
_!!'_              (a ::  _)  fzero    = a
_!!'_ {_} {succ n} (_ :: as) (fsucc f) = _!!'_ {n = n} as f

_ : (0 :: 1 :: 2 :: []) !   fsucc fzero PE.≡ 1
_ = PE.refl
_ : (0 :: 1 :: 2 :: []) !!  fsucc fzero PE.≡ 1
_ = PE.refl
_ : (0 :: 1 :: 2 :: []) !!' fsucc fzero PE.≡ 1
_ = PE.refl

-- Exercise: Give an alternative definition of Fin as a recursive family.

-- from Conal Eliot https://raw.githubusercontent.com/conal/agda-play/main/DependentTypesAtWork/RecVec.agda
FinR : Nat -> Set
FinR  zero    = DEP.⊥
FinR (succ n) = Maybe.Maybe (FinR n)

f5 : FinR 5
f5 = Maybe.nothing
f5' : FinR 5 PE.≡ Maybe.Maybe (Maybe.Maybe (Maybe.Maybe (Maybe.Maybe (Maybe.Maybe DEP.⊥))))
f5' = PE.refl
f4 : FinR 4
f4 = Maybe.nothing
f3 : FinR 3
f3 = Maybe.nothing
f2 : FinR 2
f2 = Maybe.nothing
f1 : FinR 1
f1 = Maybe.nothing
f1' : FinR 1 PE.≡ Maybe.Maybe DEP.⊥
f1' = PE.refl
--f0 : FinR 0
--f0 = {!!} -- no solution found
f0' : FinR 0 PE.≡ DEP.⊥
f0' = PE.refl

_!'_ : {A : Set} {n : Nat} → VecR A n → FinR n → A
_!'_  {n = succ m} < a ,  _ >  Maybe.nothing    = a
_!'_  {n = succ m} < _ , as > (Maybe.just finm) = as !' finm

_    : < 0 , < 1 , < 2 , <> > > >  !'                                    Maybe.nothing   PE.≡ 0
_    = PE.refl
_    : < 0 , < 1 , < 2 , <> > > >  !'                         Maybe.just Maybe.nothing   PE.≡ 1
_    = PE.refl
_    : < 0 , < 1 , < 2 , <> > > >  !'             Maybe.just (Maybe.just Maybe.nothing)  PE.≡ 2
_    = PE.refl
-- (Maybe.Maybe _A_645) !=< DEP.⊥
-- _ : < 0 , < 1 , < 2 , <> > > >  !' Maybe.just (Maybe.just (Maybe.just Maybe.nothing)) PE.≡ 100
-- _ = PE.refl

--------------------------------------------------
-- 3.3 More Inductive Families

-- binary trees of a certain height
-- any given (t : DBTree A n) is a perfectly balanced tree
-- with 2n elements and information in the leaves
data DBTree (A : Set) : Nat -> Set where
  leaf :                     A                 -> DBTree A zero
  node : {n : Nat} -> DBTree A n -> DBTree A n -> DBTree A (succ n)

l1       : DBTree Nat 0
l1       = leaf 1
l2       : DBTree Nat 0
l2       = leaf 2
n1-2     : DBTree Nat 1
n1-2     = node l1 l2
l3       : DBTree Nat 0
l3       = leaf 3
l4       : DBTree Nat 0
l4       = leaf 4
n3-4     : DBTree Nat 1
n3-4     = node l3 l4
n1-2-3-4 : DBTree Nat 2
n1-2-3-4 = node n1-2 n3-4

-- Exercise/TODO: Modify DBTree def to define the height balanced binary trees
-- i.e., binary trees where difference between heights of left and right subtree is at most one.

-- Exercise/TODO: Define lambda terms as an inductive family
-- indexed by the maximal number of free variables allowed in the term.

-- Exercise/TODO: define typed lambda terms as an inductive family indexed by the type of the term.

{-
------------------------------------------------------------------------------
Curry, 1930’s : one-to-one correspondence between propositions in propositional logic and types.
1960’s de Bruijn and Howard introduced dependent types to extend Curry’s correspondence to predicate logic.
Scott [31] and Martin-Löf [20], correspondence became building block of foundation for constructive math:
- Martin-Löf’s intuitionistic type theory.

Will now show how intuitionistic predicate logic with equality is a subsystem of
Martin-Löf type theory by realising it as a theory in Agda.

4.1 Propositional Logic

isomorphism : each proposition is interpreted as the set of its proofs.
To emphazie PROOFS here are FIRST-CLASS mathematical object, say PROOF OBJECTS.
aka : CONSTRUCTIONS

proposition
- is true IFF its set of proofs is inhabited
- it is false iff its set of proofs is empty
-}

-- CONJUNCTION (aka AND)
-- A and B are two propositions represented by their sets of proofs.
-- A & B is also a set of proofs, representing the conjunction of A and B.
-- Note : conjunction is same as Cartesian product of two sets.
-- applying & constructor is &-introduction
data _&_ (A B : Set) : Set where
  <_,_> : A -> B -> A & B

-- alternate def
_&'_ : Set -> Set -> Set
A &' B = A X B

-- two rules of &-elimination
-- rules state that if A & B is true then A and B are also true
fst : {A B : Set} -> A & B -> A
fst < a , _ > = a

snd : {A B : Set} -> A & B -> B
snd < _ , b > = b

sndP : {A B : Set} -> A &' B -> B
sndP < _ , b > = b

-- DISJUNCTION (aka OR)
-- disjunction corresponds to disjoint union
data _\/_ (A B : Set) : Set where
  inl : A -> A \/ B
  inr : B -> A \/ B

-- alternate def
_\/'_ : Set -> Set -> Set
A \/' B = Either A B

-- rule of \/-elimination is case analysis for a disjoint union
case : {A B C : Set} -> A \/ B -> (A -> C) -> (B -> C) -> C
case (inl a) d e = d a
case (inr b) d e = e b

-- proposition which is always true
-- corresponds to the unit set (see page 14) according to Curry-Howard:
data True : Set where
  <> : True

-- proposition False is the proposition that is false by definition
-- corresponds to the the empty set according to Curry-Howard.
-- This is the set which is defined by stating that it has no canonical elements.
-- sometimes referred as the “absurdity” set and denoted by ⊥
data False : Set where

-- rule of ⊥-elimination states that if False has been proved, then can prove any proposition A.
-- Can only happen by starting with contradictory assumptions.
nocase : {A : Set} -> False -> A
nocase () -- tells agda no cases to consider
{-
Justification of this rule is same as justification of existence of
an empty function from the empty set into an arbitrary set.
Since the empty set has no elements there is nothing to define; it is definition by no cases.
Recall the explanation on page 16 when we used the notation ().
-}

-- In constructive logic
-- - proving negation of a proposition is the same as
-- - proving the proposition leads to absurdity:
Not : Set -> Set
Not A = A -> False

{-
BHK-interpretation says an implication is proved by providing a method for transforming
a proof of A into a proof of B.

When Brouwer pioneered this 100 years ago, there were no computers and no models of computation.
In contemporary constructive mathematics in general, and in Martin-Löf type theory in particular,
a “method” is usually understood as a computable function (or computer program) which transforms proofs.
Implication is defined as function space.
Emphasized via new notation:
-}

_==>_ : (A B : Set) -> Set
A ==> B = A -> B

_ : False ==> False -- T
_ = λ ()
_ : False ==> True  -- T
_ = λ ()
_ : True  ==> False -- F
_ = λ _ → {!!}
_ : True  ==> True  -- T
_ = λ _ → <>

{-
Above def not accepted in Martin-Löf’s version of propositions-as-sets.
Because each proposition should be defined by stating what its canonical proofs are.
A canonical proof should always begin with a constructor.
A function in A -> B does not, unless one considers the lambda-sign as a constructor.

Instead, Martin-Löf defines implication as a set with one constructor:
Using this, a canonical proof of A ==>' B always begins with the constructor fun.
-}
data _==>'_ (A B : Set) : Set where
  fun : (A -> B) -> A ==>' B

-- rule of ==>-elimination (modus ponens)
apply : {A B : Set} -> A ==>' B -> A -> B
apply (fun f) a = f a

-- equivalence of propositions
_<==>_ : Set -> Set -> Set
A <==> B = (A ==> B) & (B ==> A)

_ : False <==> False
_ = < ( λ () ) , ( λ () ) >
_ : False <==> True
_ = < (λ () ) , (λ { <> → {!!}}) >
_ : True  <==> False
_ = < (λ { <> → {!!}}) , (λ () ) >
_ : True  <==> True
_ = < (λ _ → <>) , (λ _ → <>) >

{-
Exercise/TODO: Prove your favourite tautology from propositional logic.
Beware that you will not be able to prove the law of the excluded middle A \/ Not A.
This is a consequence of the definition of disjunction, can you explain why?.
The law of the excluded middle is not available in intuitonistic logic, only in classical logic.
-}

le : ∀ {P Q : Set} → (P ==> Q) <==> (Not P \/ Q)
le {P} {Q} = < (λ {P==>Q → {!!} }) , (λ { (inl NotP) p → {!!} ; (inr q) p → q}) >


{-# OPTIONS --safe --without-K #-}
module Categories.Examples.Functor.Sets where

import Data.List as List
import Data.List.Properties
open import Data.Nat using (ℕ)
import Data.Product as Product
import Data.Vec as Vec
import Data.Vec.Properties
open import Function using (id; λ-; _$-)
open import Relation.Binary.PropositionalEquality using (refl)

open import Categories.Category.Discrete using (Discrete)
open import Categories.Category.Instance.Sets
open import Categories.Functor using (Endofunctor)
open import Categories.Functor.Bifunctor using (Bifunctor; appʳ)

List : ∀ {o} → Endofunctor (Sets o)
List = record
  { F₀ = List.List
  ; F₁ = List.map
  ; identity = map-id $-
  ; homomorphism = map-compose $-
  ; F-resp-≈ = λ f≈g → map-cong (λ- f≈g) $-
  }
  where
    open Data.List.Properties

Vec′ : ∀ {o} → Bifunctor (Sets o) (Discrete ℕ) (Sets o)
Vec′ = record
  { F₀ = Product.uncurry Vec.Vec
  ; F₁ = λ { (f , refl) → Vec.map f }
  ; identity = map-id $-
  ; homomorphism = λ { {_} {_} {_} {f , refl} {g , refl} → map-∘ g f _}
  ; F-resp-≈ = λ { {_} {_} {_} {_ , refl} (g , refl) → map-cong (λ- g) _}
  }
  where
    open Product using (_,_)
    open Data.Vec.Properties

Vec : ∀ {o} → ℕ → Endofunctor (Sets o)
Vec = appʳ Vec′


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

module Categories.Category.Instance.Properties.Setoids.CCC where

open import Level
open import Data.Product using (Σ ; _,_)
open import Function.Equality as ⟶ using (Π; _⟶_)
open import Relation.Binary using (Setoid)

open import Categories.Category
open import Categories.Category.Monoidal.Instance.Setoids
open import Categories.Category.CartesianClosed
open import Categories.Category.CartesianClosed.Canonical renaming (CartesianClosed to CCartesianClosed)
open import Categories.Category.Instance.Setoids

open Π

module _ ℓ where
  private
    S = Setoids ℓ ℓ
    module S = Category S

  Setoids-Canonical : CCartesianClosed S
  Setoids-Canonical = record
    { ⊤            = ⊤
    ; _×_          = _×_
    ; !            = !
    ; π₁           = π₁
    ; π₂           = π₂
    ; ⟨_,_⟩        = ⟨_,_⟩
    ; !-unique     = !-unique
    ; π₁-comp      = λ {_ _ f _ g} → project₁ {h = f} {g}
    ; π₂-comp      = λ {_ _ f _ g} → project₂ {h = f} {g}
    ; ⟨,⟩-unique   = λ {_ _ _ f g h} → unique {h = h} {f} {g}
    ; _^_          = λ X Y → Y ⟶.⇨ X
    ; eval         = λ {X Y} → record
      { _⟨$⟩_ = λ { (f , x) → f ⟨$⟩ x }
      ; cong  = λ { (eq₁ , eq₂) → eq₁ eq₂ }
      }
    ; curry        = λ {C A B} f → record
      { _⟨$⟩_ = λ c → record
        { _⟨$⟩_ = λ a → f ⟨$⟩ (c , a)
        ; cong  = λ eq → cong f (Setoid.refl C , eq)
        }
      ; cong  = λ eq₁ eq₂ → cong f (eq₁ , eq₂)
      }
    ; eval-comp    = λ {_ _ _ f} → cong f
    ; curry-resp-≈ = λ eq₁ eq₂ eq → eq₁ (eq₂ , eq)
    ; curry-unique = λ eq₁ eq₂ eq → eq₁ (eq₂ , eq)
    }
    where open Setoids-Cartesian

  Setoids-CCC : CartesianClosed S
  Setoids-CCC = Equivalence.fromCanonical S Setoids-Canonical


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

module Data.Binary.Subtraction where

open import Data.Binary.Definition
open import Data.Nat

double : 𝔹 → 𝔹
double 0ᵇ      = 0ᵇ
double (1ᵇ xs) = 2ᵇ double xs
double (2ᵇ xs) = 2ᵇ 1ᵇ xs

dec : 𝔹 → 𝔹
dec 0ᵇ = 0ᵇ
dec (2ᵇ xs) = 1ᵇ xs
dec (1ᵇ xs) = double xs

ones : ℕ → 𝔹 → 𝔹
ones zero    xs = xs
ones (suc n) xs = ones n (1ᵇ xs)

fromZ₁ : ℕ → 𝔹 → 𝔹
fromZ₁ n 0ᵇ      = 0ᵇ
fromZ₁ n (1ᵇ xs) = 2ᵇ ones n (double xs)
fromZ₁ n (2ᵇ xs) = 2ᵇ ones (suc n) xs

mutual
  compl₁′ : ℕ → 𝔹 → 𝔹 → 𝔹
  compl₁′ n 0ᵇ      _       = 2ᵇ ones n 0ᵇ
  compl₁′ n (1ᵇ xs) 0ᵇ      = fromZ₁ (suc n) xs
  compl₁′ n (2ᵇ xs) 0ᵇ      = 2ᵇ ones n (double xs)
  compl₁′ n (1ᵇ xs) (1ᵇ ys) = 2ᵇ ones n (compl₁′ 0 xs ys)
  compl₁′ n (1ᵇ xs) (2ᵇ ys) = compl₁′ (suc n) xs ys
  compl₁′ n (2ᵇ xs) (1ᵇ ys) = compl₂′ (suc n) xs ys
  compl₁′ n (2ᵇ xs) (2ᵇ ys) = 2ᵇ ones n (compl₁′ 0 xs ys)

  compl₂′ : ℕ → 𝔹 → 𝔹 → 𝔹
  compl₂′ n xs      0ᵇ      = fromZ₁ n xs
  compl₂′ n 0ᵇ      _       = 0ᵇ
  compl₂′ n (1ᵇ xs) (1ᵇ ys) = compl₂′ (suc n) xs ys
  compl₂′ n (1ᵇ xs) (2ᵇ ys) = 2ᵇ ones n (compl₁′ 0 xs ys)
  compl₂′ n (2ᵇ xs) (1ᵇ ys) = 2ᵇ ones n (compl₂′ 0 xs ys)
  compl₂′ n (2ᵇ xs) (2ᵇ ys) = compl₂′ (suc n) xs ys

mutual
  compl₁ : 𝔹 → 𝔹 → 𝔹
  compl₁ xs      0ᵇ      = dec xs
  compl₁ 0ᵇ      _       = 1ᵇ 0ᵇ
  compl₁ (1ᵇ xs) (1ᵇ ys) = 1ᵇ compl₁ xs ys
  compl₁ (1ᵇ xs) (2ᵇ ys) = compl₁′ zero xs ys
  compl₁ (2ᵇ xs) (1ᵇ ys) = compl₂′ zero xs ys
  compl₁ (2ᵇ xs) (2ᵇ ys) = 1ᵇ compl₁ xs ys

  compl₂ : 𝔹 → 𝔹 → 𝔹
  compl₂ 0ᵇ      _       = 0ᵇ
  compl₂ xs      0ᵇ      = xs
  compl₂ (1ᵇ xs) (1ᵇ ys) = compl₂′ zero xs ys
  compl₂ (1ᵇ xs) (2ᵇ ys) = 1ᵇ compl₁ xs ys
  compl₂ (2ᵇ xs) (1ᵇ ys) = 1ᵇ compl₂ xs ys
  compl₂ (2ᵇ xs) (2ᵇ ys) = compl₂′ zero xs ys

open import Data.Binary.Order
open import Data.Bool

infixl 6 _-_
_-_ : 𝔹 → 𝔹 → 𝔹
n - m = if n ≤ᴮ m then 0ᵇ else compl₂ n m

open import Data.Binary.Testers
open import Path using (refl)

_ : test _-_ _∸_ 30
_ = refl


module RMonads.Modules where

open import Library
open import Categories
open import Functors
open import RMonads

record Mod {a}{b}{c}{d}{C : Cat {a}{b}}{D : Cat {c}{d}}{J : Fun C D}
           (RM : RMonad J) : Set (a ⊔ c ⊔ d) where
  constructor mod
  open Cat
  open Fun
  open RMonad RM
  field M       : Obj C → Obj D
        mbind   : ∀ {X Y} → Hom D (OMap J X) (T Y) → Hom D (M X) (M Y)
        laweta  : ∀{X} → mbind (η {X}) ≅ iden D {M X}
        lawbind : ∀{X Y Z}
                  {f : Hom D (OMap J X) (T Y)}{g : Hom D (OMap J Y) (T Z)} →
                  mbind (comp D (bind g) f) ≅ comp D (mbind g) (mbind f)

-- any rel. monad is trivially a module over its J.
ModJ : forall {a}{b}{c}{d}{C : Cat {a}{b}}{D : Cat {c}{d}}
       {J : Fun C D} (RM : RMonad J) -> Mod (trivRM J)
ModJ {D = D}{J = J} RM =
  mod T
      (\ f -> bind (comp η f))
      (trans (cong bind idr) law1)
      (trans (cong bind (trans (trans (sym ass)
                                      (cong (\ g -> comp g _) (sym law2)))
                               ass))
             law3)
  where open Fun J; open RMonad RM; open Cat D

open import Functors.FullyFaithful
open import Isomorphism
-- if J is fully faithful then any suitably typed functor is a module for trivial rel monad given by J
ModF : forall {a}{b}{c}{d}{C : Cat {a}{b}}{D : Cat {c}{d}}
       {J : Fun C D} → Full J → Faithful J → (F : Fun C D) -> Mod (trivRM J)
ModF {C = C}{D = D}{J = J} P Q F = mod
  (OMap F)
  (λ f → HMap F (fst (P f)))
  (trans (cong (HMap F) (Q (trans (snd (P (Cat.iden D))) (sym $ fid J)))) (fid F))
  (trans (cong (HMap F)
               (Q (trans (snd (P _))
                         (trans (cong₂ (Cat.comp D)
                                       (sym $ snd (P _))
                                       (sym $ snd (P _)))
                                (sym $ fcomp J)))))
         (fcomp F)) where open Fun

-- any rel. monad is trivially a module over itself, 'tautalogical module'
ModRM : forall {a}{b}{c}{d}{C : Cat {a}{b}}{D : Cat {c}{d}}
       {J : Fun C D} (RM : RMonad J) -> Mod RM
ModRM RM = mod T bind law1 law3 where open RMonad RM


{-
This second-order signature was created from the following second-order syntax description:

syntax Combinatory | CL

type
  * : 0-ary

term
  app : *  *  ->  * | _$_ l20
  i   : *
  k   : *
  s   : *

theory
  (IA) x     |> app (i, x) = x
  (KA) x y   |> app (app(k, x), y) = x
  (SA) x y z |> app (app (app (s, x), y), z) = app (app(x, z), app(y, z))
-}

module Combinatory.Signature where

open import SOAS.Context

open import SOAS.Common

open import SOAS.Syntax.Signature *T public
open import SOAS.Syntax.Build *T public

-- Operator symbols
data CLₒ : Set where
  appₒ iₒ kₒ sₒ : CLₒ

-- Term signature
CL:Sig : Signature CLₒ
CL:Sig = sig λ
  {  appₒ  → (⊢₀ *) , (⊢₀ *) ⟼₂ *
  ;  iₒ    → ⟼₀ *
  ;  kₒ    → ⟼₀ *
  ;  sₒ    → ⟼₀ *
  }

open Signature CL:Sig public


{-# OPTIONS --cubical #-}

module Type.Cubical.Path.Proofs where

import      Lvl
open import Type
open import Type.Cubical
open import Type.Cubical.Path

private variable ℓ ℓ₁ ℓ₂ : Lvl.Level

module _ where
  private variable A B : Type{ℓ}
  private variable P : Interval → Type{ℓ}
  private variable x y z w : A

  -- The full path from the starting point to the end.
  path : (point : Interval → A) → Path(point(Interval.𝟎)) (point(Interval.𝟏))
  path point i = point i

  -- The point on the path, given a point of the interval indexing the path.
  pointOn : ∀{x y : A} → Path x y → (Interval → A)
  pointOn p i = p i

  -- The path from a point to itself.
  -- This path only consists of the point itself.
  point : Path x x
  point{x = x} _ = x

  -- The reverse path of a path.
  -- Reverses the direction of a path by flipping all points around the point of symmetry.
  reverseP : PathP(P) x y → PathP(\i → P(Interval.flip i)) y x
  reverseP p(i) = p(Interval.flip i)

  reverse : Path x y → Path y x
  reverse = reverseP

  -- A function mapping points between two spaces, given a path between the spaces.
  spaceMap : Path A B → (A → B)
  spaceMap p = Interval.transp (pointOn p) Interval.𝟎

  -- TODO: Move
  module _ {A : Type{ℓ}} where
    private variable a₀ a₁ a₂ a₃ : A
    private variable a₀₀ a₀₁ a₁₀ a₁₁ : A
    private variable a₀₀₀ a₀₀₁ a₀₁₀ a₀₁₁ a₁₀₀ a₁₀₁ a₁₁₀ a₁₁₁ : A
    private variable p₀₀₋ p₀₁₋ p₀₋₀ p₀₋₁ p₁₀₋ p₁₁₋ p₁₋₀ p₁₋₁ p₋₀₀ p₋₀₁ p₋₁₀ p₋₁₁ : Path a₀ a₁

    Path-missingSide : A → A
    Path-missingSide a = Interval.hComp diag a where
      diag : Interval → Interval.Partial Interval.𝟎 A
      diag i ()

    module _
      (p₀₋ : Path a₀₀ a₀₁)
      (p₁₋ : Path a₁₀ a₁₁)
      (p₋₀ : Path a₀₀ a₁₀)
      (p₋₁ : Path a₀₁ a₁₁)
      where
      -- a₀₁ → p₋₁ → a₁₁
      --  ↑           ↑
      -- p₀₋         p₁₋
      --  ↑           ↑
      -- a₀₀ → p₋₀ → a₁₀
      Square : Type
      Square = PathP (\i → Path (p₋₀ i) (p₋₁ i)) p₀₋ p₁₋
    module Square where
      missingSide : Path a₀₀ a₀₁ → Path a₁₀ a₁₁ → Path a₀₀ a₁₀ → Path a₀₁ a₁₁
      missingSide p₀₋ p₁₋ p₋₀ ix = Interval.hComp diagram (p₋₀ ix) where
        diagram : Interval → Interval.Partial(Interval.max ix (Interval.flip ix)) A
        diagram iy (ix = Interval.𝟎) = p₀₋ iy
        diagram iy (ix = Interval.𝟏) = p₁₋ iy

      module _
        {p₀₋ : Path a₀₀ a₀₁}
        {p₁₋ : Path a₁₀ a₁₁}
        {p₋₀ : Path a₀₀ a₁₀}
        {p₋₁ : Path a₀₁ a₁₁}
        (s : Square p₀₋ p₁₋ p₋₀ p₋₁)
        where

        diagonal : Path a₀₀ a₁₁
        diagonal i = s i i

        rotate₊ : Square p₋₁ p₋₀ (reverse p₀₋) (reverse p₁₋)
        rotate₊ iy ix = s ix (Interval.flip iy)

        rotate₋ : Square (reverse p₋₀) (reverse p₋₁) p₁₋ p₀₋
        rotate₋ iy ix = s (Interval.flip ix) iy

        flipX : Square p₁₋ p₀₋ (reverse p₋₀) (reverse p₋₁)
        flipX iy ix = s (Interval.flip iy) ix

        flipY : Square (reverse p₀₋) (reverse p₁₋) p₋₁ p₋₀
        flipY iy ix = s iy (Interval.flip ix)

      module _
        {a₀ a₁ : A}
        (p : Path a₀ a₁)
        where

        lineX : Square point point p p
        lineX ix iy = p ix

        lineY : Square p p point point
        lineY ix iy = p iy

        min : Square point p point p
        min ix iy = p(Interval.min ix iy)

        max : Square p point p point
        max ix iy = p(Interval.max ix iy)

    module _
      (p₀₋₋ : Square p₀₀₋ p₀₁₋ p₀₋₀ p₀₋₁)
      (p₁₋₋ : Square p₁₀₋ p₁₁₋ p₁₋₀ p₁₋₁)
      (p₋₀₋ : Square p₀₀₋ p₁₀₋ p₋₀₀ p₋₀₁)
      (p₋₁₋ : Square p₀₁₋ p₁₁₋ p₋₁₀ p₋₁₁)
      (p₋₋₀ : Square p₀₋₀ p₁₋₀ p₋₀₀ p₋₁₀)
      (p₋₋₁ : Square p₀₋₁ p₁₋₁ p₋₀₁ p₋₁₁)
      where

      Cube : Type
      Cube = PathP (\i → Square (p₋₀₋ i) (p₋₁₋ i) (p₋₋₀ i) (p₋₋₁ i)) p₀₋₋ p₁₋₋
    {-
    module Cube where
      module _
        (p₀₋₋ : Square p₀₀₋ p₀₁₋ p₀₋₀ p₀₋₁)
        (p₁₋₋ : Square p₁₀₋ p₁₁₋ p₁₋₀ p₁₋₁)
        (p₋₀₋ : Square p₀₀₋ p₁₀₋ p₋₀₀ p₋₀₁)
        (p₋₁₋ : Square p₀₁₋ p₁₁₋ p₋₁₀ p₋₁₁)
        (p₋₋₀ : Square p₀₋₀ p₁₋₀ p₋₀₀ p₋₁₀)
        where
        missingSide : Square p₀₋₁ p₁₋₁ p₋₀₁ p₋₁₁
        missingSide ix iy = Interval.hComp diagram (p₋₋₀ ix iy) where -- (Square.max {!!} ix iy)
          diagram : Interval → Interval.Partial {!!} A
          {-diagram : (i : Interval) → Interval.Partial (Interval.max (Interval.max ix (Interval.flip ix)) (Interval.max iy (Interval.flip iy))) _
          diagram iz (ix = Interval.𝟎) = Square.max p₀₋₁ ix iy
          diagram iz (ix = Interval.𝟏) = Square.min p₁₋₁ ix iy
          diagram iz (iy = Interval.𝟎) = Square.max p₋₀₁ ix iy
          diagram iz (iy = Interval.𝟏) = Square.min p₋₁₁ ix iy-}
    -}

  -- Concatenates (joins the ends of) two paths.
  concat : Path x y → Path y z → Path x z
  concat xy yz = Square.missingSide point yz xy

module _ where
  private variable X : Type{ℓ}
  private variable Y : X → Type{ℓ}

  -- Maps a path into another space.
  mapP : (f : (x : X) → Y(x)) → ∀{x y} → (path : Path x y) → PathP(\i → Y(path(i))) (f(x)) (f(y))
  mapP(f) p(i) = f(p(i))

  -- When there is a path between two space mappings.
  mapping : ∀{f g : (x : X) → Y(x)} → (∀{x} → Path(f(x)) (g(x))) → Path f g
  mapping ppt i x = ppt{x} i

  mappingPoint : ∀{f g : (x : X) → Y(x)} → Path f g → (∀{x} → Path(f(x)) (g(x)))
  mappingPoint pfg {x} i = pfg i x

module _ where
  private variable X : Type{ℓ}
  private variable Y : X → Type{ℓ}
  private variable Z : (x : X) → Y(x) → Type{ℓ}

  -- mapP₂' : (f : (x : X) → (y : Y(x)) → Z(x)(y)) → ∀{a₁ b₁ : X}{a₂ : Y(a₁)}{b₂ : Y(b₁)} → (path₁ : Path a₁ b₁) → (path₂ : Path a₂ b₂) → PathP(\i → Z(path₁(i))(?)) (f a₁ a₂) (f b₁ b₂)

  mapP₂ : (f : (x : X) → (y : (x : X) → Y(x)) → Z(x)(y)) → ∀{a₁ b₁ : X}{a₂ b₂ : (x : X) → Y(x)} → (path₁ : Path a₁ b₁) → (path₂ : Path a₂ b₂) → PathP(\i → Z(path₁(i))(path₂(i))) (f a₁ a₂) (f b₁ b₂)
  mapP₂ f ab1 ab2 i = mapP (mapP f ab1 i) ab2 i

  -- mapP₂ : (f : (x : X) → (y : Y(x)) → Z(x)(y)) → ∀{x₁ x₂} → (path : Path x₁ x₂) → PathP(\i → Y(path(i))) (f(x)) (f(y))
  -- mapP₂(f) = ?

module _ where
  private variable X X₁ X₂ Y Y₁ Y₂ : Type{ℓ}

  map : (f : X → Y) → ∀{a b} → Path a b → Path (f(a)) (f(b))
  map = mapP

  map₂ : (f : X₁ → X₂ → Y) → ∀{a₁ b₁}{a₂ b₂} → Path a₁ b₁ → Path a₂ b₂ → Path (f a₁ a₂) (f b₁ b₂)
  map₂ f ab1 ab2 i = mapP (mapP f ab1 i) ab2 i

  liftedSpaceMap : (S : X → Type{ℓ}) → ∀{a b} → Path a b → S(a) → S(b)
  liftedSpaceMap S p = spaceMap(map S p)

  liftedSpaceMap₂ : (S : X → Y → Type{ℓ}) → ∀{a₁ b₁}{a₂ b₂} → Path a₁ b₁ → Path a₂ b₂ → S a₁ a₂ → S b₁ b₂
  liftedSpaceMap₂ S p q = spaceMap(map₂ S p q)

-- TODO: Move
data Loop{ℓ} : Type{ℓ} where
  base : Loop
  loop : Path base base


{-# OPTIONS --without-K #-}
open import Types

module J {a c} {A : Set a} (C : (x y : A) → x ≡ y → Set c) where

open import GroupoidStructure
open import PathOperations
open import PathStructure.Sigma
open import Transport

γ : {x y : A} (p : x ≡ y) →
  Id (Σ A λ y → x ≡ y) (x , refl) (y , p)
γ p = merge-path (p , tr-post _ p refl · id·p _)

justification : {x y : A} (p : x ≡ y) → C x x refl → C x y p
justification p = tr (λ z → C _ (π₁ z) (π₂ z)) (γ p)


open import Prelude
open import Nat
open import dynamics-core
open import contexts

module lemmas-gcomplete where
  -- if you add a complete type to a complete context, the result is also a
  -- complete context
  gcomp-extend : ∀{Γ τ x} → Γ gcomplete → τ tcomplete → x # Γ → (Γ ,, (x , τ)) gcomplete
  gcomp-extend {Γ} {τ} {x} gc tc apart x_query τ_query x₁ with natEQ x x_query
  gcomp-extend {Γ} {τ} {x} gc tc apart .x τ_query x₂ | Inl refl = tr (λ qq → qq tcomplete) (someinj x₂) tc
  gcomp-extend {Γ} {τ} {x} gc tc apart x_query τ_query x₂ | Inr x₁ = gc x_query τ_query x₂


module Generic.Test.Data.Fin where

open import Generic.Main

import Data.Fin as Fin

Fin : ℕ -> Set
Fin = readData Fin.Fin

pattern fzero {n}   = #₀  (relv n , lrefl)
pattern fsuc  {n} i = !#₁ (relv n , i , lrefl)

elimFin : ∀ {n π}
        -> (P : ∀ {n} -> Fin n -> Set π)
        -> (∀ {n} {i : Fin n} -> P i -> P (fsuc i))
        -> (∀ {n} -> P {suc n} fzero)
        -> (i : Fin n)
        -> P i
elimFin P f x  fzero   = x
elimFin P f x (fsuc i) = f (elimFin P f x i)


module Serializer.VecBool where

open import Data.Fin hiding (_+_)
open import Data.Nat
open import Data.Vec
open import Data.Bool
open import Function using (_∘_ ; _$_ ; _∋_ ; id ; const)
open import Function.Bijection
open import Function.Injection
open import Function.Surjection
open import Function.Equality using (_⟶_ ;  _⟨$⟩_ ; Π )
open import Function.LeftInverse hiding (_∘_)
open import Relation.Binary
open import Relation.Binary.EqReasoning
open import Relation.Binary.PropositionalEquality using (_≡_ ; refl ; cong ; setoid )
import Relation.Binary.Indexed as I

open import Serializer
open import Helper.Fin
open import Serializer.Bool

open Serializer.Serializer {{...}}

2^ : ℕ -> ℕ
2^ zero = 1
2^ (suc b) = 2 * 2^ b

--fromVec : ∀ {a n} {A : Set a} -> Vec A n -> Fin n
fromVec : ∀ {n} -> Vec Bool n -> Fin (2^ n)
fromVec {zero} [] = zero
fromVec {suc n} (x ∷ x₁) = × 2 (2^ n) (from x) (fromVec x₁)

toVec` : ∀ {n} -> Fin (2^ n) -> Vec Bool n
toVec` {zero} x = []
toVec` {suc n} x with ⨂ 2 (2^ n) x
toVec` {suc n} .(× 2 (2^ n) i j) | is× i j = (to i) ∷ (toVec` j)

vec-from-cong : ∀ {n} -> Setoid._≈_ (setoid (Vec Bool n)) I.=[ fromVec ]⇒ Setoid._≈_ (setoid (Fin (2^ n)))
vec-from-cong refl = refl

vec-from-preserves-eq : ∀ {n} -> setoid (Vec Bool n) ⟶ setoid (Fin (2^ n))
vec-from-preserves-eq = record { _⟨$⟩_ = fromVec ; cong = vec-from-cong }

vec-from-injective : ∀ {n} -> Injective (vec-from-preserves-eq {n})
vec-from-injective {zero} {[]} {[]} p with fromVec []
... | p2 = refl
vec-from-injective {suc n} {x ∷ x₁} {y ∷ y₁} p with (⨂ 2 (2^ n) (fromVec (x ∷ x₁))) | (⨂ 2 (2^ n) (fromVec (y ∷ y₁)))
... | a1 | b2 with vec-from-injective {n} {x₁} {y₁}
... | c with c (×-equal₁ {x₁ = (fromBool x)} {x₂ = (fromBool y)} p) | (×-equal₂ {x₁ = (fromBool x)} {x₂ = (fromBool y)} p)
... | p1 | p2 with from-bool-injective p2
vec-from-injective {suc n} {x ∷ x₁} {.x ∷ .x₁} p | a1 | b2 | c | refl | p2 | refl = refl

vec-from-surjective : ∀ {n} -> Surjective (vec-from-preserves-eq {n})
vec-from-surjective {n} = record { from = preserves-eq-inv ; right-inverse-of = (inv {n}) }
  where
    cong-inverse : ∀ {n} -> Setoid._≈_ (setoid (Fin (2^ n))) I.=[ toVec` ]⇒ Setoid._≈_ (setoid (Vec Bool n))
    cong-inverse refl = refl

    preserves-eq-inv : ∀ {n} -> setoid (Fin  (2^ n)) ⟶ setoid (Vec Bool n)
    preserves-eq-inv = record { _⟨$⟩_ = toVec` ; cong = cong-inverse }

    inv : ∀ {n} -> (preserves-eq-inv {n}) RightInverseOf (vec-from-preserves-eq {n})
    inv {zero} zero = refl
    inv {zero} (suc ())
    inv {suc n₁} x with ⨂ 2 (2^ n₁) x
    inv {suc n₁} .(× 2 (2^ n₁) i j) | is× i j
      rewrite (Surjective.right-inverse-of (Bijective.surjective (Bijection.bijective (bijection {Bool})))) i | (inv {n₁} j) = refl


------------------------------------------------------------------------------
-- Inequalities on partial natural numbers
------------------------------------------------------------------------------

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

module FOTC.Data.Nat.Inequalities where

open import FOTC.Base

infix 4 _<_ _≮_ _>_ _≯_ _≤_ _≰_ _≥_ _≱_

------------------------------------------------------------------------------
-- The function terms.

postulate
  lt    : D → D → D
  lt-00 : lt zero zero                   ≡ false
  lt-0S : ∀ n → lt zero (succ₁ n)        ≡ true
  lt-S0 : ∀ n → lt (succ₁ n) zero        ≡ false
  lt-SS : ∀ m n → lt (succ₁ m) (succ₁ n) ≡ lt m n
{-# ATP axioms lt-00 lt-0S lt-S0 lt-SS #-}

le : D → D → D
le m n = lt m (succ₁ n)
{-# ATP definition le #-}

gt : D → D → D
gt m n = lt n m
{-# ATP definition gt #-}

ge : D → D → D
ge m n = le n m
{-# ATP definition ge #-}

------------------------------------------------------------------------
-- The relations.

_<_ : D → D → Set
m < n = lt m n ≡ true
{-# ATP definition _<_ #-}

_≮_ : D → D → Set
m ≮ n = lt m n ≡ false
{-# ATP definition _≮_ #-}

_>_ : D → D → Set
m > n = gt m n ≡ true
{-# ATP definition _>_ #-}

_≯_ : D → D → Set
m ≯ n = gt m n ≡ false
{-# ATP definition _≯_ #-}

_≤_  : D → D → Set
m ≤ n = le m n ≡ true
{-# ATP definition _≤_ #-}

_≰_  : D → D → Set
m ≰ n = le m n ≡ false
{-# ATP definition _≰_ #-}

_≥_ : D → D → Set
m ≥ n = ge m n ≡ true
{-# ATP definition _≥_ #-}

_≱_ : D → D → Set
m ≱ n = ge m n ≡ false
{-# ATP definition _≱_ #-}

------------------------------------------------------------------------------
-- The lexicographical order.
Lexi : D → D → D → D → Set
Lexi m n m' n' = m < m' ∨ m ≡ m' ∧ n < n'
{-# ATP definition Lexi #-}


------------------------------------------------------------------------
-- The Agda standard library
--
-- An example of how the Record module can be used
------------------------------------------------------------------------

-- Taken from Randy Pollack's paper "Dependently Typed Records in Type
-- Theory".

module README.Record where

open import Data.Product
open import Data.String
open import Function using (flip)
open import Level
import Record
open import Relation.Binary

-- Let us use strings as labels.

open Record String _≟_

-- Partial equivalence relations.

PER : Signature _
PER = ∅ , "S"     ∶ (λ _ → Set)
        , "R"     ∶ (λ r → r · "S" → r · "S" → Set)
        , "sym"   ∶ (λ r → Lift (Symmetric (r · "R")))
        , "trans" ∶ (λ r → Lift (Transitive (r · "R")))

-- Given a PER the converse relation is also a PER.

converse : (P : Record PER) →
           Record (PER With "S" ≔ (λ _ → P · "S")
                       With "R" ≔ (λ _ → flip (P · "R")))
converse P =
  Rec′⇒Rec
    (PER With "S" ≔ (λ _ → P · "S")
         With "R" ≔ (λ _ → flip (P · "R")))
    ((((_ ,) ,) , lift λ {_} → lower (P · "sym")) ,
     (lift λ {_} yRx zRy → lower (P · "trans") zRy yRx))


{-# OPTIONS --safe #-}
module Cubical.Algebra.CommAlgebra.Kernel where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Equiv

open import Cubical.Algebra.CommRing.Base
open import Cubical.Algebra.CommRing.Ideal using (Ideal→CommIdeal)
open import Cubical.Algebra.Ring.Kernel using () renaming (kernelIdeal to ringKernel)
open import Cubical.Algebra.CommAlgebra.Base
open import Cubical.Algebra.CommAlgebra.Properties
open import Cubical.Algebra.CommAlgebra.Ideal

private
  variable
    ℓ : Level

module _ {R : CommRing ℓ} (A B : CommAlgebra R ℓ) (ϕ : CommAlgebraHom A B) where

  kernel : IdealsIn A
  kernel = Ideal→CommIdeal (ringKernel (CommAlgebraHom→RingHom {A = A} {B = B} ϕ))


{-# OPTIONS --without-K #-}
module sets.vec.core where

open import function.core
open import sets.nat.core using (ℕ; zero; suc)
open import sets.fin

data Vec {i}(A : Set i) : ℕ → Set i where
  [] : Vec A 0
  _∷_ : ∀ {n} → A → Vec A n → Vec A (suc n)
infixr 5 _∷_

head : ∀ {i}{A : Set i}{n : ℕ} → Vec A (suc n) → A
head (x ∷ _) = x

tail : ∀ {i}{A : Set i}{n : ℕ} → Vec A (suc n) → Vec A n
tail (_ ∷ xs) = xs

tabulate : ∀ {i}{A : Set i}{n : ℕ}
         → (Fin n → A) → Vec A n
tabulate {n = zero} _ = []
tabulate {n = suc m} f = f zero ∷ tabulate (f ∘ suc)

lookup : ∀ {i}{A : Set i}{n : ℕ}
       → Vec A n → Fin n → A
lookup [] ()
lookup (x ∷ xs) zero = x
lookup (x ∷ xs) (suc i) = lookup xs i

_!_ : ∀ {i}{A : Set i}{n : ℕ}
    → Vec A n → Fin n → A
_!_ = lookup


module Cats.Category.Constructions.Epi where

open import Level

open import Cats.Category.Base

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

  private open module Cat = Category Cat
  open Cat.≈-Reasoning

  IsEpi : ∀ {A B} → A ⇒ B → Set (lo ⊔ la ⊔ l≈)
  IsEpi {A} {B} f = ∀ {C} {g h : B ⇒ C} → g ∘ f ≈ h ∘ f → g ≈ h


{-# OPTIONS --safe --warning=error --cubical --without-K #-}

open import Agda.Primitive
open import Cubical.Core.Everything
open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Isomorphism
open import Naturals
open import Basic

module File1 where

{-
Let's define the integers. Answers can be found in commit 4d7aad52e014daeb48fe5fb6854d3b6e0080012a.

If you want to learn how to type something, remember the Emacs action is M-x describe-char with the cursor on a character.
-}
data ℤ : Set where
  pos : ℕ → ℤ
  neg : ℕ → ℤ
  congZero : pos 0 ≡ neg 0

{-
A bit odd! We defined an integer to be "a natural tagged as positive", "a natural tagged as negative", or "a witness that pos 0 = neg 0".
This is obvious nonsense (how can two things be equal if they were made from different constructors?! what even does it mean for "pos 0 ≡ neg 0" to be an integer?!) until you let go of your preconceptions about what it means for two things to be equal.
-}

-- Just to help you realise how odd this is, we'll use some new Cubical definitions to manifest a really weird element of ℤ which is neither a `pos` nor a `neg`. Don't worry too much about this for now.
oddThing : ℤ
oddThing = congZero i1

-- Even odder, the integer we just made is equal to 0. Think how strange this is: it's not built using the only constructor that could build `pos 0`, but it's still equal to `pos 0`.
oddThing2 : pos 0 ≡ oddThing
oddThing2 = congZero

{-
Add 1 to an integer. The first cases are easy, but the last case is very odd and we will discuss it in detail.
-}
incr : ℤ → ℤ
incr (pos x) = pos (succ x)
incr (neg zero) = pos 1
incr (neg (succ x)) = neg x

-- First of all, we have in scope an "integer" `congZero` which is of type `pos 0 ≡ neg 0` - huh?!
incr (congZero i) = {!!}
{-
This one looks different from the non-cubical world.

Goal: ℤ
———— Boundary ——————————————————————————————————————————————
i = i0 ⊢ pos 1
i = i1 ⊢ pos 1
————————————————————————————————————————————————————————————
i : I
———— Constraints ———————————————————————————————————————————
pos 1 = ?0 (i = i1) : ℤ
pos 1 = ?0 (i = i0) : ℤ

The constraints look pretty normal, telling you that goal 0 needs to be equal to `pos 1`.
But there's stuff about this `i : I` here. What's that?

Recall that Cubical alters the definition of ≡. Given any element of X, an element of X ≡ Y contains content for how that element corresponds to a Y, and vice versa.
Homotopy type theory, and Cubical Agda, prefer to think of a more general structure on top of that data, though: not only can we go from an X to a Y, but we also store *how* we get there.
More abstractly, we store how to traverse a path from X to Y. The `i` here is telling us where along that path we are; the template goes from the special symbol `i0` to the special symbol `i1`, and `i` is notionally somewhere between those two points.

This gives some nice composability properties: if we know that X ≡ Y and that Y ≡ Z, we can compose the two paths to get a path X ≡ Z.
Cubical Agda's great innovation is that all this has computational content: proofs about X can be transported along the path to obtain for free proofs about Z.

Having told Agda that `incr (pos x) = pos (succ x)`, it can infer that `incr (pos 0) = pos 1`.
So our goal of constructing a path `incr congZero : incr (pos 0) ≡ incr (neg 0)` is in fact the requirement to construct a path `pos 1 ≡ incr (neg 0)`.
Similarly, we told it that `incr (neg 0) = pos 1`, so our path `incr congZero` is actually of type `pos 1 ≡ pos 1`
The `I` from the goal is the template of the path `congZero n` which we are trying to construct from `pos 1` to `pos 1`.
The `i0` and `i1` are the templates for the start and end points of the path, i.e. they both indicate `pos 1`.

What we really wanted to write here was `incr congZero`. For reasons I don't understand, Agda doesn't accept this.
We are forced to descend to the level of actually implementing the path from `pos 1` to `pos 1` by telling Agda where each `i` goes, even though there's an obvious way (`refl`) to specify the entire path at once.

Agda can automatically infer the correct hole-fill, but you can try it with `pos 1`.
-}

{-
Let's show that successors of naturals are nonzero.
We do this using a new technique that only makes sense in Cubical-land.

Given a path from `succ a` to `0`, we need to show False.
The assumed member of `succ a ≡ 0` is a path that tells us how to get from `succ a` to `0` in a way that is preserved by function application.
That is, it tells us how to show `f (succ a) ≡ f 0` for any `f`. (The function `subst` captures this ability to move functions from one end of a path to the other.)
So if we pick `f` appropriately so that it outputs `False` at 0 but something else (anything else, as long as we can construct it!) at `succ a`, we will be done.
-}
succNonzero : {a : ℕ} → succ a ≡ 0 → False
succNonzero {a} sa=0 = subst f sa=0 {!!}
  where
    f : ℕ → Set
    f zero = False
    f (succ i) = {!!}

{-
Let's show that these equality things aren't *too* weird. We'll show that even though `pos 0` is equal to `congZero i1`, it's actually the *only* other `pos` which is equal to `congZero i1`. (You'd hope this would be the case; otherwise we'd have several `pos` being equal to each other.)

Cubical Agda gives us a magical way to do this. Recall that we can move functions along paths (the `subst` from above, which lets us take an `x ≡ y` and an `f` to get `f x → f y`); we can actually also do the same thing viewed as transforming the *entire path* `x ≡ y` using `f` into `f x ≡ f y`.
This is the function `cong`.

If we can somehow create a function that "strips off `pos`", then that function can transform the path `pos a ≡ pos b` into a path `a ≡ b`.
-}
toNat : ℤ → ℕ
toNat z = {!!}

posInjective : {a b : ℕ} → pos a ≡ pos b → a ≡ b
posInjective pr = cong toNat pr

{-
Finally, we can use the injectivity of `pos` in the obvious way to prove that only `pos 0` is `congZero i1`.
-}
notTooOdd : (n : ℕ) → (pos n ≡ congZero i1) → n ≡ 0
notTooOdd zero pr = refl
notTooOdd (succ n) pr = posInjective (pr ∙ sym congZero)

{-
Let's show that `pos` is injective in a different way, inspired by how you might do this kind of thing in a non-Cubical world. We'll go by induction, which means the interesting case will be `pos (succ a) ≡ pos (succ b) → pos a ≡ pos b`.

This is a classic example of moving a path wholesale along a function; in this case, the function is "decrement".
-}
decr : ℤ → ℤ -- TODO hole this
decr z = {!!}

posDecr : {a b : ℕ} → pos (succ a) ≡ pos (succ b) → pos a ≡ pos b
posDecr {a} {b} pr = cong decr pr

posInjective' : {a b : ℕ} → pos a ≡ pos b → a ≡ b
-- The two easy cases first
posInjective' {zero} {zero} x = refl
posInjective' {succ a} {succ b} x = cong succ (posInjective (posDecr x))
{-
We need to transform `pos zero ≡ pos (succ b)` into `zero ≡ succ b`.
We could do this using the proof of `posInjective` - moving the path along `toNat` - but it is also interesting to construct the path `zero ≡ succ b` manually using `subst`.

To use `subst`, we need to construct a function from ℤ which is "a type we can populate" at `pos zero` (i.e. at one end of the path), and `zero ≡ succ b` at `pos (succ b)` (i.e. at the other end of the path); then we will be able to move from the populated type to `zero ≡ succ b` using that function.

These two are quite hard!
-}
posInjective' {zero} {succ b} x = subst t x {!!}
  where
    t : ℤ → Set
    t z = {!!}
posInjective' {succ a} {zero} x = subst t x {!!}
  where
    t : ℤ → Set
    t z = {!!}

{-
What does equality mean on the level of types, rather than values?
Univalence, the axiom which Cubical provides computational meaning to, basically says "identity really means isomorphism".
I will gloss over a lot of detail there, but here is a witness to `ℤ ≡ ℤ` that is not `refl`!
-}
incrDecrInverse1 : (a : ℤ) → incr (decr a) ≡ a
incrDecrInverse1 (pos zero) = sym congZero
incrDecrInverse1 (pos (succ x)) = refl
incrDecrInverse1 (neg x) = refl
{-
The remaining case is worth talking about.
We need a path from incr (decr (congZero j)) to congZero j.
That is, we need a path from incr (neg 1) to congZero j; i.e. from neg 0 to congZero j.
We already have a path from neg 0 to pos 0 - namely `sym congZero`.
But `sym congZero` goes from `neg 0` to every point along `sym congZero` - and `congZero j` is also a point along `sym congZero`, because `sym` just means "reverse the path"!
So we can get the path we need by truncating `sym congZero`, using the `∧` ("min") operation (a special builtin primitive in Cubical).
The path, parameterised by `i` with `j` fixed, is "go backwards (`sym`) along `congZero` to the min of `i` and where `j` lies on this path (`~ j`), but then don't go any further".
Here, `~` means essentially "negate"; if you got to position `i` travelling forward on path `p`, then you got to position `~ i` travelling backward on path `sym p`.

This is probably best done if you draw a little picture.
-}
incrDecrInverse1 (congZero j) i = sym congZero (i ∧ (~ j))

incrDecrInverse2 : (a : ℤ) → decr (incr a) ≡ a
incrDecrInverse2 x = {!!}

incrIso : ℤ ≡ ℤ
incrIso = isoToPath (iso incr decr incrDecrInverse1 incrDecrInverse2)

{-
Unexpected! Cubical has let us prove that ℤ ≡ ℤ in a very nonstandard way. What use is this? Who knows.
-}


{-
  This is mostly for convenience, when working with ideals
  (which are defined for general rings) in a commutative ring.
-}
{-# OPTIONS --safe #-}
module Cubical.Algebra.CommRing.Ideal where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Function
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Powerset

open import Cubical.Data.Nat using (ℕ ; zero ; suc)
open import Cubical.Data.FinData

open import Cubical.Algebra.CommRing.Base
open import Cubical.Algebra.Ring.Ideal renaming (IdealsIn to IdealsInRing)
open import Cubical.Algebra.Ring.BigOps
open import Cubical.Algebra.RingSolver.ReflectionSolving

private
  variable
    ℓ : Level

IdealsIn : (R : CommRing ℓ) → Type _
IdealsIn R = IdealsInRing (CommRing→Ring R)

module _ (Ring@(R , str) : CommRing ℓ) where
  open CommRingStr str
  makeIdeal : (I : R → hProp ℓ)
              → (+-closed : {x y : R} → x ∈ I → y ∈ I → (x + y) ∈ I)
              → (0r-closed : 0r ∈ I)
              → (·-closedLeft : {x : R} → (r : R) → x ∈ I → r · x ∈ I)
              → IdealsIn (R , str)
  makeIdeal I +-closed 0r-closed ·-closedLeft = I ,
    (record
       { +-closed = +-closed
       ; -closed = λ x∈I → subst-∈ I (useSolver _)
                             (·-closedLeft (- 1r) x∈I)
       ; 0r-closed = 0r-closed
       ; ·-closedLeft = ·-closedLeft
       ; ·-closedRight = λ r x∈I →
                       subst-∈ I
                             (·-comm r _)
                             (·-closedLeft r x∈I)
       })
       where useSolver : (x : R) → - 1r · x ≡ - x
             useSolver = solve Ring

-- better?
module _ (R' : CommRing ℓ) where
 private R = fst R'
 open CommRingStr (snd R')
 open Sum (CommRing→Ring R')

 record isCommIdeal (I : ℙ R) : Type ℓ where
  constructor
   makeIsCommIdeal
  field
   +Closed : ∀ {x y : R} → x ∈ I → y ∈ I → (x + y) ∈ I
   contains0 : 0r ∈ I
   ·Closed : ∀ {x : R} (r : R) → x ∈ I → r · x ∈ I

 open isCommIdeal
 isPropIsCommIdeal : (I : ℙ R) → isProp (isCommIdeal I)
 +Closed (isPropIsCommIdeal I ici₁ ici₂ i) x∈I y∈I =
         I _ .snd (ici₁ .+Closed x∈I y∈I) (ici₂ .+Closed x∈I y∈I) i
 contains0 (isPropIsCommIdeal I ici₁ ici₂ i) = I 0r .snd (ici₁ .contains0) (ici₂ .contains0) i
 ·Closed (isPropIsCommIdeal I ici₁ ici₂ i) r x∈I =
         I _ .snd (ici₁ .·Closed r x∈I) (ici₂ .·Closed r x∈I) i

 CommIdeal : Type _
 CommIdeal = Σ[ I ∈ ℙ R ] isCommIdeal I

 ∑Closed : (I : CommIdeal) {n : ℕ} (V : FinVec R n)
         → (∀ i → V i ∈ fst I) → ∑ V ∈ fst I
 ∑Closed I {n = zero} _ _ = I .snd .contains0
 ∑Closed I {n = suc n} V h = I .snd .+Closed (h zero) (∑Closed I (V ∘ suc) (h ∘ suc))


{-# OPTIONS --without-K #-}
module sets.finite.core where

open import sum
open import function.isomorphism
open import sets.nat
open import sets.fin

IsOfSize : ∀ {i} → Set i → ℕ → Set i
IsOfSize A n = A ≅ Fin n

IsFinite : ∀ {i} → Set i → Set i
IsFinite A = Σ ℕ (IsOfSize A)

mk-is-finite : ∀ {i n}{A : Set i}
             → (A ≅ Fin n)
             → IsFinite A
mk-is-finite {n = n} enum = n , enum

fin-is-finite : ∀ {n} → IsFinite (Fin n)
fin-is-finite {n} = mk-is-finite refl≅

⊎-of-size : ∀ {i j n m}{A : Set i}{B : Set j}
          → IsOfSize A n
          → IsOfSize B m
          → IsOfSize (A ⊎ B) (n + m)
⊎-of-size isoA isoB = ⊎-ap-iso isoA isoB ·≅ fin-sum

⊎-finite : ∀ {i j}{A : Set i}{B : Set j}
         → IsFinite A → IsFinite B
         → IsFinite (A ⊎ B)
⊎-finite (_ , fA) (_ , fB) = mk-is-finite (⊎-of-size fA fB)

×-of-size : ∀ {i j n m}{A : Set i}{B : Set j}
          → IsOfSize A n
          → IsOfSize B m
          → IsOfSize (A × B) (n * m)
×-of-size isoA isoB = ×-ap-iso isoA isoB ·≅ fin-prod

×-finite : ∀ {i j}{A : Set i}{B : Set j}
         → IsFinite A → IsFinite B
         → IsFinite (A × B)
×-finite (_ , fA) (_ , fB) = mk-is-finite (×-of-size fA fB)


{-# OPTIONS --cubical --no-import-sorts --safe #-}
module Cubical.HITs.Rationals.SigmaQ.Base where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.HLevels

open import Cubical.HITs.Ints.QuoInt

open import Cubical.Data.Nat as ℕ hiding (_·_)
open import Cubical.Data.NatPlusOne
open import Cubical.Data.Sigma

open import Cubical.Data.Nat.GCD
open import Cubical.Data.Nat.Coprime

-- ℚ as the set of coprime pairs in ℤ × ℕ₊₁

ℚ : Type₀
ℚ = Σ[ (a , b) ∈ ℤ × ℕ₊₁ ] areCoprime (abs a , ℕ₊₁→ℕ b)

isSetℚ : isSet ℚ
isSetℚ = isSetΣ (isSet× isSetℤ (subst isSet 1+Path isSetℕ)) (λ _ → isProp→isSet isPropIsGCD)

signedPair : Sign → ℕ × ℕ₊₁ → ℤ × ℕ₊₁
signedPair s (a , b) = (signed s a , b)

[_] : ℤ × ℕ₊₁ → ℚ
[ signed s a , b ] = signedPair s (toCoprime (a , b)) , toCoprimeAreCoprime (a , b)
[ posneg i   , b ] = (posneg i , 1) , toCoprimeAreCoprime (0 , b)

[]-cancelʳ : ∀ ((a , b) : ℤ × ℕ₊₁) k → [ a · pos (ℕ₊₁→ℕ k) , b ·₊₁ k ] ≡ [ a , b ]
[]-cancelʳ (signed s zero    , b) k =
  Σ≡Prop (λ _ → isPropIsGCD) (λ i → signed-zero spos s i , 1)
[]-cancelʳ (signed s (suc a) , b) k =
  Σ≡Prop (λ _ → isPropIsGCD) (λ i → signedPair (·S-comm s spos i)
                                               (toCoprime-cancelʳ (suc a , b) k i))
[]-cancelʳ (posneg i ,         b) k j =
  isSet→isSet' isSetℚ ([]-cancelʳ (pos zero , b) k) ([]-cancelʳ (neg zero , b) k)
                      (λ i → [ posneg i · pos (ℕ₊₁→ℕ k) , b ·₊₁ k ]) (λ i → [ posneg i , b ]) i j

-- Natural number and negative integer literals for ℚ

open import Cubical.Data.Nat.Literals public

instance
  fromNatℚ : HasFromNat ℚ
  fromNatℚ = record { Constraint = λ _ → Unit ; fromNat = λ n → (pos n , 1) , oneGCD n }

instance
  fromNegℚ : HasFromNeg ℚ
  fromNegℚ = record { Constraint = λ _ → Unit ; fromNeg = λ n → (neg n , 1) , oneGCD n }


module plfa-code.Lists where

import Relation.Binary.PropositionalEquality as Eq
open Eq using (_≡_; refl; sym; trans; cong)
open Eq.≡-Reasoning
open import Data.Bool using (Bool; true; false; T; _∧_; _∨_; not)
open import Data.Nat using (ℕ; zero; suc; _+_; _*_; _∸_; _≤_; s≤s; z≤n)
open import Data.Nat.Properties using
  (+-assoc; +-identityˡ; +-identityʳ; *-assoc; *-identityˡ; *-identityʳ)
open import Relation.Nullary using (¬_; Dec; yes; no)
open import Data.Product using (_×_; ∃; ∃-syntax) renaming (_,_ to ⟨_,_⟩)
open import Function using (_∘_)
open import Level using (Level)
open import plfa-code.Isomorphism using (_≃_; _⇔_)

data List (A : Set) : Set where
  []  : List A
  _∷_ : A → List A → List A

infixr 5 _∷_

_ : List ℕ
_ = 0 ∷ 1 ∷ 2 ∷ []

data List′ : Set → Set where
  []′  : ∀ {A : Set} → List′ A
  _∷′_ : ∀ {A : Set} → A → List′ A → List′ A

_ : List ℕ
_ = _∷_ {ℕ} 0 (_∷_ {ℕ} 1 (_∷_ {ℕ} 2 ([] {ℕ})))

{-# BUILTIN LIST List #-}

pattern [_] z = z ∷ []
pattern [_,_] y z = y ∷ z ∷ []
pattern [_,_,_] x y z = x ∷ y ∷ z ∷ []
pattern [_,_,_,_] w x y z = w ∷ x ∷ y ∷ z ∷ []
pattern [_,_,_,_,_] v w x y z = v ∷ w ∷ x ∷ y ∷ z ∷ []
pattern [_,_,_,_,_,_] u v w x y z = u ∷ v ∷ w ∷ x ∷ y ∷ z ∷ []

infixr 5 _++_

_++_ : ∀ {A : Set} → List A → List A → List A
[]       ++ ys  =  ys
(x ∷ xs) ++ ys  =  x ∷ (xs ++ ys)

_ : [ 0 , 1 , 2 ] ++ [ 3 , 4 ] ≡ [ 0 , 1 , 2 , 3 , 4 ]
_ = -- refl
  begin
    0 ∷ 1 ∷ 2 ∷ [] ++ 3 ∷ 4 ∷ []
  ≡⟨⟩
    0 ∷ (1 ∷ 2 ∷ [] ++ 3 ∷ 4 ∷ [])
  ≡⟨⟩
    0 ∷ 1 ∷ (2 ∷ [] ++ 3 ∷ 4 ∷ [])
  ≡⟨⟩
    0 ∷ 1 ∷ 2 ∷ ([] ++ 3 ∷ 4 ∷ [])
  ≡⟨⟩
    0 ∷ 1 ∷ 2 ∷ 3 ∷ 4 ∷ []
  ∎

++-assoc : ∀ {A : Set} (xs ys zs : List A)
  → (xs ++ ys) ++ zs ≡ xs ++ (ys ++ zs)
++-assoc [] ys zs = -- refl
  begin
    ([] ++ ys) ++ zs
  ≡⟨⟩
    ys ++ zs
  ≡⟨⟩
    [] ++ (ys ++ zs)
  ∎
++-assoc (x ∷ xs) ys zs = -- cong (_∷_ x) (++-assoc xs ys zs)
  begin
    (x ∷ xs ++ ys) ++ zs
  ≡⟨⟩
    x ∷ (xs ++ ys) ++ zs
  ≡⟨⟩
    x ∷ ((xs ++ ys) ++ zs)
  ≡⟨ cong (x ∷_) (++-assoc xs ys zs) ⟩
    x ∷ (xs ++ (ys ++ zs))
  ≡⟨⟩
    x ∷ xs ++ (ys ++ zs)
  ∎

++-identityˡ : ∀ {A : Set} (xs : List A) → [] ++ xs ≡ xs
++-identityˡ xs = -- refl
  begin
    [] ++ xs
  ≡⟨⟩
    xs
  ∎

++-identityʳ : ∀ {A : Set} (xs : List A) → xs ++ [] ≡ xs
++-identityʳ [] = -- refl
  begin
    [] ++ []
  ≡⟨⟩
    []
  ∎
++-identityʳ (x ∷ xs) = -- cong (_∷_ x) (++-identityʳ xs)
  begin
   (x ∷ xs) ++ []
  ≡⟨⟩
    x ∷ (xs ++ [])
  ≡⟨ cong (x ∷_) (++-identityʳ xs) ⟩
   x ∷ xs
  ∎

length : ∀ {A : Set} → List A → ℕ
length []       = zero
length (x ∷ xs) = suc (length xs)

_ : length [ 0 , 1 , 2 ] ≡ 3
_ = -- refl
  begin
    length (0 ∷ 1 ∷ 2 ∷ [])
  ≡⟨⟩
    suc (length (1 ∷ 2 ∷ []))
  ≡⟨⟩
    suc (suc (length (2 ∷ [])))
  ≡⟨⟩
    suc (suc (suc (length {ℕ} [])))
  ≡⟨⟩
    suc (suc (suc zero))
  ∎

length-++ : ∀ {A : Set} (xs ys : List A)
  → length (xs ++ ys) ≡ length xs + length ys
length-++ {A} [] ys = -- refl
  begin
    length ([] ++ ys)
  ≡⟨⟩
    length ys
  ≡⟨⟩
    length {A} [] + length ys
  ∎
length-++ {A} (x ∷ xs) ys = -- cong suc (length-++ xs ys)
  begin
    length ((x ∷ xs) ++ ys)
  ≡⟨⟩
    suc (length (xs ++ ys))
  ≡⟨ cong suc (length-++ xs ys) ⟩
    suc (length xs + length ys)
  ≡⟨⟩
    length (x ∷ xs) + length ys
  ∎

reverse : ∀ {A : Set} → List A → List A
reverse [] = []
reverse (x ∷ xs) = reverse xs ++ [ x ]

_ : reverse [ 0 , 1 , 2 ] ≡ [ 2 , 1 , 0 ]
_ =
  begin
    reverse (0 ∷ 1 ∷ 2 ∷ [])
  ≡⟨⟩
    reverse (1 ∷ 2 ∷ []) ++ [ 0 ]
  ≡⟨⟩
    (reverse (2 ∷ []) ++ [ 1 ]) ++ [ 0 ]
  ≡⟨⟩
    ((reverse [] ++ [ 2 ]) ++ [ 1 ]) ++ [ 0 ]
  ≡⟨⟩
    (([] ++ [ 2 ]) ++ [ 1 ]) ++ [ 0 ]
  ≡⟨⟩
    (([] ++ 2 ∷ []) ++ 1 ∷ []) ++ 0 ∷ []
  ≡⟨⟩
    (2 ∷ [] ++ 1 ∷ []) ++ 0 ∷ []
  ≡⟨⟩
    2 ∷ ([] ++ 1 ∷ []) ++ 0 ∷ []
  ≡⟨⟩
    (2 ∷ 1 ∷ []) ++ 0 ∷ []
  ≡⟨⟩
    2 ∷ (1 ∷ [] ++ 0 ∷ [])
  ≡⟨⟩
    2 ∷ 1 ∷ ([] ++ 0 ∷ [])
  ≡⟨⟩
    2 ∷ 1 ∷ 0 ∷ []
  ≡⟨⟩
    [ 2 , 1 , 0 ]
  ∎

---------- practice ----------
reverse-++-commute : ∀ {A : Set} {xs ys : List A}
  → reverse (xs ++ ys) ≡ reverse ys ++ reverse xs
reverse-++-commute {_} {[]} {[]} = refl
reverse-++-commute {_} {[]} {y ∷ ys} = sym (++-identityʳ (reverse ys ++ [ y ]))
reverse-++-commute {_} {x ∷ xs} {ys} =
  begin
    reverse ((x ∷ xs) ++ ys)
  ≡⟨⟩
    reverse (x ∷ (xs ++ ys))
  ≡⟨⟩
    reverse (xs ++ ys) ++ [ x ]
  ≡⟨ cong (_++ [ x ]) (reverse-++-commute {_} {xs} {ys}) ⟩
    (reverse ys ++ reverse xs) ++ [ x ]
  ≡⟨ ++-assoc (reverse ys) (reverse xs) [ x ] ⟩
    reverse ys ++ reverse xs ++ [ x ]
  ≡⟨ cong (reverse ys ++_) (sym refl) ⟩
    reverse ys ++ reverse ([ x ] ++ xs)
  ≡⟨⟩
    reverse ys ++ reverse (x ∷ xs)
  ∎

reverse-involutive : ∀ {A : Set} {xs : List A}
  → reverse (reverse xs) ≡ xs
reverse-involutive {_} {[]} = refl
reverse-involutive {_} {x ∷ xs} =
  begin
    reverse (reverse (x ∷ xs))
  ≡⟨⟩
    reverse (reverse xs ++ [ x ])
  ≡⟨ reverse-++-commute {_} {reverse xs} {[ x ]} ⟩
    reverse [ x ] ++ reverse (reverse xs)
  ≡⟨ cong (reverse [ x ] ++_) reverse-involutive ⟩
    [ x ] ++ xs
  ≡⟨⟩
    x ∷ xs
  ∎

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

shunt : ∀ {A : Set} → List A → List A → List A
shunt [] ys = ys
shunt (x ∷ xs) ys = shunt xs (x ∷ ys)

shunt-reverse : ∀ {A : Set} (xs ys : List A)
  → shunt xs ys ≡ (reverse xs) ++ ys
shunt-reverse [] ys = -- refl
  begin
    shunt [] ys
  ≡⟨⟩
    reverse [] ++ ys
  ∎
shunt-reverse (x ∷ xs) ys =
  begin
    shunt (x ∷ xs) ys
  ≡⟨⟩
    shunt xs (x ∷ ys)
  ≡⟨ shunt-reverse xs (x ∷ ys) ⟩
    reverse xs ++ (x ∷ ys)
  ≡⟨⟩
    reverse xs ++ ([ x ] ++ ys)
  ≡⟨ sym (++-assoc (reverse xs) [ x ] ys) ⟩
    (reverse xs ++ [ x ]) ++ ys
  ≡⟨⟩
    reverse (x ∷ xs) ++ ys
  ∎

reverse′ : ∀ {A : Set} → List A → List A
reverse′ xs = shunt xs []

reverses : ∀ {A : Set} (xs : List A)
  → reverse′ xs ≡ reverse xs
reverses xs =
  begin
    reverse′ xs
  ≡⟨⟩
    shunt xs []
  ≡⟨ shunt-reverse xs [] ⟩
    reverse xs ++ []
  ≡⟨ ++-identityʳ (reverse xs) ⟩
    reverse xs
  ∎

_ : reverse′ [ 0 , 1 , 2 ] ≡ [ 2 , 1 , 0 ]
_ =
  begin
    reverse′ (0 ∷ 1 ∷ 2 ∷ [])
  ≡⟨⟩
    shunt (0 ∷ 1 ∷ 2 ∷ []) []
  ≡⟨⟩
    shunt (1 ∷ 2 ∷ []) (0 ∷ [])
  ≡⟨⟩
    shunt (2 ∷ []) (1 ∷ 0 ∷ [])
  ≡⟨⟩
    shunt [] (2 ∷ 1 ∷ 0 ∷ [])
  ≡⟨⟩
    2 ∷ 1 ∷ 0 ∷ []
  ∎

map : ∀ {A B : Set} → (A → B) → List A → List B
map f [] = []
map f (x ∷ xs) = f x ∷ map f xs

_ : map suc [ 0 , 1 , 2 ] ≡ [ 1 , 2 , 3 ]
_ =
  begin
    map suc (0 ∷ 1 ∷ 2 ∷ [])
  ≡⟨⟩
    suc 0 ∷ map suc (1 ∷ 2 ∷ [])
  ≡⟨⟩
    suc 0 ∷ suc 1 ∷ map suc (2 ∷ [])
  ≡⟨⟩
    suc 0 ∷ suc 1 ∷ suc 2 ∷ map suc []
  ≡⟨⟩
    suc 0 ∷ suc 1 ∷ suc 2 ∷ []
  ≡⟨⟩
    1 ∷ 2 ∷ 3 ∷ []
  ∎

sucs : List ℕ → List ℕ
sucs = map suc

_ : sucs [ 0 , 1 , 2 ] ≡ [ 1 , 2 , 3 ]
_ =
  begin
    sucs [ 0 , 1 , 2 ]
  ≡⟨⟩
    map suc [ 0 , 1 , 2 ]
  ≡⟨⟩
    [ 1 , 2 , 3 ]
  ∎
---------- practice ----------
open plfa-code.Isomorphism using (extensionality)

map-compose : ∀ {A B C : Set} {f : A → B} {g : B → C}
  → map (g ∘ f) ≡ map g ∘ map f
map-compose {A} {B} {C} {f} {g} = extensionality hf
  where
  hf : (xs : List A) → map (g ∘ f) xs ≡ map g (map f xs)
  hf [] = refl
  hf (x ∷ xs) = cong ((g (f x)) ∷_) (hf xs)

map-++-commute : ∀ {A B : Set} {f : A → B} {xs ys : List A}
  →  map f (xs ++ ys) ≡ map f xs ++ map f ys
map-++-commute {A} {B} {f} {[]} {ys} = refl
map-++-commute {A} {B} {f} {x ∷ xs} {ys} =
  cong ((f x) ∷_) (map-++-commute {_} {_} {f} {xs} {ys})

data Tree (A B : Set) : Set where
  leaf : A → Tree A B
  node : Tree A B → B → Tree A B → Tree A B

map-Tree : ∀ {A B C D : Set}
  → (A → C) → (B → D) → Tree A B → Tree C D
map-Tree f g (leaf a) = leaf (f a)
map-Tree f g (node tree b tree₁) = node (map-Tree f g tree) (g b) (map-Tree f g tree₁)

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

foldr : ∀ {A B : Set} → (A → B → B) → B → List A → B
foldr _⊗_ e       []  =  e
foldr _⊗_ e (x ∷ xs)  =  x ⊗ (foldr _⊗_ e xs)

_ : foldr _+_ 0 [ 1 , 2 , 3 , 4 ] ≡ 10
_ =
  begin
    foldr _+_ 0 (1 ∷ 2 ∷ 3 ∷ 4 ∷ [])
  ≡⟨⟩
    1 + foldr _+_ 0 (2 ∷ 3 ∷ 4 ∷ [])
  ≡⟨⟩
    1 + (2 + foldr _+_ 0 (3 ∷ 4 ∷ []))
  ≡⟨⟩
    1 + (2 + (3 + foldr _+_ 0 (4 ∷ [])))
  ≡⟨⟩
    1 + (2 + (3 + (4 + foldr _+_ 0 [])))
  ≡⟨⟩
    1 + (2 + (3 + (4 + 0)))
  ∎

sum : List ℕ → ℕ
sum = foldr _+_ 0

_ : sum [ 1 , 2 , 3 , 4 ] ≡ 10
_ =
  begin
    sum [ 1 , 2 , 3 , 4 ]
  ≡⟨⟩
    foldr _+_ 0 [ 1 , 2 , 3 , 4 ]
  ≡⟨⟩
    10
  ∎

---------- practice ----------
product : List ℕ → ℕ
product = foldr _*_ 1

_ : product [ 1 , 2 , 3 , 4 ] ≡ 24
_ = refl

foldr-++ : ∀ {A B : Set} (_⊗_ : A → B → B) (e : B) (xs ys : List A) →
  foldr _⊗_ e (xs ++ ys) ≡ foldr _⊗_ (foldr _⊗_ e ys) xs
foldr-++ _⊗_ e []       ys = refl
foldr-++ _⊗_ e (x ∷ xs) ys = cong (x ⊗_) (foldr-++ _⊗_ e xs ys)

map-is-foldr : ∀ {A B : Set} {f : A → B} →
  map f ≡ foldr (λ x xs → f x ∷ xs) []
map-is-foldr {A} {_} {f} = extensionality hf
  where
  hf : (l : List A) → map f l ≡ foldr (λ x xs → f x ∷ xs) [] l
  hf [] = refl
  hf (x ∷ xs) = cong (f x ∷_) (hf xs)

fold-Tree : ∀ {A B C : Set}
  → (A → C) → (C → B → C → C) → Tree A B → C
fold-Tree f g (leaf a)            = f a
fold-Tree f g (node tree b tree₁) = g (fold-Tree f g tree) b (fold-Tree f g tree₁)

downFrom : ℕ → List ℕ
downFrom zero     =  []
downFrom (suc n)  =  n ∷ downFrom n

_ : downFrom 3 ≡ [ 2 , 1 , 0 ]
_ = refl

open Data.Nat.Properties using
  (*-distribʳ-+; *-distribʳ-∸; m∸n+n≡m; m≤m+n;
   +-comm; *-comm; +-identityʳ; *-identityˡ)
open Eq using (cong₂)

2n≡n+n : ∀ n → 2 * n ≡ n + n
2n≡n+n zero = refl
2n≡n+n (suc n) = cong (λ x → suc (n + suc x)) (+-identityʳ n)

n≤n*n : ∀ n → n ≤ n * n
n≤n*n zero    = z≤n
n≤n*n (suc n) = s≤s (m≤m+n n (n * suc n))

sum-downFrom : ∀ (n : ℕ)
  → sum (downFrom n) * 2 ≡ n * (n ∸ 1)
sum-downFrom zero    = refl
sum-downFrom (suc n) =
  begin
    sum (downFrom (suc n)) * 2
  ≡⟨⟩
    (n + sum (downFrom n)) * 2
  ≡⟨ *-distribʳ-+ 2 n (sum (downFrom n)) ⟩
    n * 2 + sum (downFrom n) * 2
  ≡⟨ cong (n * 2 +_) (sum-downFrom n) ⟩
    n * 2 + n * (n ∸ 1)
  ≡⟨ cong₂ _+_ (*-comm n 2) (*-comm n (n ∸ 1)) ⟩
    2 * n + (n ∸ 1) * n
  ≡⟨ cong (2 * n +_) (*-distribʳ-∸ n n 1) ⟩
    2 * n + (n * n ∸ 1 * n)
  ≡⟨ cong₂ (λ a b → a + (n * n ∸ b)) (2n≡n+n n) (*-identityˡ n) ⟩
    n + n + (n * n ∸ n)
  ≡⟨ +-comm (n + n) (n * n ∸ n) ⟩
    n * n ∸ n + (n + n)
  ≡⟨ sym (+-assoc (n * n ∸ n) n n) ⟩
    n * n ∸ n + n + n
  ≡⟨ cong (_+ n) (m∸n+n≡m (n≤n*n n)) ⟩
    n * n + n
  ≡⟨ +-comm (n * n) n ⟩
    n + n * n
  ≡⟨⟩
    (suc n) * (suc n ∸ 1)
  ∎

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

record IsMonoid {A : Set} (_⊗_ : A → A → A) (e : A) : Set where
  field
    assoc : ∀ (x y z : A) → (x ⊗ y) ⊗ z ≡ x ⊗ (y ⊗ z)
    identityˡ : ∀ (x : A) → e ⊗ x ≡ x
    identityʳ : ∀ (x : A) → x ⊗ e ≡ x

open IsMonoid

+-monoid : IsMonoid _+_ 0
+-monoid =
  record
    { assoc = +-assoc
    ; identityˡ = +-identityˡ
    ; identityʳ = +-identityʳ
    }

*-monoid : IsMonoid _*_ 1
*-monoid =
  record
    { assoc = *-assoc
    ; identityˡ = *-identityˡ
    ; identityʳ = *-identityʳ
    }

++-monoid : ∀ {A : Set} → IsMonoid {List A} _++_ []
++-monoid =
  record
    { assoc = ++-assoc
    ; identityˡ = ++-identityˡ
    ; identityʳ = ++-identityʳ
    }

foldr-monoid : ∀ {A : Set} (_⊗_ : A → A → A) (e : A) → IsMonoid _⊗_ e →
  ∀ (xs : List A) (y : A) → foldr _⊗_ y xs ≡ foldr _⊗_ e xs ⊗ y
foldr-monoid _⊗_ e ⊗-monoid [] y = -- sym (identityˡ ⊗-monoid y)
  begin
    foldr _⊗_ y []
  ≡⟨⟩
    y
  ≡⟨ sym (identityˡ ⊗-monoid y) ⟩
    (e ⊗ y)
  ≡⟨⟩
    foldr _⊗_ e [] ⊗ y
  ∎
foldr-monoid _⊗_ e ⊗-monoid (x ∷ xs) y =
  begin
    foldr _⊗_ y (x ∷ xs)
  ≡⟨⟩
    x ⊗ (foldr _⊗_ y xs)
  ≡⟨ cong (x ⊗_) (foldr-monoid _⊗_ e ⊗-monoid xs y) ⟩
    x ⊗ ((foldr _⊗_ e xs) ⊗ y)
  ≡⟨ sym (assoc ⊗-monoid x (foldr _⊗_ e xs) y) ⟩
    (x ⊗ (foldr _⊗_ e xs)) ⊗ y
  ∎

foldr-monoid-++ : ∀ {A : Set} (_⊗_ : A → A → A) (e : A) → IsMonoid _⊗_ e →
  ∀ (xs ys : List A) → foldr _⊗_ e (xs ++ ys) ≡ foldr _⊗_ e xs ⊗ foldr _⊗_ e ys
foldr-monoid-++ _⊗_ e monoid-⊗ xs ys =
  begin
    foldr _⊗_ e (xs ++ ys)
  ≡⟨ foldr-++ _⊗_ e xs ys ⟩
    foldr _⊗_ (foldr _⊗_ e ys) xs
  ≡⟨ foldr-monoid _⊗_ e monoid-⊗ xs (foldr _⊗_ e ys) ⟩
    foldr _⊗_ e xs ⊗ foldr _⊗_ e ys
  ∎

---------- practice ----------
foldl : ∀ {A B : Set} → (B → A → B) → B → List A → B
foldl _⊗_ e       []  =  e
foldl _⊗_ e (x ∷ xs)  =  foldl _⊗_ (e ⊗ x) xs

foldl-monoid : ∀ {A : Set} (_⊗_ : A → A → A) (e : A) → IsMonoid _⊗_ e →
  ∀ (xs : List A) (y : A) → foldl _⊗_ y xs ≡ y ⊗ foldl _⊗_ e xs
foldl-monoid _⊗_ e ⊗-monoid []       y = sym (identityʳ ⊗-monoid y)
foldl-monoid _⊗_ e ⊗-monoid (x ∷ xs) y =
  begin
    foldl _⊗_ y (x ∷ xs)
  ≡⟨⟩
    foldl _⊗_ (y ⊗ x) xs
  ≡⟨ foldl-monoid _⊗_ e ⊗-monoid xs (y ⊗ x) ⟩
    (y ⊗ x) ⊗ (foldl _⊗_ e xs)
  ≡⟨ assoc ⊗-monoid y x (foldl _⊗_ e xs) ⟩
    y ⊗ (x ⊗ foldl _⊗_ e xs)
  ≡⟨ sym (cong (y ⊗_) (foldl-monoid _⊗_ e ⊗-monoid xs x)) ⟩
    y ⊗ (foldl _⊗_ x xs)
  ≡⟨ sym (cong (λ z → y ⊗ foldl _⊗_ z xs) (identityˡ ⊗-monoid x)) ⟩
    y ⊗ (foldl _⊗_ (e ⊗ x) xs)
  ≡⟨⟩
    y ⊗ foldl _⊗_ e (x ∷ xs)
  ∎

foldr-monoid-foldl : ∀ {A : Set} (_⊗_ : A → A → A) (e : A) → IsMonoid _⊗_ e →
  ∀ (xs : List A) → foldr _⊗_ e xs ≡ foldl _⊗_ e xs
foldr-monoid-foldl _⊗_ e ⊗-monoid []       = refl
foldr-monoid-foldl _⊗_ e ⊗-monoid (x ∷ xs) =
  begin
    foldr _⊗_ e (x ∷ xs)
  ≡⟨⟩
    (x ⊗ foldr _⊗_ e xs)
  ≡⟨ cong (x ⊗_) (foldr-monoid-foldl _⊗_ e ⊗-monoid xs) ⟩
    (x ⊗ foldl _⊗_ e xs)
  ≡⟨ sym (foldl-monoid _⊗_ e ⊗-monoid xs x) ⟩
    foldl _⊗_ x xs
  ≡⟨ cong (λ a → foldl _⊗_ a xs) (sym (identityˡ ⊗-monoid x)) ⟩
    foldl _⊗_ (e ⊗ x) xs
  ≡⟨⟩
    foldl _⊗_ e (x ∷ xs)
  ∎

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

data All {A : Set} (P : A → Set) : List A → Set where
  []  : All P []
  _∷_ : ∀ {x : A} {xs : List A} → P x → All P xs → All P (x ∷ xs)

_ : All (_≤ 2) [ 0 , 1 , 2 ]
_ = z≤n ∷ s≤s z≤n ∷ s≤s (s≤s z≤n) ∷ []

data Any {A : Set} (P : A → Set) : List A → Set where
  here  : ∀ {x : A} {xs : List A} → P x → Any P (x ∷ xs)
  there : ∀ {x : A} {xs : List A} → Any P xs → Any P (x ∷ xs)

_∈_ : ∀ {A : Set} (x : A) (xs : List A) → Set
x ∈ xs = Any (x ≡_) xs

_∉_ : ∀ {A : Set} (x : A) (xs : List A) → Set
x ∉ xs = ¬ (x ∈ xs)

_ : 0 ∈ [ 0 , 1 , 0 , 2 ]
_ = here refl

_ : 0 ∈ [ 0 , 1 , 0 , 2 ]
_ = there (there (here refl))

not-in : 3 ∉ [ 0 , 0 , 0 , 2 ]
not-in (here ())
not-in (there (here ()))
not-in (there (there (here ())))
not-in (there (there (there (here ()))))
not-in (there (there (there (there ()))))

All-++-⇔ : ∀ {A : Set} {P : A → Set} (xs ys : List A) →
  All P (xs ++ ys) ⇔ (All P xs × All P ys)
All-++-⇔ xs ys =
  record
    { to   = to xs ys
    ; from = from xs ys
    }
  where

  to : ∀ {A : Set} {P : A → Set} (xs ys : List A) →
    All P (xs ++ ys) → (All P xs × All P ys)
  to [] ys Pys = ⟨ [] , Pys ⟩
  to (x ∷ xs) ys (Px ∷ Pxs++ys) with to xs ys Pxs++ys
  ... | ⟨ Pxs , Pys ⟩ = ⟨ Px ∷ Pxs , Pys ⟩

  from : ∀ { A : Set} {P : A → Set} (xs ys : List A) →
    All P xs × All P ys → All P (xs ++ ys)
  from [] ys ⟨ [] , Pys ⟩ = Pys
  from (x ∷ xs) ys ⟨ Px ∷ Pxs , Pys ⟩ = Px ∷ (from xs ys ⟨ Pxs , Pys ⟩)

---------- practice ----------
open import Data.Sum using (_⊎_; inj₁; inj₂)
open Data.Product using (proj₁; proj₂)
open import Data.Empty using (⊥-elim)

Any-++-⇔ : ∀ {A : Set} {P : A → Set} (xs ys : List A) →
  Any P (xs ++ ys) ⇔ (Any P xs ⊎ Any P ys)
Any-++-⇔ {A} {P} xs ys =
  record
  { to   = to xs ys
  ; from = from xs ys
  }
  where
  to : ∀ (xs ys : List A) →
    Any P (xs ++ ys) → Any P xs ⊎ Any P ys
  to []       ys Pys = inj₂ Pys
  to (x ∷ xs) ys (here Px)       = inj₁ (here Px)
  to (x ∷ xs) ys (there ∃Pxs++ys) with to xs ys ∃Pxs++ys
  ...                                | inj₁ ∃Pxs = inj₁ (there ∃Pxs)
  ...                                | inj₂ ∃Pys = inj₂ ∃Pys
  from : ∀ (xs ys : List A) →
    Any P xs ⊎ Any P ys → Any P (xs ++ ys)
  from [] ys (inj₂ ∃Pys) = ∃Pys
  from (x ∷ xs) ys (inj₁ (here Px))    = here Px
  from (x ∷ xs) ys (inj₁ (there ∃Pxs)) = there (from xs ys (inj₁ ∃Pxs))
  from (x ∷ xs) ys (inj₂ ∃Pys)         = there (from xs ys (inj₂ ∃Pys))

All-++-≃ : ∀ {A : Set} {P : A → Set} (xs ys : List A) →
  All P (xs ++ ys) ≃ (All P xs × All P ys)
All-++-≃ {A} {P} xs ys =
  record
  { to      = to      xs ys
  ; from    = from    xs ys
  ; from∘to = from∘to xs ys
  ; to∘from = to∘from xs ys
  }
  where
  to : ∀ (xs ys : List A) → All P (xs ++ ys) → (All P xs × All P ys)
  to xs ys = _⇔_.to (All-++-⇔ xs ys)

  from : ∀ (xs ys : List A) → All P xs × All P ys → All P (xs ++ ys)
  from xs ys = _⇔_.from (All-++-⇔ xs ys)

  from∘to : ∀ (xs ys : List A) (Pxs++ys : All P (xs ++ ys))
    → from xs ys (to xs ys Pxs++ys) ≡ Pxs++ys
  from∘to [] ys Pxs++ys = refl
  from∘to (x ∷ xs) ys (Px ∷ Pxs++ys) = -- cong (Px ∷_) (from∘to xs ys Pxs++ys)
    begin
      from (x ∷ xs) ys (to (x ∷ xs) ys (Px ∷ Pxs++ys))
    ≡⟨⟩
      from (x ∷ xs) ys ⟨ Px ∷ proj₁ t , proj₂ t ⟩
    ≡⟨⟩
      Px ∷ from xs ys ⟨ proj₁ t , proj₂ t ⟩
    ≡⟨⟩
      Px ∷ from xs ys (to xs ys Pxs++ys)
    ≡⟨ cong (Px ∷_) (from∘to xs ys Pxs++ys) ⟩
      Px ∷ Pxs++ys
    ∎
    where t = to xs ys Pxs++ys

  to∘from : ∀ (xs ys : List A) (Pxs×Pys : All P xs × All P ys)
    → to xs ys (from xs ys Pxs×Pys) ≡ Pxs×Pys
  to∘from [] ys ⟨ [] , Pys ⟩ = refl
  to∘from (x ∷ xs) ys ⟨ Px ∷ Pxs , Pys ⟩ =
  -- cong (λ k → ⟨ Px ∷ (proj₁ k) , proj₂ k ⟩) (to∘from xs ys ⟨ Pxs , Pys ⟩)
    begin
      to (x ∷ xs) ys (from (x ∷ xs) ys ⟨ Px ∷ Pxs , Pys ⟩)
    ≡⟨⟩
      to (x ∷ xs) ys (Px ∷ (from xs ys ⟨ Pxs , Pys ⟩))
    ≡⟨⟩
      ⟨ Px ∷ (proj₁ tf) , (proj₂ tf) ⟩
    ≡⟨ cong (λ k → ⟨ Px ∷ (proj₁ k) , proj₂ k ⟩) (to∘from xs ys ⟨ Pxs , Pys ⟩) ⟩
      ⟨ Px ∷ (proj₁ ⟨ Pxs , Pys ⟩) , (proj₂ ⟨ Pxs , Pys ⟩) ⟩
    ≡⟨⟩
      ⟨ Px ∷ Pxs , Pys ⟩
    ∎
    where tf = (to xs ys (from xs ys ⟨ Pxs , Pys ⟩))

_∘′_ : ∀ {ℓ₁ ℓ₂ ℓ₃ : Level} {A : Set ℓ₁} {B : Set ℓ₂} {C : Set ℓ₃}
  → (B → C) → (A → B) → A → C
(g ∘′ f) x  =  g (f x)

¬Any≃All¬ : ∀ {A : Set} (P : A → Set) (xs : List A)
  → (¬_ ∘′ Any P) xs ≃ All (¬_ ∘′ P) xs
¬Any≃All¬ {A} P xs =
  record
  { to      = to xs
  ; from    = from xs
  ; from∘to = from∘to xs
  ; to∘from = to∘from xs
  }
  where
  to : (xs : List A) → (¬_ ∘′ Any P) xs → All (¬_ ∘′ P) xs
  to []       ¬Any = []
  to (x ∷ xs) ¬Any = (λ Px → ¬Any (here Px)) ∷ to xs (λ Pxs → ¬Any (there Pxs))
  from : (xs : List A) → All (¬_ ∘′ P) xs → (¬_ ∘′ Any P) xs
  from (x ∷ xs) (¬Px ∷ All¬) (here Px) = ¬Px Px
  from (x ∷ xs) (¬Px ∷ All¬) (there AnyP) = from xs All¬ AnyP

  from∘to : (xs : List A) → ∀ (¬Any) → from xs (to xs ¬Any) ≡ ¬Any
  from∘to xs ¬Any = extensionality λ AnyP → ⊥-elim (¬Any AnyP)

  to∘from : (xs : List A) → ∀ (All¬) → to xs (from xs All¬) ≡ All¬
  to∘from [] [] = refl
  to∘from (x ∷ xs) (¬Px ∷ ¬Pxs) = cong (¬Px ∷_) (to∘from xs ¬Pxs)

-- ¬All≃Any¬ : ∀ {A : Set} (P : A → Set) (xs : List A)
--  → (¬_ ∘′ All P) xs ≃ Any (¬_ ∘′ P) xs
-- doesn't hold, because we couldn't use
-- ((¬_ ∘′ All P) xs) to construct a (Any (¬_ ∘′ P) xs)

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

all : ∀ {A : Set} → (A → Bool) → List A → Bool
all p  =  foldr _∧_ true ∘ map p

Decidable : ∀ {A : Set} → (A → Set) → Set
Decidable {A} P  =  ∀ (x : A) → Dec (P x)

All? : ∀ {A : Set} {P : A → Set} → Decidable P → Decidable (All P)
All? P? []                                 = yes []
All? P? (x ∷ xs) with P? x   | All? P? xs
...                 | yes Px | yes p₁      = yes (Px ∷ p₁)
...                 | no ¬Px | _           = no λ{ (Px ∷ Pxs) → ¬Px Px}
...                 | _      | no ¬Pxs     = no λ{ (Px ∷ Pxs) → ¬Pxs Pxs}

---------- practice ----------
any : ∀ {A : Set} → (A → Bool) → List A → Bool
any p  =  foldr _∨_ false ∘ map p

Any? : ∀ {A : Set} {P : A → Set} → Decidable P → Decidable (Any P)
Any? P? [] = no (λ ())
Any? P? (x ∷ xs) with P? x   | Any? P? xs
Any? P? (x ∷ xs)    | yes Px | _        = yes (here Px)
Any? P? (x ∷ xs)    | _      | yes ∃Pxs = yes (there ∃Pxs)
Any? P? (x ∷ xs)    | no ¬Px | no ¬∃Pxs = no λ{ (here Px) → ¬Px Px
                                              ; (there ∃Px) → ¬∃Pxs ∃Px
                                              }

open import plfa-code.Quantifiers using (∀-extensionality)

-- the implicit argument x is too difficult to deal with ..
-- so I replace it with explicit

All-∀ : ∀ {A : Set} {P : A → Set} (xs : List A) →
  All P xs ≃ (∀ (x) → x ∈ xs → P x)
All-∀ {A} {P} xs =
  record
  { to      = to xs
  ; from    = from xs
  ; from∘to = from∘to xs
  ; to∘from = to∘from xs
  }
  where
  to : ∀ (xs : List A) → All P xs → (∀ (x) → x ∈ xs → P x)
  to (x₁ ∷ xs) (Px₁ ∷  _ ) .x₁ (here refl)  = Px₁
  to (x₁ ∷ xs) ( _  ∷ Pxs) x   (there x∈xs) = to xs Pxs x x∈xs

  from : ∀ (xs : List A) → (∀ (x) → x ∈ xs → P x) → All P xs
  from []       f = []
  from (x ∷ xs) f = f x (here refl) ∷ from xs (λ x₁ → (f x₁) ∘ there)

  from∘to : ∀ (xs : List A) → (Pxs : All P xs) → from xs (to xs Pxs) ≡ Pxs
  from∘to []       []         = refl
  from∘to (x ∷ xs) (Px ∷ Pxs) = cong (Px ∷_) (from∘to xs Pxs)

  to∘from : ∀ (xs : List A) → ∀ (f) → to xs (from xs f) ≡ f
  to∘from [] f = ∀-extensionality (to [] []) f
    (λ x → ∀-extensionality (to [] [] x) (f x) λ ())
  to∘from (x ∷ xs) f = ∀-extensionality (to (x ∷ xs) (from (x ∷ xs) f)) f
    (λ x₁ → ∀-extensionality (to (x ∷ xs) (from (x ∷ xs) f) x₁) (f x₁)
      λ{ (here refl) → refl
       ; (there x₁∈xs) → cong (λ g → g x₁ x₁∈xs)
                (to∘from xs (λ x₂ → (f x₂) ∘ there))
       }
    )

-- Emmmm..., "∃[ x ∈ xs ] P x" is not a valid type, I guess its real
-- meaning is like below

Any-∃ : {A : Set} {P : A → Set} (xs : List A) →
  Any P xs ≃ ∃[ x ] (x ∈ xs × P x)
Any-∃ {A} {P} xs =
  record
  { to      = to xs
  ; from    = from xs
  ; from∘to = from∘to xs
  ; to∘from = to∘from xs
  }
  where
  to : ∀ (xs : List A) → Any P xs → ∃[ x ] (x ∈ xs × P x)
  to (x ∷ xs) (here Px)         = ⟨ x , ⟨ here refl , Px ⟩ ⟩
  to (x ∷ xs) (there AnyP) with to xs AnyP
  ... | ⟨ x₁ , ⟨ x₁∈xs , Px ⟩ ⟩ = ⟨ x₁ , ⟨ there x₁∈xs , Px ⟩ ⟩

  from : ∀ (xs : List A) → ∃[ x ] (x ∈ xs × P x) → Any P xs
  from (x ∷ xs) ⟨ .x , ⟨ here refl   , Px  ⟩ ⟩ = here Px
  from (x ∷ xs) ⟨ x₁ , ⟨ there x₁∈xs , Px₁ ⟩ ⟩ = there (from xs ⟨ x₁ , ⟨ x₁∈xs , Px₁ ⟩ ⟩)

  from∘to : ∀ (xs : List A) → (AnyP : Any P xs)
    → from xs (to xs AnyP) ≡ AnyP
  from∘to (x ∷ xs) (here x₁)    = refl
  from∘to (x ∷ xs) (there AnyP) = cong there (from∘to xs AnyP)

  to∘from : ∀ (xs : List A) → ∀ ∃Pxs → to xs (from xs ∃Pxs) ≡ ∃Pxs
  to∘from (x ∷ xs) ⟨ .x , ⟨ here refl   , Px  ⟩ ⟩ = refl
  to∘from (x ∷ xs) ⟨ x₁ , ⟨ there x₁∈xs , Px₁ ⟩ ⟩
    = cong (λ a → ⟨ proj₁ a , ⟨ there (proj₁ (proj₂ a)) , proj₂ (proj₂ a) ⟩ ⟩)
           (to∘from xs ⟨ x₁ , ⟨ x₁∈xs , Px₁ ⟩ ⟩)

filter? : ∀ {A : Set} {P : A → Set}
  → (P? : Decidable P) → List A → ∃[ ys ]( All P ys )
filter? P? [] = ⟨ [] , [] ⟩
filter? P? (x ∷ xs) with P? x   | filter? P? xs
...                    | yes Px | ⟨ ys , Pys ⟩  = ⟨ (x ∷ ys) , (Px ∷ Pys) ⟩
...                    | no ¬Px | ∃ys-Pys       = ∃ys-Pys


{-# OPTIONS --show-implicit #-}
-- {-# OPTIONS -v tc.polarity:10 #-}
module Issue44 where

data Ty : Set where
  ι : Ty
  _⇒_ : Ty -> Ty -> Ty

data Con : Set where
  ε : Con
  _<_ : Con -> Ty -> Con

data Var : Con -> Ty -> Set where
  vZ : forall {Γ σ} -> Var (Γ < σ) σ
  vS : forall {Γ σ}{τ : Ty} -> Var Γ σ -> Var (Γ < τ) σ

{-
stren : forall {Γ σ} -> Var Γ σ -> Con
stren (vZ {Γ}) = Γ
stren (vS {τ = τ} v) = stren v < τ

_/_ : forall Γ {σ} -> Var Γ σ -> Con
Γ / v = stren v
-}

-- However if I make stren a local function:

_/_ : forall Γ {σ} -> Var Γ σ -> Con
Γ / v = stren v where
  stren : forall {Γ σ} -> Var Γ σ -> Con
  stren (vZ {Γ}) = Γ
  stren (vS {τ = τ} v) = stren v < τ

thin : forall {Γ σ τ}(v : Var Γ σ) -> Var (Γ / v) τ -> Var Γ τ
thin vZ v' = vS v'
thin (vS v) vZ = vZ
thin (vS v) (vS v') = vS (thin v v')