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

open import LogicalFormulae
open import Agda.Primitive using (Level; lzero; lsuc; _⊔_)
open import Numbers.Naturals.Semiring
open import Numbers.Naturals.Naturals
open import Numbers.Naturals.Order
open import Numbers.Naturals.EuclideanAlgorithm
open import Lists.Lists
open import Numbers.Primes.PrimeNumbers
open import Decidable.Relations
open import Numbers.BinaryNaturals.Definition
open import Numbers.BinaryNaturals.Addition
open import Numbers.BinaryNaturals.Order
open import Sequences
open import Vectors
open import Orders.Total.Definition
open import Setoids.Orders.Partial.Definition
open import Setoids.Orders.Partial.Sequences
open import Setoids.Orders.Total.Definition
open import Setoids.Setoids
open import Functions.Definition
open import Semirings.Definition

module ProjectEuler.Problem2 where

fibUnary : ℕ → ℕ
fibUnary zero = 1
fibUnary (succ zero) = 1
fibUnary (succ (succ n)) = fibUnary (succ n) +N fibUnary n

fibUnaryStrictlyPositive : (a : ℕ) → 0 <N fibUnary a
fibUnaryStrictlyPositive zero = le zero refl
fibUnaryStrictlyPositive (succ zero) = le zero refl
fibUnaryStrictlyPositive (succ (succ a)) = addStrongInequalities (fibUnaryStrictlyPositive (succ a)) (fibUnaryStrictlyPositive a)

fibUnaryIncreasing : (a : ℕ) → (fibUnary (succ a)) <N (fibUnary (succ (succ a)))
fibUnaryIncreasing zero = le zero refl
fibUnaryIncreasing (succ a) = identityOfIndiscernablesLeft _<N_ (additionPreservesInequalityOnLeft (fibUnary (succ a) +N fibUnary a) (fibUnaryStrictlyPositive (succ a))) (Semiring.sumZeroRight ℕSemiring (fibUnary (succ a) +N fibUnary a))

fibUnaryBiggerThanN : (a : ℕ) → (succ (succ (succ (succ a)))) <N fibUnary (succ (succ (succ (succ a))))
fibUnaryBiggerThanN zero = le zero refl
fibUnaryBiggerThanN (succ a) = TotalOrder.<Transitive ℕTotalOrder (succPreservesInequality (fibUnaryBiggerThanN a)) (ans ((fibUnary (succ a) +N fibUnary a) +N fibUnary (succ a)) ans')
  where
    ans : {t : ℕ} → (u : ℕ) → 1 <N u → succ t <N t +N u
    ans {t} u (le x proof) rewrite Semiring.commutative ℕSemiring x 1 = le x (transitivity (applyEquality succ (Semiring.commutative ℕSemiring x (succ t))) (transitivity (applyEquality (λ i → succ (succ i)) (Semiring.commutative ℕSemiring t x)) (transitivity (applyEquality (_+N t) proof) (Semiring.commutative ℕSemiring u t))))
    ans' : 1 <N (fibUnary (succ a) +N fibUnary a) +N fibUnary (succ a)
    ans' with fibUnaryStrictlyPositive (succ a)
    ... | fibPos with fibUnary (succ a)
    ans' | fibPos | succ bl rewrite Semiring.commutative ℕSemiring (bl +N fibUnary a) (succ bl) = succPreservesInequality (le (bl +N (bl +N fibUnary a)) (Semiring.sumZeroRight ℕSemiring _))

fibUnaryArchimedean : (a : ℕ) → Sg ℕ (λ i → a <N fibUnary i)
fibUnaryArchimedean zero = 0 , le zero refl
fibUnaryArchimedean (succ zero) = 2 , le zero refl
fibUnaryArchimedean (succ (succ zero)) = 3 , le zero refl
fibUnaryArchimedean (succ (succ (succ zero))) = 4 , le 1 refl
fibUnaryArchimedean (succ (succ (succ (succ a)))) = (succ (succ (succ (succ a)))) , fibUnaryBiggerThanN a

record FibEntry : Set where
  field
    prev : BinNat
    curr : BinNat

nextFib : FibEntry → FibEntry
nextFib record { prev = prev ; curr = curr } = record { prev = curr ; curr = prev +B curr }

fib : Sequence BinNat
fib = Sequences.map FibEntry.curr (unfold nextFib record { prev = NToBinNat 0 ; curr = NToBinNat 1 })

fibMove : (n : ℕ) → FibEntry.prev (index (unfold nextFib (record { prev = [] ; curr = one :: [] })) (succ n)) ≡ FibEntry.curr (index (unfold nextFib (record { prev = [] ; curr = one :: [] })) n)
fibMove zero = refl
fibMove (succ n) rewrite indexAndUnfold nextFib (record { prev = [] ; curr = one :: [] }) (succ n) = refl

fibAlternative : (N : ℕ) → index fib N ≡ FibEntry.curr (index (unfold nextFib (record { prev = [] ; curr = one :: [] })) N)
fibAlternative n rewrite equalityCommutative (mapAndIndex (unfold nextFib record { prev = NToBinNat 0 ; curr = NToBinNat 1 }) FibEntry.curr n) = refl

fibAlternative' : (n : ℕ) → FibEntry.curr (index (unfold nextFib (record { prev = [] ; curr = one :: [] })) (succ n)) ≡ FibEntry.prev (index (unfold nextFib (record { prev = [] ; curr = one :: [] })) n) +B FibEntry.curr (index (unfold nextFib (record { prev = [] ; curr = one :: [] })) n)
fibAlternative' zero = refl
fibAlternative' (succ n) rewrite indexAndUnfold nextFib (record { prev = [] ; curr = one :: [] }) (succ n) = refl

fibsCanonical : (n : ℕ) → canonical (index fib n) ≡ index fib n
fibsCanonical zero = refl
fibsCanonical (succ zero) = refl
fibsCanonical (succ (succ n)) = transitivity (applyEquality canonical {index fib (succ (succ n))} {FibEntry.prev (index (unfold nextFib (nextFib (record { prev = [] ; curr = one :: [] }))) n) +B FibEntry.curr (index (unfold nextFib (nextFib (record { prev = [] ; curr = one :: [] }))) n)} (transitivity (fibAlternative (succ (succ n))) (fibAlternative' (succ n)))) (transitivity (sumCanonical (FibEntry.prev (index (unfold nextFib (nextFib (record { prev = [] ; curr = one :: [] }))) n)) (FibEntry.curr (index (unfold nextFib (nextFib (record { prev = [] ; curr = one :: [] }))) n)) (transitivity (transitivity (applyEquality canonical (fibMove n)) (transitivity (applyEquality canonical (equalityCommutative (fibAlternative n))) (transitivity (fibsCanonical n) (fibAlternative n)))) (equalityCommutative (fibMove n))) (transitivity (applyEquality canonical (mapAndIndex (unfold nextFib (nextFib (record { prev = [] ; curr = one :: [] }))) FibEntry.curr n)) (transitivity (fibsCanonical (succ n)) (equalityCommutative (mapAndIndex (unfold nextFib (nextFib (record { prev = [] ; curr = one :: [] }))) FibEntry.curr n))))) (equalityCommutative (transitivity (fibAlternative (succ (succ n))) (fibAlternative' (succ n)))))

fibStart : take 5 fib ≡ vecMap NToBinNat (1 ,- 1 ,- 2 ,- 3 ,- 5 ,- [])
fibStart = refl

fibsMatch : (n : ℕ) → binNatToN (index fib n) ≡ fibUnary n
fibsMatch zero = refl
fibsMatch (succ zero) = refl
fibsMatch (succ (succ n)) rewrite equalityCommutative (fibsMatch (succ n)) | equalityCommutative (fibsMatch n) | equalityCommutative (mapAndIndex (unfold nextFib record { prev = NToBinNat 0 ; curr = NToBinNat 1 }) FibEntry.curr (succ (succ n))) | indexAndUnfold nextFib (record { prev = [] ; curr = one :: [] }) (succ n) | equalityCommutative (mapAndIndex (unfold nextFib (nextFib (record { prev = [] ; curr = one :: [] }))) FibEntry.curr n) | equalityCommutative (mapAndIndex (unfold nextFib (record { prev = [] ; curr = one :: [] })) FibEntry.curr n) | indexAndUnfold nextFib (nextFib (record { prev = [] ; curr = one :: [] })) n | indexAndUnfold nextFib (record { prev = [] ; curr = one :: [] }) n = ans
  where
    x = FibEntry.curr (index (unfold nextFib (record { prev = [] ; curr = one :: [] })) n)
    y = FibEntry.prev (index (unfold nextFib (record { prev = [] ; curr = one :: [] })) n)
    ans : binNatToN (x +B (y +B x)) ≡ binNatToN (y +B x) +N binNatToN x
    ans rewrite +BCommutative x (y +B x) = +BIsHom (y +B x) x

fibsMatch' : (n : ℕ) → NToBinNat (fibUnary n) ≡ index fib n
fibsMatch' n = transitivity (applyEquality NToBinNat (equalityCommutative (fibsMatch n))) (transitivity (binToBin (index fib n)) (fibsCanonical n))

ArchimedeanSequence : {a b c : _} {A : Set a} {S : Setoid {a} {b} A} {_<_ : Rel {a} {c} A} (pOrder : SetoidPartialOrder S _<_) (S : Sequence A) → Set (a ⊔ c)
ArchimedeanSequence {A = A} {_<_ = _<_} _ S = (x : A) → Sg ℕ (λ n → x < (index S n))

archimImpliesTailArchim : {a b c : _} {A : Set a} {S : Setoid {a} {b} A} {_<_ : Rel {a} {c} A} (pOrder : SetoidPartialOrder S _<_) {S : Sequence A} → ArchimedeanSequence pOrder S → (Sg ℕ (λ i → index S 0 < index S i)) → ArchimedeanSequence pOrder (Sequence.tail S)
archimImpliesTailArchim {S} pOrder arch 0small x with arch x
archimImpliesTailArchim pOrder {S} arch (zero , S0<SN) x | zero , pr = exFalso (SetoidPartialOrder.irreflexive pOrder S0<SN)
archimImpliesTailArchim pOrder {S} arch (succ N , S0<SN) x | zero , pr = N , SetoidPartialOrder.<Transitive pOrder pr S0<SN
archimImpliesTailArchim pOrder arch 0small x | succ N , pr = N , pr

takeUpTo : {a b c : _} {A : Set a} {S : Setoid {a} {b} A} {_<_ : Rel {a} {c} A} {pOrder : SetoidPartialOrder S _<_} {seq : Sequence A} → (arch : ArchimedeanSequence pOrder seq) → (lim : A) → List A
takeUpTo {seq = S} arch lim with arch lim
takeUpTo {seq = S} arch lim | zero , pr = []
takeUpTo {seq = S} arch lim | succ N , pr = vecToList (take N S)

archim : ArchimedeanSequence (partialOrderToSetoidPartialOrder BinNatOrder) fib
archim x with fibUnaryArchimedean (binNatToN x)
archim x | N , pr = N , u
  where
    t : (canonical x) <B (NToBinNat (binNatToN (index (Sequences.map FibEntry.curr (unfold nextFib (record { prev = [] ; curr = one :: [] }))) N)))
    t rewrite (fibsMatch N) = identityOfIndiscernablesLeft _<B_ (translate' _ _ pr) (binToBin x)
    u : x <B (index (Sequences.map FibEntry.curr (unfold nextFib (record { prev = [] ; curr = one :: [] }))) N)
    u rewrite equalityCommutative (mapAndIndex (unfold nextFib (record { prev = [] ; curr = one :: [] })) FibEntry.curr N) with transitivity (canonicalFirst x (NToBinNat (fibUnary N)) Equal) (identityOfIndiscernablesLeft _<B_ (translate' (binNatToN x) (fibUnary N) pr) (binToBin x))
    ... | r = identityOfIndiscernablesRight {a = x} {b = NToBinNat (fibUnary N)} {c = FibEntry.curr (index (unfold nextFib (record { prev = [] ; curr = one :: [] })) N)} _<B_ r (transitivity (fibsMatch' N) (fibAlternative N))

isEven : BinNat → Set
isEven [] = True
isEven (zero :: xs) = True
isEven (one :: xs) = False

isEvenAgrees : (n : BinNat) → isEven n → 2 ∣ (binNatToN n)
isEvenAgrees [] nEven = divides (record { quot = zero ; rem = zero ; pr = refl ; remIsSmall = inl (le 1 refl) ; quotSmall = inl (le 1 refl)}) refl
isEvenAgrees (zero :: n) nEven = divides (record { quot = binNatToN n ; rem = zero ; pr = Semiring.sumZeroRight ℕSemiring _ ; remIsSmall = inl (le 1 refl) ; quotSmall = inl (le 1 refl) }) refl

isEvenIncrs : (n : BinNat) → isEven n → isEven (incr (incr n))
isEvenIncrs [] nEven = record {}
isEvenIncrs (zero :: n) nEven = record {}

isEvenAgrees' : (n : ℕ) → 2 ∣ n → isEven (NToBinNat n)
isEvenAgrees' zero nEven = record {}
isEvenAgrees' (succ zero) (divides record { quot = (succ zero) ; rem = zero ; pr = () ; remIsSmall = remIsSmall ; quotSmall = (inl x) } refl)
isEvenAgrees' (succ zero) (divides record { quot = (succ (succ quot)) ; rem = zero ; pr = () ; remIsSmall = remIsSmall ; quotSmall = (inl x) } refl)
isEvenAgrees' (succ (succ n)) (divides record { quot = succ quot ; rem = zero ; pr = pr ; remIsSmall = remIsSmall ; quotSmall = inl 0<2 } refl) with isEvenAgrees' n (divides record { quot = quot ; rem = zero ; pr = transitivity (transitivity (Semiring.sumZeroRight ℕSemiring _) (Semiring.commutative ℕSemiring quot (quot +N 0))) (succInjective (succInjective (transitivity (equalityCommutative (applyEquality succ (transitivity (Semiring.sumZeroRight ℕSemiring (quot +N succ (quot +N zero))) (Semiring.commutative ℕSemiring quot (succ (quot +N 0)))))) pr))) ; remIsSmall = remIsSmall ; quotSmall = inl 0<2 } refl)
... | bl = isEvenIncrs (NToBinNat n) bl

isEvenWellDefined : (n m : BinNat) → canonical n ≡ canonical m → isEven n → isEven m
isEvenWellDefined [] [] n=m nEven = record {}
isEvenWellDefined [] (zero :: m) n=m nEven = record {}
isEvenWellDefined (zero :: n) [] n=m nEven = record {}
isEvenWellDefined (zero :: n) (zero :: m) n=m nEven = record {}
isEvenWellDefined (zero :: n) (one :: m) n=m nEven with canonical n
isEvenWellDefined (zero :: n) (one :: m) () nEven | []
isEvenWellDefined (zero :: n) (one :: m) () nEven | x :: bl

isEvenDecidable : DecidableRelation isEven
isEvenDecidable [] = inl (record {})
isEvenDecidable (zero :: x₁) = inl (record {})
isEvenDecidable (one :: x₁) = inr (λ x → x)

increasing : StrictlyIncreasing (partialOrderToSetoidPartialOrder BinNatOrder) (Sequence.tail fib)
increasing m = SetoidPartialOrder.<WellDefined (partialOrderToSetoidPartialOrder BinNatOrder) (fibsMatch' (succ m)) (fibsMatch' (succ (succ m))) (translate' (fibUnary (succ m)) (fibUnary (succ (succ m))) (fibUnaryIncreasing m))

-- increasingNaturalsBound : (S : Sequence ℕ) → StrictlyIncreasing S → (bound : ℕ) → List ℕ
-- increasingNaturalsBound s n = {!!}

{-
fibsLessThan4Mil : List BinNat
fibsLessThan4Mil = takeUpToMonotone {tOrder = BinNatTOrder} (archimImpliesTailArchim {tOrder = BinNatTOrder} archim (2 , ordersAgree 1 2 (le zero refl))) increasing (one :: one :: one :: one :: zero :: one :: zero :: zero :: zero :: zero :: one :: zero :: zero :: one :: zero :: zero :: zero :: zero :: zero :: zero :: zero :: one :: [])

evens : List BinNat
evens = filter' isEvenDecidable fibsLessThan4Mil

ans : BinNat
ans = fold _+B_ [] evens

-}


------------------------------------------------------------------------
-- The Agda standard library
--
-- Various forms of induction for natural numbers
------------------------------------------------------------------------

module Induction.Nat where

open import Function
open import Data.Nat
open import Data.Fin using (_≺_)
open import Data.Fin.Properties
open import Data.Product
open import Data.Unit
open import Induction
open import Induction.WellFounded as WF
open import Level using (Lift)
open import Relation.Binary.PropositionalEquality
open import Relation.Unary

------------------------------------------------------------------------
-- Ordinary induction

Rec : ∀ ℓ → RecStruct ℕ ℓ ℓ
Rec _ P zero    = Lift ⊤
Rec _ P (suc n) = P n

rec-builder : ∀ {ℓ} → RecursorBuilder (Rec ℓ)
rec-builder P f zero    = _
rec-builder P f (suc n) = f n (rec-builder P f n)

rec : ∀ {ℓ} → Recursor (Rec ℓ)
rec = build rec-builder

------------------------------------------------------------------------
-- Complete induction

CRec : ∀ ℓ → RecStruct ℕ ℓ ℓ
CRec _ P zero    = Lift ⊤
CRec _ P (suc n) = P n × CRec _ P n

cRec-builder : ∀ {ℓ} → RecursorBuilder (CRec ℓ)
cRec-builder P f zero    = _
cRec-builder P f (suc n) = f n ih , ih
  where ih = cRec-builder P f n

cRec : ∀ {ℓ} → Recursor (CRec ℓ)
cRec = build cRec-builder

------------------------------------------------------------------------
-- Complete induction based on _<′_

<-Rec : ∀ {ℓ} → RecStruct ℕ ℓ ℓ
<-Rec = WfRec _<′_

mutual

  <-well-founded : Well-founded _<′_
  <-well-founded n = acc (<-well-founded′ n)

  <-well-founded′ : ∀ n → <-Rec (Acc _<′_) n
  <-well-founded′ zero     _ ()
  <-well-founded′ (suc n) .n ≤′-refl       = <-well-founded n
  <-well-founded′ (suc n)  m (≤′-step m<n) = <-well-founded′ n m m<n

module _ {ℓ} where
  open WF.All <-well-founded ℓ public
    renaming ( wfRec-builder to <-rec-builder
             ; wfRec to <-rec
             )

------------------------------------------------------------------------
-- Complete induction based on _≺_

≺-Rec : ∀ {ℓ} → RecStruct ℕ ℓ ℓ
≺-Rec = WfRec _≺_

≺-well-founded : Well-founded _≺_
≺-well-founded = Subrelation.well-founded ≺⇒<′ <-well-founded

module _ {ℓ} where
  open WF.All ≺-well-founded ℓ public
    renaming ( wfRec-builder to ≺-rec-builder
             ; wfRec to ≺-rec
             )

------------------------------------------------------------------------
-- Examples

private

 module Examples where

  -- Doubles its input.

  twice : ℕ → ℕ
  twice = rec _ λ
    { zero    _       → zero
    ; (suc n) twice-n → suc (suc twice-n)
    }

  -- Halves its input (rounding downwards).
  --
  -- The step function is mentioned in a proof below, so it has been
  -- given a name. (The mutual keyword is used to avoid having to give
  -- a type signature for the step function.)

  mutual

    half₁-step = λ
      { zero          _                → zero
      ; (suc zero)    _                → zero
      ; (suc (suc n)) (_ , half₁n , _) → suc half₁n
      }

    half₁ : ℕ → ℕ
    half₁ = cRec _ half₁-step

  -- An alternative implementation of half₁.

  mutual

    half₂-step = λ
      { zero          _   → zero
      ; (suc zero)    _   → zero
      ; (suc (suc n)) rec → suc (rec n (≤′-step ≤′-refl))
      }

    half₂ : ℕ → ℕ
    half₂ = <-rec _ half₂-step

  -- The application half₁ (2 + n) is definitionally equal to
  -- 1 + half₁ n. Perhaps it is instructive to see why.

  half₁-2+ : ∀ n → half₁ (2 + n) ≡ 1 + half₁ n
  half₁-2+ n = begin

    half₁ (2 + n)                                                         ≡⟨⟩

    cRec (λ _ → ℕ) half₁-step (2 + n)                                     ≡⟨⟩

    half₁-step (2 + n) (cRec-builder (λ _ → ℕ) half₁-step (2 + n))        ≡⟨⟩

    half₁-step (2 + n)
      (let ih = cRec-builder (λ _ → ℕ) half₁-step (1 + n) in
       half₁-step (1 + n) ih , ih)                                        ≡⟨⟩

    half₁-step (2 + n)
      (let ih = cRec-builder (λ _ → ℕ) half₁-step n in
       half₁-step (1 + n) (half₁-step n ih , ih) , half₁-step n ih , ih)  ≡⟨⟩

    1 + half₁-step n (cRec-builder (λ _ → ℕ) half₁-step n)                ≡⟨⟩

    1 + cRec (λ _ → ℕ) half₁-step n                                       ≡⟨⟩

    1 + half₁ n                                                           ∎

    where open ≡-Reasoning

  -- The application half₂ (2 + n) is definitionally equal to
  -- 1 + half₂ n. Perhaps it is instructive to see why.

  half₂-2+ : ∀ n → half₂ (2 + n) ≡ 1 + half₂ n
  half₂-2+ n = begin

    half₂ (2 + n)                                                         ≡⟨⟩

    <-rec (λ _ → ℕ) half₂-step (2 + n)                                    ≡⟨⟩

    half₂-step (2 + n) (<-rec-builder (λ _ → ℕ) half₂-step (2 + n))       ≡⟨⟩

    1 + <-rec-builder (λ _ → ℕ) half₂-step (2 + n) n (≤′-step ≤′-refl)    ≡⟨⟩

    1 + Some.wfRec-builder (λ _ → ℕ) half₂-step (2 + n)
          (<-well-founded (2 + n)) n (≤′-step ≤′-refl)                    ≡⟨⟩

    1 + Some.wfRec-builder (λ _ → ℕ) half₂-step (2 + n)
          (acc (<-well-founded′ (2 + n))) n (≤′-step ≤′-refl)             ≡⟨⟩

    1 + half₂-step n
          (Some.wfRec-builder (λ _ → ℕ) half₂-step n
             (<-well-founded′ (2 + n) n (≤′-step ≤′-refl)))               ≡⟨⟩

    1 + half₂-step n
          (Some.wfRec-builder (λ _ → ℕ) half₂-step n
             (<-well-founded′ (1 + n) n ≤′-refl))                         ≡⟨⟩

    1 + half₂-step n
          (Some.wfRec-builder (λ _ → ℕ) half₂-step n (<-well-founded n))  ≡⟨⟩

    1 + half₂-step n (<-rec-builder (λ _ → ℕ) half₂-step n)               ≡⟨⟩

    1 + <-rec (λ _ → ℕ) half₂-step n                                      ≡⟨⟩

    1 + half₂ n                                                           ∎

    where open ≡-Reasoning

  -- Some properties that the functions above satisfy, proved using
  -- cRec.

  half₁-+₁ : ∀ n → half₁ (twice n) ≡ n
  half₁-+₁ = cRec _ λ
    { zero          _                        → refl
    ; (suc zero)    _                        → refl
    ; (suc (suc n)) (_ , half₁twice-n≡n , _) →
        cong (suc ∘ suc) half₁twice-n≡n
    }

  half₂-+₁ : ∀ n → half₂ (twice n) ≡ n
  half₂-+₁ = cRec _ λ
    { zero          _                        → refl
    ; (suc zero)    _                        → refl
    ; (suc (suc n)) (_ , half₁twice-n≡n , _) →
        cong (suc ∘ suc) half₁twice-n≡n
    }

  -- Some properties that the functions above satisfy, proved using
  -- <-rec.

  half₁-+₂ : ∀ n → half₁ (twice n) ≡ n
  half₁-+₂ = <-rec _ λ
    { zero          _   → refl
    ; (suc zero)    _   → refl
    ; (suc (suc n)) rec →
        cong (suc ∘ suc) (rec n (≤′-step ≤′-refl))
    }

  half₂-+₂ : ∀ n → half₂ (twice n) ≡ n
  half₂-+₂ = <-rec _ λ
    { zero          _   → refl
    ; (suc zero)    _   → refl
    ; (suc (suc n)) rec →
        cong (suc ∘ suc) (rec n (≤′-step ≤′-refl))
    }


open import Data.Nat     using (ℕ; _+_) renaming (_≤?_ to _≤?ₙ_)
open import Data.Bool    using (Bool; not; _∧_)
open import Data.String  using (String; _≟_)
open import Relation.Nullary           using (yes; no)
open import Relation.Nullary.Decidable using (⌊_⌋)
open import Relation.Binary.PropositionalEquality using (_≡_; refl; sym)

vname = String
val = ℕ
bool = Bool
state = vname → val

data aexp : Set where
  N : ℕ → aexp
  V : vname → aexp
  Plus : aexp → aexp → aexp

_[_/_] : aexp → aexp → vname → aexp
N c [ a′ / X ] = N c
V Y [ a′ / X ] with Y ≟ X
... | yes _ = a′
... | no  _ = V Y
Plus a b [ a′ / X ] = Plus (a [ a′ / X ]) (b [ a′ / X ])

_[_::=_] : state → vname → val → state
(s [ X ::= n ]) Y with Y ≟ X
... | yes _ = n
... | no  _ = s Y

aval : aexp → state → val
aval (N c) s = c
aval (V v) s = s v
aval (Plus a b) s = aval a s + aval b s

substitution : ∀ a {X a′ s}
  → aval (a [ a′ / X ]) s ≡ aval a (s [ X ::= aval a′ s ])
substitution (N x) = refl
substitution (V Y) {X} with Y ≟ X
... | yes _ = refl
... | no  _ = refl
substitution (Plus a b) {X}{a′}{s}
  rewrite substitution a {X}{a′}{s}
        | substitution b {X}{a′}{s} = refl

substitution-equiv : ∀{a a₁ a₂ X s}
  → aval a₁ s             ≡ aval a₂ s
  → aval (a [ a₁ / X ]) s ≡ aval (a [ a₂ / X ]) s
substitution-equiv {a}{a₁}{a₂}{X}{s} hyp
  rewrite substitution a {X}{a₁}{s}
        | hyp
        | sym (substitution a {X}{a₂}{s}) = refl

data bexp : Set where
  Bc   : Bool → bexp
  Not  : bexp → bexp
  And  : bexp → bexp → bexp
  Less : aexp → aexp → bexp

_≤?_ : ℕ → ℕ → Bool
a ≤? b = ⌊ a ≤?ₙ b ⌋

bval : bexp → state → bool
bval (Bc x) s = x
bval (Not b) s = not (bval b s)
bval (And a b) s = bval a s ∧ bval b s
bval (Less a b) s = aval a s ≤? aval b s

data com : Set where
  SKIP  : com
  _::=_ : String → aexp → com
  _::_  : com → com → com
  IF_THEN_ELSE_ : bexp → com → com → com
  WHILE_DO_     : bexp → com → com


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

open import Categories.Category.Core using (Category)
open import Categories.Functor.Bifunctor using (Bifunctor)

module Categories.Category.Construction.Wedges {o ℓ e o′ ℓ′ e′} {C : Category o ℓ e} {D : Category o′ ℓ′ e′}
  (F : Bifunctor (Category.op C) C D) where

open import Level

open import Categories.Category.Core using (Category)
open import Categories.Diagram.Wedge F

Wedges : Category (o ⊔ ℓ ⊔ e ⊔ o′ ⊔ ℓ′ ⊔ e′) (o ⊔ ℓ ⊔ e ⊔ o′ ⊔ ℓ′ ⊔ e′) e′
Wedges =  record
  { Obj = Wedge
  ; _⇒_ = Wedge-Morphism
  ; _≈_ = λ M N → u M ≈ u N
  ; id = Wedge-id
  ; _∘_ = Wedge-Morphism-∘
  ; assoc = assoc
  ; sym-assoc = sym-assoc
  ; identityˡ = identityˡ
  ; identityʳ = identityʳ
  ; identity² = identity²
  ; equiv = record { refl = Equiv.refl ; sym = Equiv.sym ; trans = Equiv.trans }
  ; ∘-resp-≈ = ∘-resp-≈
  }
  where
  open Wedge-Morphism
  open Category D


module Golden.InsertionSort where

open import Agda.Builtin.Nat
open import Agda.Builtin.List
open import Agda.Builtin.Bool

insert : Nat -> List Nat -> List Nat
insert a [] = a ∷ []
insert x (a ∷ b) with x < a
... | true = x ∷ a ∷ b
... | false = a ∷ (insert x b)

foldr : ∀ {a b : Set} → (a → b → b) → b → List a -> b
foldr f ini [] = ini
foldr f ini (x ∷ l) = f x (foldr f ini l)

insertSort : List Nat -> List Nat
insertSort = foldr insert []

atDef : ∀ {a : Set} → a → List a -> Nat -> a
atDef def (x ∷ l) zero = x
atDef def (x ∷ l) (suc ix) = atDef def l ix
atDef def _ _ = def

lst : List Nat
lst = 4 ∷ 2 ∷ 7 ∷ []

slst : List Nat
slst = insertSort lst

l0 : Nat
l0 = atDef 0 slst 0

l1 : Nat
l1 = atDef 0 slst 1

l2 : Nat
l2 = atDef 0 slst 2


{-# OPTIONS --safe #-}
module Cubical.Algebra.OrderedCommMonoid.Instances where

open import Cubical.Foundations.Prelude

open import Cubical.Algebra.OrderedCommMonoid.Base

open import Cubical.Data.Nat
open import Cubical.Data.Nat.Order

ℕ≤+ : OrderedCommMonoid ℓ-zero ℓ-zero
ℕ≤+ .fst = ℕ
ℕ≤+ .snd .OrderedCommMonoidStr._≤_ = _≤_
ℕ≤+ .snd .OrderedCommMonoidStr._·_ = _+_
ℕ≤+ .snd .OrderedCommMonoidStr.ε = 0
ℕ≤+ .snd .OrderedCommMonoidStr.isOrderedCommMonoid =
  makeIsOrderedCommMonoid
         isSetℕ
         +-assoc +-zero (λ _ → refl) +-comm
         (λ _ _ → isProp≤) (λ _ → ≤-refl) (λ _ _ _ → ≤-trans) (λ _ _ → ≤-antisym)
         (λ _ _ _ → ≤-+k) (λ _ _ _ → ≤-k+)

ℕ≤· : OrderedCommMonoid ℓ-zero ℓ-zero
ℕ≤· .fst = ℕ
ℕ≤· .snd .OrderedCommMonoidStr._≤_ = _≤_
ℕ≤· .snd .OrderedCommMonoidStr._·_ = _·_
ℕ≤· .snd .OrderedCommMonoidStr.ε = 1
ℕ≤· .snd .OrderedCommMonoidStr.isOrderedCommMonoid =
  makeIsOrderedCommMonoid
        isSetℕ
        ·-assoc ·-identityʳ ·-identityˡ ·-comm
        (λ _ _ → isProp≤) (λ _ → ≤-refl) (λ _ _ _ → ≤-trans) (λ _ _ → ≤-antisym)
        (λ _ _ _ → ≤-·k) lmono
  where lmono : (x y z : ℕ) → x ≤ y → z · x ≤ z · y
        lmono x y z x≤y = subst ((z · x) ≤_) (·-comm y z) (subst (_≤ (y · z)) (·-comm x z) (≤-·k x≤y))


------------------------------------------------------------------------
-- The Agda standard library
--
-- Properties of operations on containers
------------------------------------------------------------------------

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

module Data.Container.Properties where

import Function as F
open import Relation.Binary

open import Data.Container.Core
open import Data.Container.Relation.Binary.Equality.Setoid

module _ {s p} {C : Container s p} where

  map-identity : ∀ {x e} (X : Setoid x e) xs →
                 Eq X C (map F.id xs) xs
  map-identity X xs = refl X C

  map-compose : ∀ {x y z e} {X : Set x} {Y : Set y} (Z : Setoid z e) g (f : X → Y) xs →
                Eq Z C (map g (map f xs)) (map (g F.∘′ f) xs)
  map-compose Z g f xs = refl Z C


module Data.Dyck.Sized where

open import Prelude
open import Data.Nat using (_+_)
open import Data.Vec.Iterated using (Vec; _∷_; []; foldlN; head; foldl′)

open import Cubical.Foundations.Prelude using (substRefl)
open import Cubical.Data.Sigma.Properties using (Σ≡Prop)
open import Data.Nat.Properties using (isSetℕ)

private variable
    n m k : ℕ
    n⊙ m⊙ k⊙ : ℕ → ℕ

--------------------------------------------------------------------------------
-- Tree
--------------------------------------------------------------------------------

data Tree : Type where
  ⟨⟩ : Tree
  _*_ : Tree → Tree → Tree

size⊙ : Tree → ℕ → ℕ
size⊙ ⟨⟩ = suc
size⊙ (xs * ys) = size⊙ xs ∘ size⊙ ys

size : Tree → ℕ
size t = size⊙ t zero

--------------------------------------------------------------------------------
-- Dyck Word
--------------------------------------------------------------------------------

infixr 6 ⟨_ ⟩_
data Dyck : ℕ → ℕ → Type where
  ⟩! : Dyck 1 0
  ⟨_ : Dyck (1 + n) m → Dyck n (1 + m)
  ⟩_ : Dyck (1 + n) m → Dyck (2 + n) m

--------------------------------------------------------------------------------
-- Tree to Stack
--------------------------------------------------------------------------------

tree→stack⊙ : (t : Tree) → Dyck (suc m) k → Dyck m (size⊙ t k)
tree→stack⊙ ⟨⟩         = ⟨_
tree→stack⊙ (xs * ys) = tree→stack⊙ xs ∘ tree→stack⊙ ys ∘ ⟩_

tree→stack : (t : Tree) → Dyck 0 (size t)
tree→stack tr = tree→stack⊙ tr ⟩!

--------------------------------------------------------------------------------
-- Stack to Tree
--------------------------------------------------------------------------------

stack→tree⊙ : Dyck n m → Vec Tree n → Tree
stack→tree⊙ ⟩!     (v ∷ [])       = v
stack→tree⊙ (⟨ is) st             = stack→tree⊙ is (⟨⟩ ∷ st)
stack→tree⊙ (⟩ is) (t₁ ∷ t₂ ∷ st) = stack→tree⊙ is (t₂ * t₁ ∷ st)

stack→tree : Dyck 0 n → Tree
stack→tree ds = stack→tree⊙ ds []

--------------------------------------------------------------------------------
-- Size lemma
--------------------------------------------------------------------------------

stack→tree-size⊙ :  {st : Vec Tree n} (is : Dyck n m) →
 size (stack→tree⊙ is st) ≡ foldl′ size⊙ m st
stack→tree-size⊙ ⟩!     = refl
stack→tree-size⊙ (⟨ is) = stack→tree-size⊙ is
stack→tree-size⊙ (⟩ is) = stack→tree-size⊙ is

--------------------------------------------------------------------------------
-- Roundtrip
--------------------------------------------------------------------------------

tree→stack→tree⊙ :  {is : Dyck (1 + n) m} {st : Vec Tree n} (e : Tree) →
  stack→tree⊙ (tree→stack⊙ e is) st ≡ stack→tree⊙ is (e ∷ st)
tree→stack→tree⊙ ⟨⟩         = refl
tree→stack→tree⊙ (xs * ys) = tree→stack→tree⊙ xs ; tree→stack→tree⊙ ys

foldlNN : ∀ {A : Type a} {p} (P : ℕ → ℕ → Type p) →
          (f : A → ℕ → ℕ) →
          (∀ {n m} → (x : A) → P (suc n) m → P n (f x m)) →
          P n m →
          (xs : Vec A n) → P zero (foldl′ f m xs)
foldlNN {n = zero} P f s b xs = b
foldlNN {n = suc n} P f s b (x ∷ xs) = foldlNN P f s (s x b) xs

stack→tree→stack⊙ :  {st : Vec Tree n} (is : Dyck n m) →
 subst (Dyck 0) (stack→tree-size⊙ is) (tree→stack (stack→tree⊙ is st))
   ≡ foldlNN Dyck size⊙ tree→stack⊙ is st
stack→tree→stack⊙  ⟩!       = substRefl {B = Dyck 0} _
stack→tree→stack⊙ (⟨ is) = stack→tree→stack⊙ is
stack→tree→stack⊙ (⟩ is) = stack→tree→stack⊙ is

--------------------------------------------------------------------------------
-- Isomorphism
--------------------------------------------------------------------------------

STree : ℕ → Type
STree = fiber size

tree-stack : STree n ⇔ Dyck 0 n
tree-stack .fun (t , n) = subst (Dyck 0) n (tree→stack t)
tree-stack .inv st .fst = stack→tree st
tree-stack .inv st .snd = stack→tree-size⊙ st
tree-stack .leftInv  (t , sz≡) =
  Σ≡Prop
  (λ _ → isSetℕ _ _)
  (J
    (λ n sz≡ → stack→tree (subst (Dyck 0) sz≡ (tree→stack t)) ≡ t)
    (cong stack→tree (substRefl {B = Dyck 0} (tree→stack t)) ; tree→stack→tree⊙ t) sz≡)
tree-stack .rightInv st = stack→tree→stack⊙ st

--------------------------------------------------------------------------------
-- Finite
--------------------------------------------------------------------------------

open import Data.List

support-dyck : ∀ n m → List (Dyck n m)
support-dyck = λ n m → sup-k n m id []
  module ListDyck where
  Diff : Type → Type₁
  Diff A = ∀ {B : Type} → (A → B) → List B → List B

  infixr 5 _++′_
  _++′_ : Diff A → Diff A → Diff A
  xs ++′ ys = λ k → xs k ∘ ys k

  mutual
    sup-k : ∀ n m → Diff (Dyck n m)
    sup-k n m = end n m ++′ lefts n m  ++′ rights n m

    lefts : ∀ n m → Diff (Dyck n m)
    lefts n zero    k = id
    lefts n (suc m) k = sup-k (suc n) m (k ∘ ⟨_)

    rights : ∀ n m → Diff (Dyck n m)
    rights (suc (suc n)) m k = sup-k (suc n) m (k ∘ ⟩_)
    rights _             m k = id

    end : ∀ n m → Diff (Dyck n m)
    end 1 0 k xs = k ⟩! ∷ xs
    end _ _ k = id

open import Data.List.Membership
open import Data.Fin

cover-dyck : (x : Dyck n m) → x ∈ support-dyck n m
cover-dyck x = go _ _ x id []
  where
  open ListDyck

  mutual
    pushLefts : ∀ n m (k : Dyck n m → B) x xs → x ∈ xs → x ∈ lefts n m k xs
    pushLefts n (suc m) k = pushSup (suc n) m (λ z → k (⟨ z))
    pushLefts _ zero    k x xs p = p

    pushEnd : ∀ n m (k : Dyck n m → B) x xs → x ∈ xs → x ∈ end n m k xs
    pushEnd 0 m k x xs p = p
    pushEnd 1 0 k x xs (i , p) = fs i , p
    pushEnd 1 (suc m) k x xs p = p
    pushEnd (suc (suc n)) m k x xs p = p

    pushRights : ∀ n m (k : Dyck n m → B) x xs → x ∈ xs → x ∈ rights n m k xs
    pushRights (suc (suc n)) m k = pushSup (suc n) m λ z → k (⟩ z)
    pushRights 1 _ _ _ _ p = p
    pushRights 0 _ _ _ _ p = p

    pushSup : ∀ n m (k : Dyck n m → B) x xs → x ∈ xs → x ∈ sup-k n m k xs
    pushSup n m k x xs p = pushEnd n m k x (lefts n m k (rights n m k xs)) (pushLefts n m k x (rights n m k xs) (pushRights n m k x xs p))

  go : ∀ n m → (x : Dyck n m) → (k : Dyck n m → A) → (xs : List A) → k x ∈ sup-k n m k xs
  go .1 .0 ⟩! k xs = f0 , refl
  go 0 (suc m) (⟨ x) k xs = go 1 m x (k ∘ ⟨_) xs
  go 1 (suc m) (⟨ x) k xs = go 2 m x (k ∘ ⟨_) xs
  go (suc n@(suc _)) (suc m) (⟨ x) k xs = go (suc (suc n)) m x (k ∘ ⟨_) (rights (suc n) (suc m) k xs)
  go (suc n) m (⟩ x) k xs =
    let p = go n m x (k ∘ ⟩_) xs
    in pushEnd (suc n) m k (k (⟩ x)) (lefts (suc n) m k _) (pushLefts (suc n) m k (k (⟩ x)) (rights (suc n) m k xs) p)


------------------------------------------------------------------------------
-- Definition of FOTC streams using the Agda co-inductive combinators
------------------------------------------------------------------------------

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

module FOT.FOTC.Data.Stream.TypeSL where

open import Codata.Musical.Notation
open import Data.Product renaming ( _×_ to _∧_ )
open import Relation.Binary.PropositionalEquality

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

-- We add 3 to the fixities of the Agda standard library 0.8.1 (see
-- Data/List.agda).
infixr 8 _∷_

data D : Set where
  _∷_ : D → D → D

data Stream : D → Set where
  consS : ∀ x {xs} → ∞ (Stream xs) → Stream (x ∷ xs)

Stream-out : ∀ {xs} → Stream xs →
             ∃ λ x' → ∃ λ xs' → Stream xs' ∧ xs ≡ x' ∷ xs'
Stream-out (consS x' {xs'} Sxs') = x' , xs' , ♭ Sxs' , refl

{-# NON_TERMINATING #-}
Stream-coind :
  (A : D → Set) →
  (∀ {xs} → A xs → ∃ λ x' → ∃ λ xs' → xs ≡ x' ∷ xs' ∧ A xs') →
  ∀ {xs} → A xs → Stream xs
Stream-coind A h Axs with h Axs
... | x' , xs' , prf₁ , Axs' = subst Stream (sym prf₁) prf₂
  where
  prf₂ : Stream (x' ∷ xs')
  prf₂ = consS x' (♯ Stream-coind A h Axs')


module Haskell.Prim.Maybe where

open import Agda.Builtin.List public

open import Haskell.Prim
open import Haskell.Prim.List

--------------------------------------------------
-- Maybe

data Maybe {ℓ} (a : Set ℓ) : Set ℓ where
  Nothing : Maybe a
  Just    : a -> Maybe a

maybe : ∀ {ℓ₁ ℓ₂} {a : Set ℓ₁} {b : Set ℓ₂} → b → (a → b) → Maybe a → b
maybe n j Nothing  = n
maybe n j (Just x) = j x

mapMaybe : (a -> Maybe b) -> List a -> List b
mapMaybe _ [] = []
mapMaybe f (x ∷ xs) =
  case f x of λ where
    Nothing -> mapMaybe f xs
    (Just v) -> v ∷ (mapMaybe f xs)


{-# OPTIONS --type-in-type #-}

U = Set

id  : {A : Set} → A → A
id = λ x → x

id2 : ∀ {A} → A → A
id2 = id id

id3 : ∀ {A} → A → A
id3 = λ x → id x

id4 : {A : Set} -> A -> A
id4 = \x -> x

the : (A : Set) → A → A
the = λ _ x → x

constTy = {A B : Set} → A → B → A

const : constTy
const = λ x y → x

constU = const {Set}

namedArgTest  = const {B = U} U
namedArgTest2 = the constTy (λ x y → x) {B = U} U

-- Church bools
--------------------------------------------------------------------------------
Bool = (B : U) → B → B → B

true  : Bool
true = λ _ t f → t

false : Bool
false = λ _ t f  → f

and : Bool → Bool → Bool
and = λ b1 b2 → b1 Bool true b2

-- Church natural numbers
--------------------------------------------------------------------------------

Nat : U
Nat = (n : U) → (n → n) → n → n

zero : Nat
zero = λ n s z → z

suc : Nat → Nat
suc = λ a n s z → s (a n s z)

n2 : Nat
n2 = λ n s z → s (s z)

n5 : Nat
n5 = λ n s z → s (s (s (s (s z))))

mul : Nat → Nat → Nat
mul = λ a b n s z → a n (b n s) z

add : Nat → Nat → Nat
add = λ a b n s z → a n s (b n s z)

n10    = mul n2    n5
n10b   = mul n5    n2
n100   = mul n10   n10
n100b  = mul n10b  n10b
n10k   = mul n100  n100
n10kb  = mul n100b n100b
n100k  = mul n10k  n10
n100kb = mul n10kb n10b
n1M    = mul n10k  n100
n1Mb   = mul n10kb n100b
n10M   = mul n1M   n10
n10Mb  = mul n1Mb  n10b
n100M  = mul n10M  n10
n200M  = mul n2    n100M

-- Church lists
--------------------------------------------------------------------------------

List : U → U
List = λ a → (l : U) → (a → l → l) → l → l

lnil  : ∀{a} → List a
lnil = λ l c n → n

lcons : ∀{a} → a → List a → List a
lcons = λ a as l c n → c a (as l c n)

list1 = lcons true (lcons false (lcons false lnil))

map : ∀{a b} → (a → b) → List a → List b
map = λ f as l c → as l (λ a → c (f a))

-- Church vectors
--------------------------------------------------------------------------------

Vec : U → Nat → U
Vec = λ a n → (V : Nat → U) → V zero → (∀{n} → a → V n → V (suc n)) → V n

vnil : ∀{a} → Vec a zero
vnil = λ V n c → n

vcons : ∀{a n} → a → Vec a n → Vec a (suc n)
vcons = λ a as V n c → c a (as V n c)

vec1 = vcons true (vcons false (vcons true vnil))

vecStress =
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true (vcons true (vcons true (vcons true (vcons true
   (vcons true (vcons true
   vnil))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
       ))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
       ))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
       ))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
       ))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
       ))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
       ))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
       ))))))))))))))))))))))))))))))))))))

-- Leibniz (Church) propositional equality, useful for testing conversion.
--------------------------------------------------------------------------------

Eq : ∀{A : U} → A → A → U
Eq {A} = λ x y → (P : A → U) → P x → P y

refl : ∀{A}{x : A} → Eq x x
refl = λ P px → px

trans : ∀{A}{x y z : A} → Eq x y → Eq y z → Eq x z
trans = λ p q → q _ p

sym : ∀{A}{x y : A} → Eq x y → Eq y x
sym {_}{x}{y} = λ p → p (λ y → Eq y x) refl

ap : ∀{A B}(f : A → B){x y : A} → Eq x y → Eq (f x) (f y)
ap = λ f {x}{y} p → p (λ y → Eq (f x) (f y)) refl

-- Pairs, Top, Bot
--------------------------------------------------------------------------------

Pair : U → U → U
Pair = λ A B → (Pair : U)(pair : A → B → Pair) → Pair

pair : ∀{A B} → A → B → Pair A B
pair = λ a b Pair pair → pair a b

proj1 : ∀{A B} → Pair A B → A
proj1 = λ p → p _ (λ x y → x)

proj2 : ∀{A B} → Pair A B → B
proj2 = λ p → p _ (λ x y → y)

Top : U
Top = (Top : U)(tt : Top) → Top

tt : Top
tt = λ Top tt → tt

Bot : U
Bot = (Bot : U) → Bot

-- Dependent function composition
--------------------------------------------------------------------------------

comp :
  ∀ {A : U}
  {B : A → U}
  {C : ∀{a} → B a → U}
  (f : ∀{a}(b : B a) → C b)
  (g : (a : A) → B a)
  (x : A)
  → C (g x)
comp = λ f g a → f (g a)

compTest1 : Nat → Nat
compTest1 = comp suc suc

compTest2 : ∀{m A} → A → Vec A m → Vec A (suc (suc m))
compTest2 = λ a → comp (vcons a) (vcons a)

-- Some stress tests
--------------------------------------------------------------------------------

nfun : Nat → U
nfun n = n U (λ A → U → A) U

-- OK
synEqTest1 : Eq n1M n1M
synEqTest1 = refl

-- fail
-- synEqTest2 : nfun n10k → nfun n10k
-- synEqTest2 = λ x → x

-- OK
idStress : ∀{A} → A → A
idStress =
   id id id id id id id id id id id id id id id id id id id id
   id id id id id id id id id id id id id id id id id id id id

-- fail
-- pairStress : Top
-- pairStress =
--    let x0  = pair tt tt
--        x1  = pair x0  x0
--        x2  = pair x1  x1
--        x3  = pair x2  x2
--        x4  = pair x3  x3
--        x5  = pair x4  x4
--        x6  = pair x5  x5
--        x7  = pair x6  x6
--        x8  = pair x7  x7
--        x9  = pair x8  x8
--        x10 = pair x9  x9
--        x11 = pair x10 x10
--        x12 = pair x11 x11
--        x13 = pair x12 x12
--        x14 = pair x13 x13
--        x15 = pair x14 x14
--        x16 = pair x15 x15
--        x17 = pair x16 x16
--        x18 = pair x17 x17
--        x19 = pair x18 x18
--        x20 = pair x19 x19
--    in tt

forceNat : Nat → Bool
forceNat n = n _ (λ b → b) true

computeTest = forceNat n10M

-- Conversion checking tests
--------------------------------------------------------------------------------

-- conv100k : Eq n100k n100kb
-- conv100k = refl

-- conv1M : Eq n1M n1Mb
-- conv1M = refl

-- OOM
-- conv10M : Eq n10M n1M0b
-- conv10M = refl

-- convNfun1M : Eq (nfun n1M) (nfun n1Mb)
-- convNfun1M = refl

-- conv10kmeta : Eq n10k (add n10kb _)
-- conv10kmeta = refl

-- OOM
-- conv1Mmeta : Eq n1M (add n1Mb _)
-- conv1Mmeta = refl

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

-- normalized instantly
lazyU = const U (forceNat n10M)

localCBN : Nat → Bool
localCBN
  = λ n → let m = forceNat (mul n n100k)
          in n _ (λ b → and m b) true

cbnReference = forceNat (mul n10 n100k)
localCBNtest = localCBN n10

-- Church-coded simply typed lambda calculus
--------------------------------------------------------------------------------

Ty : U
Ty
 = (Ty  : U)
   (ι   : Ty)
   (fun : Ty → Ty → Ty)
 → Ty

ι : Ty
ι
 = λ _ ι _ → ι

fun : Ty → Ty → Ty
fun
 = λ A B Ty ι fun → fun (A Ty ι fun) (B Ty ι fun)

Con : U
Con
 = (Con : U)
   (nil  : Con)
   (cons : Con → Ty → Con)
 → Con

nil : Con
nil
 = λ Con nil cons → nil

cons : Con → Ty → Con
cons
 = λ Γ A Con nil cons → cons (Γ Con nil cons) A

Var : Con → Ty → U
Var
 = λ Γ A →
   (Var : Con → Ty → U)
   (vz  : ∀{Γ A} → Var (cons Γ A) A)
   (vs  : ∀{Γ B A} → Var Γ A → Var (cons Γ B) A)
 → Var Γ A

vz : ∀{Γ A} → Var (cons Γ A) A
vz
 = λ Var vz vs → vz

vs : ∀{Γ B A} → Var Γ A → Var (cons Γ B) A
vs
 = λ x Var vz vs → vs (x Var vz vs)

Tm : Con → Ty → U
Tm
 = λ Γ A →
   (Tm  : Con → Ty → U)
   (var : ∀{Γ A} → Var Γ A → Tm Γ A)
   (lam : ∀{Γ A B} → Tm (cons Γ A) B → Tm Γ (fun A B))
   (app : ∀{Γ A B} → Tm Γ (fun A B) → Tm Γ A → Tm Γ B)
 → Tm Γ A

var : ∀{Γ A} → Var Γ A → Tm Γ A
var
 = λ x Tm var lam app → var x

lam : ∀{Γ A B} → Tm (cons Γ A) B → Tm Γ (fun A B)
lam
 = λ t Tm var lam app → lam (t Tm var lam app)

app : ∀{Γ A B} → Tm Γ (fun A B) → Tm Γ A → Tm Γ B
app
 = λ t u Tm var lam app → app (t Tm var lam app) (u Tm var lam app)

-- Well-typed interpreter for Church-coded STLC
--------------------------------------------------------------------------------

EvalTy : Ty → U
EvalTy
 = λ A → A _ Bot (λ A B → A → B)

EvalCon : Con → U
EvalCon
 = λ Γ → Γ _ Top (λ Δ A → Pair Δ (EvalTy A))

EvalVar : ∀{Γ A} → Var Γ A → EvalCon Γ → EvalTy A
EvalVar
 = λ x → x (λ Γ A → EvalCon Γ → EvalTy A)
           (λ env → proj2 env)
           (λ rec env → rec (proj1 env))

EvalTm : ∀{Γ A} → Tm Γ A → EvalCon Γ → EvalTy A
EvalTm
 = λ t → t (λ Γ A → EvalCon Γ → EvalTy A)
          EvalVar
          (λ t env α → t (pair env α))
          (λ t u env → t env (u env))

-- Large embedded STLC term
--------------------------------------------------------------------------------

t1 : Tm nil (fun (fun ι ι) (fun ι ι))
t1
 = lam (lam (
      app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz)) (app (var (vs vz))
     (var vz))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
     )))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
     )))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
     ))))))))))))))))))))))))))))))))))))))
     ))

-- test evaluation
evalTest = EvalTm t1 tt


-- Implementation of Gödel's System T as an example for
-- Σ-terms (Σ = {Z, S}; α(Z) = 0; α(S) = 1) with primitive recursion
module SystemT where

open import Data.Vec hiding (_++_)
open import Data.Nat
open import Data.List
open import Data.List.All

-- Not in use yet:
-- Σ is a vector of given length n of natural numbers denoting the arity of symbols
data Σ {n : ℕ} : Set where
  intr : Vec ℕ n → Σ

-- Successor has arity one, Zero has arity 0
Σ-Nat = intr (1 ∷ 0 ∷ [])

-- Types
data Type : Set where
  Nat  : Type
  Fun  : Type → Type → Type

-- Type environment
Env : Set
Env = List Type

-- Environment lookup
data _∈`_ : Type → Env → Set where
  here  : ∀ {φ A}    → A ∈` (A ∷ φ)
  there : ∀ {φ A A'} → A ∈` φ → A ∈` (A' ∷ φ)

-- Zero & Successor, Variables, prim. recursion on Σ-Nat, Abstraction, Application
data Expr : Env → Type → Set where
  Z      : ∀ {φ}     → Expr φ Nat
  S      : ∀ {φ}     → Expr φ Nat → Expr φ Nat
  Var    : ∀ {φ A}   → A ∈` φ → Expr φ A
  NatRec : ∀ {φ A}   → Expr φ A → Expr (A ∷ φ) A → Expr φ Nat → Expr φ A
  Abs    : ∀ {φ A B} → Expr (A ∷ φ) B → Expr φ (Fun A B)
  App    : ∀ {φ A B} → Expr φ (Fun A B) → Expr φ A → Expr φ B

----- Big step semantics using Agda semantics -----
Value : Type → Set
Value Nat        = ℕ
Value (Fun A B)  = Value A → Value B

-- Lookup in environment of values
access : ∀ {A φ} → A ∈` φ → All Value φ → Value A
access here      (px ∷ p) = px
access (there x) (px ∷ p) = access x p

-- Conversion of a natural number to an expression
natconv : ∀ {φ} → ℕ → Expr φ Nat
natconv zero    = Z
natconv (suc n) = S (natconv n)

natrec : ∀ {A : Set} → A → (A → A) → ℕ → A
natrec vz vs zero    = vz
natrec vz vs (suc n) = vs (natrec vz vs n)

-- Evaluation
eval : ∀ {φ A} → Expr φ A → All Value φ → Value A
eval Z ϱ                   = zero
eval (S x) ϱ               = suc (eval x ϱ)
eval (Var x) ϱ             = access x ϱ
eval (NatRec ez es enat) ϱ = natrec (eval ez ϱ) (λ v → eval es (v ∷ ϱ)) (eval enat ϱ)
eval (Abs e) ϱ             = λ x → eval e (x ∷ ϱ)
eval (App e e₁) ϱ          = (eval e ϱ) (eval e₁ ϱ)

----- Small step semantics -----
----- Substitution: Two methods.
-- a) Define simultaneous substitution with a map from variables to expression; see LDLC.agda and
--    the book "Programming Language Foundations in Agda"
-- b) Define singular substitution but "within" environments, i.e. for terms of the form (φ' ++ A ∷ φ)
--    This makes the Var case a tiny bit more tricky. See also type substitution in LDLC.agda
----- Method b is chosen since it is a bit shorter and (subjectively) easier to understand

-- Lemmas required for substitution, esp. Var case
-- 0) Insertion: Insert a variable and adjust DeBruijn indices
--  0.1) De Bruijn adjustement
insdebr : ∀ {φ φ'} {A B} → B ∈` (φ' ++ φ) → B ∈` (φ' ++ (A ∷ φ))
insdebr {φ' = []} here           = there here
insdebr {φ' = []} (there x)      = there (there x)
insdebr {φ' = y ∷ ys} here      = here
insdebr {φ' = y ∷ ys} (there x) = there (insdebr x)
--  0.2) Insertion
ins : ∀ {φ φ'} {A B} → Expr (φ' ++ φ) B → Expr (φ' ++ A ∷ φ) B
ins Z                                   = Z
ins (S x)                               = S (ins x)
ins (Var x)                             = Var (insdebr x)
ins {φ' = φ'} (NatRec{A = A'} ez es en) = NatRec (ins ez) (ins{φ' = A' ∷ φ'} es) (ins en)
ins {φ' = φ'} (Abs{A = A'} x)           = Abs (ins{φ' = A' ∷ φ'}  x)
ins (App x y)                           = App (ins x) (ins y)
-- 1) Variable substitution
varsub : ∀ {φ φ'} {A B} → A ∈` (φ' ++ B ∷ φ) → Expr φ B → Expr (φ' ++ φ) A
varsub {φ' = []} here M           = M
varsub {φ' = []} (there x) M      = Var x
varsub {φ' = z ∷ zs} here M      = Var here
varsub {φ' = z ∷ zs} (there x) M = ins{φ' = []} (varsub x M)

-- Substitution: Given an expression that binds topmost variable to B and has type A &
--               an expression of type B => we get an expression of type A by binding the topmost
--               variable to that expression
_[[_]] : ∀ {φ φ'} {A B : Type} → Expr (φ' ++ B ∷ φ) A → Expr φ B → Expr (φ' ++ φ) A
Z [[ M ]]               = Z
S N [[ M ]]             = S (N [[ M ]])
Var x [[ M ]]           = varsub x M
NatRec N N' N'' [[ M ]] = NatRec (N [[ M ]]) (N' [[ M ]]) (N'' [[ M ]])
Abs N [[ M ]]           = Abs (N [[ M ]])
App N N' [[ M ]]        = App (N [[ M ]]) (N' [[ M ]])

-- Values
data Value' {φ} : (t : Type) → Expr φ t → Set where
  VZero : Value' Nat Z
  VSuc  : ∀ {x} →  Value' Nat x → Value' Nat (S x)
  VFun  : ∀ {A B exp} → Value' (Fun A B) (Abs exp)

-- Semantics
data _↠_ {φ} : {A : Type} → Expr φ A → Expr φ A → Set where
  -- Basic call-by-value λ-calc. stuff
  -- Application: Elimination & Reduction
  ξ-App1 : ∀ {A B} {L L' : Expr φ (Fun B A)} {M}
           → L ↠ L'
           → App L M ↠ App L' M

  ξ-App2 : ∀ {A B} {M M' : Expr φ A} {L : Expr φ (Fun A B)}
           → Value' (Fun A B) L
           → M ↠ M'
           → App L M ↠ App L M'

  β-App : ∀ {A B M exp}
          → Value' B M
          → App{B = A} (Abs exp) M ↠ (exp [[ M ]])

  -- Elimination for successor
  ξ-Nat : ∀ {L L'}
          → L ↠ L'
          → S L ↠ S L'

  -- Interesting cases
  ξ-NatRec : ∀ {A} {Ez : Expr φ A} {Es En En'}
             → En ↠ En'
             → NatRec Ez Es En ↠ NatRec Ez Es En'

  β-NatRec-Z : ∀ {A} {Ez : Expr φ A} {Es}
               → NatRec Ez Es Z ↠ Ez

  β-NatRec-S : ∀ {A} {Ez : Expr φ A} {Es} {n}
               → Value' Nat n
               → NatRec Ez Es (S n) ↠ (Es [[ NatRec Ez Es n ]])

----- Reflexive & Transitive closure of ↠; defined for chain reasoning
infix 1 begin_
infix 2 _⇨_
infix 2 _⇨⟨_⟩_
infix 3 _∎

data _⇨_ : ∀ {φ} {A} → Expr φ A → Expr φ A → Set where
  -- Reflexivity / end of Reasoning
  _∎ : ∀ {φ} {A} (L : Expr φ A)
       → (L ⇨ L)

  -- Transitivity / chain Reasoning
  _⇨⟨_⟩_ : ∀ {φ A} {L' M : Expr φ A}
           → (L : Expr φ A)
           → L ↠ L'
           → L' ⇨ M
           → L ⇨ M

-- Highlight the start of a chain of reasoning
begin_ : ∀ {φ} {A} {M N : Expr φ A} → M ⇨ N → M ⇨ N
begin x = x

----- Progress Theorem; also used to generate evaluation sequence (since it defines how to evaluate)
-- Definition: ∀ M : (Value'(M) ∨ (∃ N : M ↠ N))
data Progress {A} (M : Expr [] A) : Set where
  step : ∀ {N : Expr [] A} → M ↠ N → Progress M
  done : Value' A M → Progress M

-- Proof
progress : ∀ {A} → (M : Expr [] A) → Progress M
progress Z                 = done VZero
progress (S L) with progress L
... | step L↠N            = step (ξ-Nat L↠N)
... | done y               = done (VSuc y)
progress (NatRec ez es en) with progress en
... | step en↠en'         = step (ξ-NatRec en↠en')
... | done (VZero)         = step (β-NatRec-Z)
... | done (VSuc x)        = step (β-NatRec-S x)
progress (Abs L)           = done VFun
progress (App L N) with progress L
... | step L↠L'           = step (ξ-App1 L↠L')
... | done VFun with progress N
...   | step N↠N'         = step (ξ-App2 VFun N↠N')
...   | done val           = step (β-App val)

----- Generation of evaluation sequence (idea & implementation from PLFA)
-- Used to limit number of steps taken
data Gas : Set where
  gas : ℕ → Gas

-- Either enough steps taken or Value' reached
data Finished {φ A} (N : Expr φ A) : Set where
  done : Value' A N → Finished N
  out-of-gas : Finished N

-- Encapsulates the steps taken
data Steps : ∀ {A} → Expr [] A → Set where
  steps : ∀ {A} {L N : Expr [] A}
          → L ⇨ N
          → Finished N
          → Steps L

-- Generation of evaluation
eval' : ∀ {A} → Gas → (L : Expr [] A) → Steps L
eval' (gas zero) L                    = steps (L ∎) out-of-gas
eval' (gas (suc m)) L with progress L
... | done VL                         = steps (L ∎) (done VL)
... | step {M} L↠M with eval' (gas m) M
...    | steps M⇨N fin               = steps (L ⇨⟨ L↠M ⟩ M⇨N) fin

----- Examples
import Relation.Binary.PropositionalEquality as Eq
open Eq using (_≡_; refl)

emptyenv : All Value []
emptyenv = []

-- Addition of two numbers defined as primitive recursion
_plus_ : ℕ → ℕ → Expr [] Nat
n plus m  = NatRec (natconv n) (S (Var here)) (natconv m)

-- _ : eval (3 plus 5) emptyenv ≡ 8
-- _ = refl

_ : eval' (gas 5) (1 plus 2) ≡ steps
  (NatRec (S Z) (S (Var here)) (S (S Z)) ⇨⟨ β-NatRec-S (VSuc VZero) ⟩
  (S (NatRec (S Z) (S (Var here)) (S Z)) ⇨⟨ ξ-Nat (β-NatRec-S VZero) ⟩
  (S (S (NatRec (S Z) (S (Var here)) Z)) ⇨⟨ ξ-Nat (ξ-Nat β-NatRec-Z) ⟩ S (S (S Z)) ∎)))
  (done (VSuc (VSuc (VSuc VZero)))) -- 3
_ = {!!}

-- Multiplication of two numbers defined as primitive recursion using addition
_times_ : ℕ → ℕ → Expr [] Nat
n times m = NatRec Z (NatRec (natconv n) (S (Var here)) (Var here)) (natconv m)

-- _ : eval (50 times 5) emptyenv ≡ 250
-- _ = refl

_ : eval' (gas 10) (2 times 2) ≡ steps
 (NatRec Z (NatRec (S (S Z)) (S (Var here)) (Var here)) (S (S Z)) ⇨⟨ β-NatRec-S (VSuc VZero) ⟩
 (NatRec (S (S Z)) (S (Var here))
  (NatRec Z (NatRec (S (S Z)) (S (Var here)) (Var here)) (S Z))  ⇨⟨ ξ-NatRec (β-NatRec-S VZero) ⟩
  (NatRec (S (S Z)) (S (Var here))
   (NatRec (S (S Z)) (S (Var here))
    (NatRec Z (NatRec (S (S Z)) (S (Var here)) (Var here)) Z)) ⇨⟨ ξ-NatRec (ξ-NatRec β-NatRec-Z) ⟩
   (NatRec (S (S Z)) (S (Var here))
    (NatRec (S (S Z)) (S (Var here)) Z) ⇨⟨ ξ-NatRec β-NatRec-Z ⟩
    (NatRec (S (S Z)) (S (Var here)) (S (S Z)) ⇨⟨ β-NatRec-S (VSuc VZero) ⟩
     (S (NatRec (S (S Z)) (S (Var here)) (S Z)) ⇨⟨ ξ-Nat (β-NatRec-S VZero) ⟩
      (S (S (NatRec (S (S Z)) (S (Var here)) Z)) ⇨⟨ ξ-Nat (ξ-Nat β-NatRec-Z) ⟩ S (S (S (S Z))) ∎)))))))
 (done (VSuc (VSuc (VSuc (VSuc VZero))))) -- 4
_ = {!!}


{-# OPTIONS --cubical --safe --postfix-projections #-}

module Data.Bool.Properties where

open import Prelude
open import Data.Bool
open import Data.Unit.Properties

T? : ∀ x → Dec (T x)
T? x .does = x
T? false .why = ofⁿ id
T? true  .why = ofʸ tt

isPropT : ∀ x → isProp (T x)
isPropT false = isProp⊥
isPropT true  = isProp⊤

discreteBool : Discrete Bool
discreteBool false y .does = not y
discreteBool true y .does = y
discreteBool false false .why = ofʸ refl
discreteBool false true .why = ofⁿ λ p → subst (bool ⊤ ⊥) p tt
discreteBool true false .why = ofⁿ λ p → subst (bool ⊥ ⊤) p tt
discreteBool true true .why = ofʸ refl


-- This code is closely based on code due to Andy Morris.

data _≡⁰_ {A : Set} (@0 x : A) : @0 A → Set where refl : x ≡⁰ x
data _≡ʷ_ {A : Set} (@ω x : A) : @ω A → Set where refl : x ≡ʷ x

works : ∀ {A} {@0 x y : A} → x ≡⁰ y → x ≡ʷ y
works refl = refl

also-works : ∀ {A} {@0 x y : A} → x ≡⁰ y → x ≡ʷ y
also-works {x = x} refl = refl {x = x}


module Numeral.Finite.LinearSearch where -- TODO: Maybe move to Numeral.CoordinateVector.LinearSearch

open import Data.Boolean
open import Data.Boolean.Stmt
open import Data.List
import      Data.List.Functions as List
open import Data.Option
import      Data.Option.Functions as Option
open import Functional
open import Logic
open import Numeral.Finite
open import Numeral.Finite.Bound
open import Numeral.Natural
open import Structure.Relator.Ordering

private variable n : ℕ
private variable i j min max : 𝕟(n)
private variable f : 𝕟(n) → Bool

-- Finds the maximal argument satisfying the given decidable predicate by searching linearly.
-- Examples:
--   findMax{5}   (10 ∣?_) = None
--   findMax{10}  (10 ∣?_) = None
--   findMax{20}  (10 ∣?_) = Some 10
--   findMax{21}  (10 ∣?_) = Some 20
--   findMax{22}  (10 ∣?_) = Some 20
--   findMax{100} (10 ∣?_) = Some 90
--   findMax{102} (10 ∣?_) = Some 100
-- Alternative implementation: findMax f = Option.map Wrapping.[−]_ (findMin(f ∘ Wrapping.[−]_))
findMax : (𝕟(n) → Bool) → Option(𝕟(n))
findMax {𝟎}    f = None
findMax {𝐒(n)} f with f(maximum)
findMax {𝐒(n)} f | 𝑇 = Some maximum
findMax {𝐒(n)} f | 𝐹 = Option.map bound-𝐒 (findMax{n} (f ∘ bound-𝐒))

-- Finds the minimal argument satisfying the given decidable predicate by searching linearly.
-- Examples:
--   findMin{5}   (10 ∣?_) = None
--   findMin{10}  (10 ∣?_) = None
--   findMin{20}  (10 ∣?_) = Some 10
--   findMin{21}  (10 ∣?_) = Some 10
--   findMin{22}  (10 ∣?_) = Some 10
--   findMin{100} (10 ∣?_) = Some 10
--   findMax{102} (10 ∣?_) = Some 10
findMin : (𝕟(n) → Bool) → Option(𝕟(n))
findMin{𝟎}    f = None
findMin{𝐒(n)} f with f(𝟎)
findMin{𝐒(n)} f | 𝑇 = Some 𝟎
findMin{𝐒(n)} f | 𝐹 = Option.map 𝐒 (findMin{n} (f ∘ 𝐒))

-- Finds all arguments satisfying the given decidable predicate by searching linearly.
-- Examples:
--   findAll{10} (2 ∣?_) = [0,2,4,6,8]
findAll : (𝕟(n) → Bool) → List(𝕟(n))
findAll{𝟎}    f = ∅
findAll{𝐒(n)} f = (if f(𝟎) then (𝟎 ⊰_) else id) (List.map 𝐒 (findAll{n} (f ∘ 𝐒)))

open import Data
open import Data.Boolean.Stmt.Proofs
open import Data.List.Relation.Membership using (_∈_)
open import Data.List.Relation.Membership.Proofs
open import Data.List.Relation.Pairwise
open import Data.List.Relation.Pairwise.Proofs
open import Data.List.Relation.Quantification
open import Data.List.Relation.Quantification.Proofs
open import Data.List.Sorting
open import Data.Option.Equiv.Id
open import Lang.Inspect
open import Logic.Propositional
open import Logic.Propositional.Theorems
open import Numeral.Finite.Oper.Comparisons
open import Numeral.Finite.Oper.Comparisons.Proofs
open import Numeral.Finite.Proofs
open import Numeral.Finite.Relation.Order as 𝕟 using (_≤_ ; _>_ ; _<_ ; [≤]-minimum ; [≤]-maximum)
import      Numeral.Natural.Relation.Order as ℕ
open import Numeral.Natural.Relation.Order.Proofs as ℕ
open import Relator.Equals
open import Relator.Equals.Proofs.Equiv
open import Structure.Function
open import Structure.Function.Domain
open import Structure.Relator.Properties
open import Structure.Relator
open import Type.Properties.Decidable
open import Type.Properties.Decidable.Proofs

{-
open import Syntax.Number
test : 𝕟(5) → Bool
test 𝟎 = 𝐹
test (𝐒 𝟎) = 𝐹
test (𝐒 (𝐒 𝟎)) = 𝑇
test (𝐒 (𝐒 (𝐒 𝟎))) = 𝐹
test (𝐒 (𝐒 (𝐒 (𝐒 𝟎)))) = 𝐹
test2 : 𝕟(1) → Bool
test2 𝟎 = 𝑇
test3 : findMax test2 ≡ Some 0
test3 = [≡]-intro
tst4 = {!Option.map bound-𝐒 (findMax (test ∘ bound-𝐒))!}
-}

findMax-None-correctness : (findMax f ≡ None) ↔ (∀{i} → IsFalse(f(i)))
findMax-None-correctness = [↔]-intro l r where
  l : (findMax f ≡ None) ← (∀{i} → IsFalse(f(i)))
  l {𝟎} {f} p = [≡]-intro
  l {𝐒 n} {f} p with f(maximum) | inspect f(maximum)
  ... | 𝑇 | intro fmax with () ← disjointness p ([↔]-to-[←] IsTrue.is-𝑇 fmax)
  ... | 𝐹 | intro fmax = congruence₁(Option.map bound-𝐒) (l p)

  r-result : IsFalse(f(maximum)) → (∀{i : 𝕟(n)} → IsFalse((f ∘ bound-𝐒) i)) → (∀{i : 𝕟(n)} → IsFalse(f(𝐒 i)))
  r-result {𝐒 n}     {f} p q {𝐒 i} = r-result {n}{f ∘ 𝐒} p (\{i} → q{𝐒 i}) {i}
  r-result {𝐒 𝟎}     {f} p q {𝟎}   = p
  r-result {𝐒 (𝐒 n)} {f} p q {𝟎}   = q{𝐒 𝟎}

  r : (findMax f ≡ None) → (∀{i} → IsFalse(f(i)))
  r {𝐒 n}     {f} p {i} with f(maximum) | inspect f(maximum)
  r {𝐒 n}     {f} p {𝐒 i} | 𝐹 | intro fmax = r-result {f = f} ([↔]-to-[←] IsFalse.is-𝐹 fmax) (\{i} → r {n}{f ∘ bound-𝐒} (map-None p) {i}) {i}
  r {𝐒 𝟎}     {f} p {𝟎}   | 𝐹 | intro fmax = [↔]-to-[←] IsFalse.is-𝐹 fmax
  r {𝐒 (𝐒 n)} {f} p {𝟎}   | 𝐹 | intro fmax = r {𝐒 n} {f ∘ bound-𝐒} (map-None p) {𝟎}

findMax-Some-correctness : (findMax f ≡ Some(i)) → IsTrue(f(i))
findMax-Some-correctness {𝐒 n} {f} {i}        eq with f(maximum) | inspect f(maximum)
findMax-Some-correctness {𝐒 n} {f} {.maximum} [≡]-intro | 𝑇 | intro fmax = [↔]-to-[←] IsTrue.is-𝑇 fmax
findMax-Some-correctness {𝐒 n} {f} {i}        eq        | 𝐹 | intro fmax with findMax(f ∘ bound-𝐒) | inspect findMax(f ∘ bound-𝐒)
findMax-Some-correctness {𝐒 n} {f} {.(_)}     [≡]-intro | 𝐹 | intro fmax | Some x | intro p = findMax-Some-correctness {f = f ∘ bound-𝐒} p

findMin-None-correctness : (findMin f ≡ None) ↔ (∀{i} → IsFalse(f(i)))
findMin-None-correctness = [↔]-intro l r where
  l : (findMin f ≡ None) ← (∀{i} → IsFalse(f(i)))
  l {𝟎} {f} p = [≡]-intro
  l {𝐒 n} {f} p with f(𝟎) | inspect f(𝟎)
  ... | 𝑇 | intro f0 with () ← disjointness p ([↔]-to-[←] IsTrue.is-𝑇 f0)
  ... | 𝐹 | intro f0 = congruence₁(Option.map 𝐒) (l p)

  r : (findMin f ≡ None) → (∀{i} → IsFalse(f(i)))
  r {𝐒 n} {f} p {i} with f(𝟎) | inspect f(𝟎)
  r {𝐒 n} {f} p {𝟎}   | 𝐹 | intro f0 = [↔]-to-[←] IsFalse.is-𝐹 f0
  r {𝐒 n} {f} p {𝐒 i} | 𝐹 | intro f0 = r {f = f ∘ 𝐒} (injective(Option.map 𝐒) ⦃ map-injectivity ⦄ p)

findMin-Some-correctness : (findMin f ≡ Some(min)) → IsTrue(f(min))
findMin-Some-correctness {𝐒 n} {f} {min} eq with f(𝟎) | inspect f(𝟎)
findMin-Some-correctness {𝐒 n} {f} {𝟎}     [≡]-intro | 𝑇 | intro f0 = [↔]-to-[←] IsTrue.is-𝑇 f0
findMin-Some-correctness {𝐒 n} {f} {𝟎}     eq        | 𝐹 | intro f0 with findMin{n} (f ∘ 𝐒)
findMin-Some-correctness {𝐒 n} {f} {𝟎}     ()        | 𝐹 | intro f0 | None
findMin-Some-correctness {𝐒 n} {f} {𝟎}     ()        | 𝐹 | intro f0 | Some _
findMin-Some-correctness {𝐒 n} {f} {𝐒 min} eq        | 𝐹 | intro f0 = findMin-Some-correctness {n} {f ∘ 𝐒} {min} (injective(Option.map 𝐒) ⦃ map-injectivity ⦄ eq)

findMin-minimal-true : (findMin f ≡ Some(min)) → IsTrue(f(i)) → (min ≤ i)
findMin-minimal-true {𝐒 n} {f} {min}   {i}   eq        p with f(𝟎) | inspect f(𝟎)
findMin-minimal-true {𝐒 n} {f} {.𝟎}    {i}   [≡]-intro p | 𝑇 | intro f0 = 𝕟.[≤]-minimum {a = i}
findMin-minimal-true {𝐒 n} {f} {𝟎}     {i}   eq        p | 𝐹 | intro f0 with findMin{n} (f ∘ 𝐒)
findMin-minimal-true {𝐒 n} {f} {𝟎}     {i}   ()        p | 𝐹 | intro f0 | None
findMin-minimal-true {𝐒 n} {f} {𝟎}     {i}   ()        p | 𝐹 | intro f0 | Some _
findMin-minimal-true {𝐒 n} {f} {𝐒 min} {𝟎}   eq        p | 𝐹 | intro f0 = disjointness p ([↔]-to-[←] IsFalse.is-𝐹 f0)
findMin-minimal-true {𝐒 n} {f} {𝐒 min} {𝐒 i} eq        p | 𝐹 | intro f0 = findMin-minimal-true {n} {f ∘ 𝐒} {min} {i} (injective(Option.map 𝐒) ⦃ map-injectivity ⦄ eq) p

findMin-minimal-false : (findMin f ≡ Some(min)) → (min > i) → IsFalse(f(i))
findMin-minimal-false {n}{f}{min}{i} eq =
  [↔]-to-[←] false-true-opposites
  ∘ contrapositiveᵣ(findMin-minimal-true{n}{f}{min}{i} eq)
  ∘ [↔]-to-[←] decider-true ∘ substitute₁ₗ(IsTrue) (⋚-elim₃-negation-distribution {x = min}{y = i})

{-
instance
  [≤]-with-[𝐒]-injective : ∀{a b} → Injective(\p → ℕ.[≤]-with-[𝐒] {a}{b} ⦃ p ⦄)
  Injective.proof [≤]-with-[𝐒]-injective [≡]-intro = [≡]-intro

import      Structure.Function.Names as Names
open import Function.Equals
bound-[≤]-injective : ∀{a b} → Injective ⦃ [≡]-equiv ⦄ ⦃ [⊜]-equiv ⦄ (bound-[≤] {a}{b})
bound-[≤]-injective {a}{b} = intro proof where
  proof : Names.Injective ⦃ [≡]-equiv ⦄ ⦃ [⊜]-equiv ⦄ (bound-[≤] {a}{b})
  proof {ℕ.[≤]-minimum} {ℕ.[≤]-minimum} p = [≡]-intro
  proof {ℕ.[≤]-with-[𝐒] ⦃ x ⦄} {ℕ.[≤]-with-[𝐒] ⦃ y ⦄} (intro p) = {!(injective ⦃ ? ⦄ ⦃ ? ⦄ (𝕟.𝐒) ⦃ ? ⦄ {x = bound-[≤] x}{y = bound-[≤] y} p)!}
--Injective.proof bound-[≤]-injective {ℕ.[≤]-minimum} {ℕ.[≤]-minimum} p = [≡]-intro
--Injective.proof bound-[≤]-injective {ℕ.[≤]-with-[𝐒] {y = _} ⦃ x ⦄} {ℕ.[≤]-with-[𝐒] {y = _} ⦃ y ⦄} (intro p) = {!injective(𝐒) ⦃ ? ⦄ p!}
-- injective(𝐒) ⦃ [≤]-with-[𝐒]-injective ⦄

bound-𝐒-injective : Injective(bound-𝐒 {n})
Injective.proof bound-𝐒-injective {𝟎} {𝟎} p = [≡]-intro
Injective.proof bound-𝐒-injective {𝐒 x} {𝐒 y} p = congruence₁(𝐒) (Injective.proof bound-𝐒-injective {x} {y} (injective(𝐒) p))
-}

-- TODO
postulate findMax-maximal-true : (findMax f ≡ Some(max)) → IsTrue(f(i)) → (i ≤ max)
{-findMax-maximal {𝐒 n}{f} eq p with f(maximum) | inspect f(maximum)
findMax-maximal {𝐒 n} {f} {i = i} [≡]-intro p | 𝑇 | intro m = [≤]-maximum {𝐒 n}{i} (reflexivity(ℕ._≤_))
findMax-maximal {𝐒 n} {f} {i = i} eq p | 𝐹 | intro m with findMax{n} (f ∘ bound-𝐒) | inspect (findMax{n}) (f ∘ bound-𝐒)
findMax-maximal {𝐒 n} {f} {i = i} eq p | 𝐹 | intro m | Some x | intro findMax-x = {!findMax-maximal {n} {f ∘ bound-𝐒} {i = ?} !}
-}

{- TODO
test : ∀{i : 𝕟(𝐒(n))} → ¬(maximum{n} < i)
test {𝟎}   {𝟎}   p = p
test {𝐒 n} {𝟎}   p = p
test {𝐒 n} {𝐒 i} p = test {n}{i} p

{-
findMax-maximal2 : (findMax f ≡ Some(max)) → (i > max) → IsFalse(f(i))
findMax-maximal2 {𝐒 n} {f} {max} {i} eq p with f(maximum) | inspect f(maximum)
findMax-maximal2 {𝐒 n} {f} {𝟎} {𝐒 i} eq p | 𝑇 | intro fmax with maximum-0(injective(Some) eq)
findMax-maximal2 {𝐒 .0} {f} {𝟎} {𝐒 ()} eq p | 𝑇 | intro fmax | [≡]-intro
findMax-maximal2 {𝐒 (𝐒 n)} {f} {𝐒 max} {𝐒 i} eq p | 𝑇 | intro fmax with f(bound-𝐒 maximum) | inspect (f ∘ bound-𝐒)(maximum)
findMax-maximal2 {𝐒 (𝐒 n)} {f} {𝐒 .maximum} {𝐒 i} [≡]-intro p | 𝑇 | intro fmax | 𝑇 | intro x with () ← test{n} p
findMax-maximal2 {𝐒 (𝐒 n)} {f} {𝐒 {.(𝐒 n)} .(maximum {n})} {𝐒 {.(𝐒 n)} i} ([≡]-intro {x = .(Some (𝐒 {𝐒 n} (maximum {n})))}) p | 𝑇 | intro fmax | 𝐹 | intro x with () ← test{n} p
findMax-maximal2 {𝐒 n} {f} {𝟎} {𝐒 i} eq p | 𝐹 | intro fmax = {!!}
findMax-maximal2 {𝐒 n} {f} {𝐒 max} {𝐒 i} eq p | 𝐹 | intro fmax = {!!}
-}

test2 : (bound-𝐒 i ≡ 𝟎) → (i ≡ 𝟎)
test2 {i = 𝟎} p = [≡]-intro

findMax-maximal : (findMax f ≡ Some(max)) → IsTrue(f(i)) → (i ≤ max)
findMax-maximal {𝐒 n}{f} eq p with f(maximum) | inspect f(maximum)
findMax-maximal {𝐒 n} {f} {_}{i} [≡]-intro p | 𝑇 | intro fmax = [≤]-maximum {𝐒 n}{i} (reflexivity(ℕ._≤_))
findMax-maximal {𝐒 n} {f} {max} {𝟎} eq p | 𝐹 | intro fmax = [≤]-minimum {a = max}
findMax-maximal {𝐒 (𝐒 n)} {f} {max} {𝐒 i} eq p | 𝐹 | intro fmax with f(bound-𝐒 maximum) | inspect (f ∘ bound-𝐒)(maximum)
findMax-maximal {𝐒 (𝐒 n)} {f} {𝟎} {𝐒 i} eq p | 𝐹 | intro fmax | 𝑇 | intro x = {!maximum-0(test2(injective(Some) eq))!}
findMax-maximal {𝐒 (𝐒 n)} {f} {𝐒 max} {𝐒 i} eq p | 𝐹 | intro fmax | 𝑇 | intro x = {!!}
... | 𝐹 | intro x = {!!}
-- findMax-maximal {f = f ∘ bound-𝐒} {i = i} (injective(Option.map bound-𝐒) ⦃ map-injectivity ⦃ ? ⦄ ⦄ {findMax(f ∘ bound-𝐒)} {Some 𝟎} eq)
{-findMax-maximal {𝐒 n} {f} {max} {i} eq p with f(maximum) | inspect f(maximum)
findMax-maximal {𝐒 n} {f} {.maximum} {i} [≡]-intro p | 𝑇 | intro fmax = [≤]-maximum {𝐒 n}{i}{n = n} (reflexivity(ℕ._≤_))
findMax-maximal {𝐒 n} {f} {max} {i} eq p | 𝐹 | intro fmax with findMax{n} (f ∘ bound-𝐒)
findMax-maximal {𝐒 n} {f} {max} {i} () p | 𝐹 | intro fmax | None
-}
{-findMax-maximal {𝐒 n} {f} {𝟎}     {𝟎}   eq p | 𝐹 | intro fmax | Some x = <>
findMax-maximal {𝐒 n} {f} {𝐒 max} {𝟎}   eq p | 𝐹 | intro fmax | Some x = <>
findMax-maximal {𝐒(𝐒 n)} {f} {𝟎} {𝐒 i} eq p | 𝐹 | intro fmax | Some 𝟎 = {!findMax-maximal!}
findMax-maximal {𝐒 n} {f} {𝐒 max} {𝐒 i} eq p | 𝐹 | intro fmax | Some x = {!!}
-}
{-findMax-maximal {𝐒 n} {f} {.(bound-[≤] ([≤]-successor ([≡]-to-[≤] [≡]-intro)) x)} {𝟎} [≡]-intro p | 𝐹 | intro fmax | Some x = [≤]-minimum {a = bound-𝐒(x)}
findMax-maximal {𝐒 .(𝐒 _)} {f} {.(bound-[≤] ([≤]-successor ([≡]-to-[≤] [≡]-intro)) 𝟎)} {𝐒 i} [≡]-intro p | 𝐹 | intro fmax | Some 𝟎 = {!!}
findMax-maximal {𝐒 .(𝐒 _)} {f} {.(bound-[≤] ([≤]-successor ([≡]-to-[≤] [≡]-intro)) (𝐒 x))} {𝐒 i} [≡]-intro p | 𝐹 | intro fmax | Some (𝐒 x) = {!!}-}
-}

{-
open import Numeral.Finite.Oper
findMax-findMin : findMax f ≡ Option.map Wrapping.[−]_ (findMin(f ∘ Wrapping.[−]_))
findMax-findMin {𝟎} {f} = [≡]-intro
findMax-findMin {𝐒 n} {f} with f(maximum) | inspect f(maximum)
... | 𝑇 | intro fmax = [≡]-intro
... | 𝐹 | intro fmax = {!!}
-}

findAll-correctness : AllElements(IsTrue ∘ f)(findAll f)
findAll-correctness {𝟎}   {f} = ∅
findAll-correctness {𝐒 n} {f} with f(𝟎) | inspect f(𝟎)
... | 𝑇 | intro f0 = ([↔]-to-[←] IsTrue.is-𝑇 f0) ⊰ (AllElements-mapᵣ 𝐒 id (findAll-correctness {n}{f ∘ 𝐒}))
... | 𝐹 | intro _  = AllElements-mapᵣ 𝐒 id (findAll-correctness {n}{f ∘ 𝐒})

findAll-completeness : IsTrue(f(i)) → (i ∈ findAll f)
findAll-completeness {𝐒 n} {f} {i}   p with f(𝟎) | inspect f(𝟎)
findAll-completeness {𝐒 n} {f} {𝟎}   p | 𝑇 | intro _  = • [≡]-intro
findAll-completeness {𝐒 n} {f} {𝐒 i} p | 𝑇 | intro _  = ⊰ [∈]-map (findAll-completeness{n}{f ∘ 𝐒}{i} p)
findAll-completeness {𝐒 n} {f} {𝟎}   p | 𝐹 | intro f0 with () ← disjointness p ([↔]-to-[←] IsFalse.is-𝐹 f0)
findAll-completeness {𝐒 n} {f} {𝐒 i} p | 𝐹 | intro _  = [∈]-map (findAll-completeness {n} {f ∘ 𝐒} {i} p)

findAll-sorted : Sorted(_≤?_)(findAll f)
findAll-sorted {𝟎}      {f} = AdjacentlyPairwise.empty
findAll-sorted {𝐒 𝟎} {f} with f(𝟎) | inspect f(𝟎)
findAll-sorted {𝐒 𝟎} {f} | 𝑇 | intro f0 = AdjacentlyPairwise.single
findAll-sorted {𝐒 𝟎} {f} | 𝐹 | intro f0 = AdjacentlyPairwise.empty
findAll-sorted {𝐒(𝐒 n)} {f} with f(𝟎) | f(𝐒 𝟎) | AdjacentlyPairwise-map {_▫₁_ = IsTrue ∘₂ _≤?_}{f = 𝐒}{_▫₂_ = IsTrue ∘₂ _≤?_} id (findAll-sorted {𝐒 n}{f ∘ 𝐒})
... | 𝑇 | 𝑇 | prev = AdjacentlyPairwise.step ⦃ <> ⦄ ⦃ prev ⦄
... | 𝑇 | 𝐹 | prev = AdjacentlyPairwise-prepend (\{ {𝟎} → <> ; {𝐒 _} → <>}) prev
... | 𝐹 | 𝑇 | prev = prev
... | 𝐹 | 𝐹 | prev = prev


{-# OPTIONS --without-K --rewriting #-}

open import lib.Basics
open import lib.types.Paths
open import lib.types.Pointed
open import lib.types.Pushout
open import lib.types.PushoutFmap
open import lib.types.Sigma
open import lib.types.Span

module lib.types.Join  where

module _ {i j} (A : Type i) (B : Type j) where

  *-span : Span
  *-span = span A B (A × B) fst snd

  infix 80 _*_

  _*_ : Type _
  _*_ = Pushout *-span

module JoinElim {i j} {A : Type i} {B : Type j} {k} {P : A * B → Type k}
  (left* : (a : A) → P (left a)) (right* : (b : B) → P (right b))
  (glue* : (ab : A × B) → left* (fst ab) == right* (snd ab) [ P ↓ glue ab ])
  = PushoutElim left* right* glue*
open JoinElim public using () renaming (f to Join-elim)

module JoinRec {i j} {A : Type i} {B : Type j} {k} {D : Type k}
  (left* : (a : A) → D) (right* : (b : B) → D)
  (glue* : (ab : A × B) → left* (fst ab) == right* (snd ab))
  = PushoutRec left* right* glue*
open JoinRec public using () renaming (f to Join-rec)

module _ {i j} (X : Ptd i) (Y : Ptd j) where

  ⊙*-span : ⊙Span
  ⊙*-span = ⊙span X Y (X ⊙× Y) ⊙fst ⊙snd

  infix 80 _⊙*_

  _⊙*_ : Ptd _
  _⊙*_ = ⊙Pushout ⊙*-span

module _ {i i' j j'} {A : Type i} {A' : Type i'} {B : Type j} {B' : Type j'}
  (eqA : A ≃ A') (eqB : B ≃ B') where

  *-span-emap : SpanEquiv (*-span A B) (*-span A' B')
  *-span-emap = ( span-map (fst eqA) (fst eqB) (×-fmap (fst eqA) (fst eqB)) (comm-sqr λ _ → idp) (comm-sqr λ _ → idp)
                , snd eqA , snd eqB , ×-isemap (snd eqA) (snd eqB))

  *-emap : A * B ≃ A' * B'
  *-emap = Pushout-emap *-span-emap

module _ {i i' j j'} {X : Ptd i} {X' : Ptd i'} {Y : Ptd j} {Y' : Ptd j'} where

  ⊙*-emap : X ⊙≃ X' → Y ⊙≃ Y' → X ⊙* Y ⊙≃ X' ⊙* Y'
  ⊙*-emap ⊙eqX ⊙eqY = ≃-to-⊙≃ (*-emap (⊙≃-to-≃ ⊙eqX) (⊙≃-to-≃ ⊙eqY)) (ap left (snd (⊙–> ⊙eqX)))


------------------------------------------------------------------------
-- The Agda standard library
--
-- Equality over lists parameterised by some setoid
------------------------------------------------------------------------

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

open import Relation.Binary using (Setoid)

module Data.List.Relation.Binary.Equality.Setoid {a ℓ} (S : Setoid a ℓ) where

open import Data.List.Base using (List)
open import Level
open import Relation.Binary renaming (Rel to Rel₂)
open import Relation.Binary.PropositionalEquality as P using (_≡_)
open import Data.List.Relation.Binary.Pointwise as PW using (Pointwise)

open Setoid S renaming (Carrier to A)

------------------------------------------------------------------------
-- Definition of equality

infix 4 _≋_

_≋_ : Rel₂ (List A) (a ⊔ ℓ)
_≋_ = Pointwise _≈_

open Pointwise public using ([]; _∷_)

------------------------------------------------------------------------
-- Relational properties

≋-refl : Reflexive _≋_
≋-refl = PW.refl refl

≋-reflexive : _≡_ ⇒ _≋_
≋-reflexive P.refl = ≋-refl

≋-sym : Symmetric _≋_
≋-sym = PW.symmetric sym

≋-trans : Transitive _≋_
≋-trans = PW.transitive trans

≋-isEquivalence : IsEquivalence _≋_
≋-isEquivalence = PW.isEquivalence isEquivalence

≋-setoid : Setoid _ _
≋-setoid = PW.setoid S

------------------------------------------------------------------------
-- Operations

open PW public using
  ( tabulate⁺
  ; tabulate⁻
  ; ++⁺
  ; concat⁺
  )


------------------------------------------------------------------------
-- Yet another implementation of the Fibonacci sequence using tail
------------------------------------------------------------------------

module LargeCombinators where

open import Codata.Musical.Notation
open import Codata.Musical.Stream as S using (Stream; _∷_; _≈_)
open import Data.Nat
import Relation.Binary.PropositionalEquality as P

-- Stream programs. Note that the destructor tail is encapsulated in a
-- larger combinator, which also incorporates a constructor. This
-- ensures that the combinator can be used freely, with no risk of
-- destroying productivity.
--
-- Note that, in the general case, the implementation of whnf for the
-- "large combinator" may be quite tricky. In this case the
-- implementation turns out to be very simple, though.
--
-- The idea to use a "large combinator" is due to Thorsten Altenkirch.

infixr 5 _∷_

data StreamP (A : Set) : Set where
  _∷_     : (x : A) (xs : ∞ (StreamP A)) → StreamP A
  zipWith : (f : A → A → A) (xs ys : StreamP A) → StreamP A

  -- The intention is that ⟦ x ∷zipWith f · xs [tail ys ] ⟧P should be
  -- equal to x ∷ ♯ S.zipWith f ⟦ xs ⟧P (S.tail ⟦ ys ⟧P).

  _∷zipWith_·_[tail_] :
    (x : A) (f : A → A → A) (xs ys : StreamP A) → StreamP A

-- WHNFs.

data StreamW (A : Set) : Set where
  _∷_ : (x : A) (xs : StreamP A) → StreamW A

-- Stream programs can be turned into streams.

whnf : ∀ {A} → StreamP A → StreamW A
whnf (x ∷ xs) = x ∷ ♭ xs
whnf (x ∷zipWith f · xs′ [tail ys ]) with whnf ys
... | _ ∷ ys′ = x ∷ zipWith f xs′ ys′
whnf (zipWith f xs ys) with whnf xs | whnf ys
... | x ∷ xs′ | y ∷ ys′ = f x y ∷ zipWith f xs′ ys′

mutual

  ⟦_⟧W : ∀ {A} → StreamW A → Stream A
  ⟦ x ∷ xs ⟧W = x ∷ ♯ ⟦ xs ⟧P

  ⟦_⟧P : ∀ {A} → StreamP A → Stream A
  ⟦ xs ⟧P = ⟦ whnf xs ⟧W

-- The Fibonacci sequence.

fib : StreamP ℕ
fib = 0 ∷ ♯ (1 ∷zipWith _+_ · fib [tail fib ])

-- ⟦_⟧P is homomorphic with respect to zipWith/S.zipWith.

zipWith-hom :
  ∀ {A} (f : A → A → A) (xs ys : StreamP A) →
  ⟦ zipWith f xs ys ⟧P ≈ S.zipWith f ⟦ xs ⟧P ⟦ ys ⟧P
zipWith-hom f xs ys with whnf xs | whnf ys
... | x ∷ xs′ | y ∷ ys′ = P.refl ∷ ♯ zipWith-hom f xs′ ys′

-- The stream ⟦ fib ⟧P satisfies its intended defining equation.

fib-correct :
  ⟦ fib ⟧P ≈ 0 ∷ ♯ (1 ∷ ♯ (S.zipWith _+_ ⟦ fib ⟧P (S.tail ⟦ fib ⟧P)))
fib-correct =
  P.refl ∷ ♯ (P.refl ∷ ♯
    zipWith-hom _+_ fib (1 ∷zipWith _+_ · fib [tail fib ]))

-- For completeness, let us show that _∷zipWith_·_[tail_] is correctly
-- implemented.

open import Relation.Binary.PropositionalEquality as P using (_≡_; [_])

_∷zipWith_·_[tail_]-hom :
  ∀ {A} (x : A) (f : A → A → A) (xs ys : StreamP A) →
  ⟦ x ∷zipWith f · xs [tail ys ] ⟧P ≈
  x ∷ ♯ S.zipWith f ⟦ xs ⟧P (S.tail ⟦ ys ⟧P)
x ∷zipWith f · xs [tail ys ]-hom with whnf ys | P.inspect whnf ys
... | y ∷ ys′ | [ eq ] = P.refl ∷ ♯ helper eq
  where
  helper : whnf ys ≡ y ∷ ys′ →
           ⟦ zipWith f xs ys′ ⟧P ≈
           S.zipWith f ⟦ xs ⟧P (S.tail ⟦ ys ⟧P)
  helper eq rewrite eq = zipWith-hom f xs ys′


module WellTypedTerms where

open import Library
open import Categories
open import Functors
open import RMonads
open import FunctorCat
open import Categories.Sets
open import Categories.Families

data Ty : Set where
  ι   : Ty
  _⇒_ : Ty → Ty → Ty

data Con : Set where
  ε   : Con
  _<_ : Con → Ty → Con

data Var : Con → Ty → Set where
  vz : ∀{Γ σ} → Var (Γ < σ) σ
  vs : ∀{Γ σ τ} → Var Γ σ → Var (Γ < τ) σ

data Tm : Con → Ty → Set where
  var : ∀{Γ σ} → Var Γ σ → Tm Γ σ
  app : ∀{Γ σ τ} → Tm Γ (σ ⇒ τ) → Tm Γ σ → Tm Γ τ
  lam : ∀{Γ σ τ} → Tm (Γ < σ) τ → Tm Γ (σ ⇒ τ)

Ren : Con → Con → Set
Ren Γ Δ = ∀ {σ} → Var Γ σ → Var Δ σ

renId : ∀{Γ} → Ren Γ Γ
renId = id

renComp : ∀{B Γ Δ} → Ren Γ Δ → Ren B Γ → Ren B Δ
renComp f g = f ∘ g

ConCat : Cat
ConCat = record{
  Obj  = Con;
  Hom  = Ren;
  iden = renId;
  comp = renComp;
  idl  = iext λ _ → refl;
  idr  = iext λ _ → refl;
  ass  = iext λ _ → refl}

wk : ∀{Γ Δ σ} → Ren Γ Δ → Ren (Γ < σ) (Δ < σ)
wk f vz     = vz
wk f (vs i) = vs (f i)

ren : ∀{Γ Δ} → Ren Γ Δ → ∀ {σ} → Tm Γ σ → Tm Δ σ
ren f (var x)   = var (f x)
ren f (app t u) = app (ren f t) (ren f u)
ren f (lam t)   = lam (ren (wk f) t)

wkid : ∀{Γ σ τ}(x : Var (Γ < τ) σ) → wk renId x ≅ renId x
wkid vz     = refl
wkid (vs x) = refl

renid : ∀{Γ σ}(t : Tm Γ σ) → ren renId t ≅ id t
renid (var x)   = refl
renid (app t u) =
  proof
  app (ren renId t) (ren renId u)
  ≅⟨ cong₂ app (renid t) (renid u) ⟩
  app t u
  ∎
renid (lam t)   =
  proof lam (ren (wk renId) t)
  ≅⟨ cong (λ (f : Ren _ _) → lam (ren f t)) (iext λ _ → ext wkid) ⟩
  lam (ren renId t)
  ≅⟨ cong lam (renid t) ⟩
  lam t
  ∎

wkcomp : ∀ {B Γ Δ}(f : Ren Γ Δ)(g : Ren B Γ){σ τ}(x : Var (B < σ) τ) →
            wk (renComp f g) x ≅ renComp (wk f) (wk g) x
wkcomp f g vz     = refl
wkcomp f g (vs i) = refl

rencomp : ∀ {B Γ Δ}(f : Ren Γ Δ)(g : Ren B Γ){σ}(t : Tm B σ) →
            ren (renComp f g) t ≅ (ren f ∘ ren g) t
rencomp f g (var x)   = refl
rencomp f g (app t u) =
  proof
  app (ren (renComp f g) t) (ren (renComp f g) u)
  ≅⟨ cong₂ app (rencomp f g t) (rencomp f g u) ⟩
  app (ren f (ren g t)) (ren f (ren g u))
  ∎
rencomp f g (lam t)   =
  proof
  lam (ren (wk (renComp f g)) t)
  ≅⟨ cong (λ (f : Ren _ _) → lam (ren f t)) (iext λ _ → ext (wkcomp f g)) ⟩
  lam (ren (renComp (wk f) (wk g)) t)
  ≅⟨ cong lam (rencomp (wk f) (wk g) t) ⟩
  lam (ren (wk f) (ren (wk g) t))
  ∎

Sub : Con → Con → Set
Sub Γ Δ = ∀{σ} → Var Γ σ → Tm Δ σ

lift : ∀{Γ Δ σ} → Sub Γ Δ → Sub (Γ < σ) (Δ < σ)
lift f vz     = var vz
lift f (vs x) = ren vs (f x)

sub : ∀{Γ Δ} → Sub Γ Δ → ∀{σ} → Tm Γ σ → Tm Δ σ
sub f (var x)   = f x
sub f (app t u) = app (sub f t) (sub f u)
sub f (lam t)   = lam (sub (lift f) t)

subId : ∀{Γ} → Sub Γ Γ
subId = var

subComp : ∀{B Γ Δ} → Sub Γ Δ → Sub B Γ → Sub B Δ
subComp f g = sub f ∘ g

liftid : ∀{Γ σ τ}(x : Var (Γ < σ) τ) → lift subId x ≅ subId x
liftid vz     = refl
liftid (vs x) = refl

subid : ∀{Γ σ}(t : Tm Γ σ) → sub subId t ≅ id t
subid (var x)   = refl
subid (app t u) =
  proof
  app (sub subId t) (sub subId u)
  ≅⟨ cong₂ app (subid t) (subid u) ⟩
  app t u
  ∎
subid (lam t)   =
  proof
  lam (sub (lift subId) t)
  ≅⟨ cong (λ (f : Sub _ _) → lam (sub f t)) (iext λ _ → ext liftid) ⟩
  lam (sub subId t)
  ≅⟨ cong lam (subid t) ⟩
  lam t
  ∎

-- time for the mysterious four lemmas:
liftwk : ∀{B Γ Δ}(f : Sub Γ Δ)(g : Ren B Γ){σ τ}(x : Var (B < σ) τ) →
            (lift f ∘ wk g) x ≅ lift (f ∘ g) x
liftwk f g vz     = refl
liftwk f g (vs x) = refl

subren : ∀{B Γ Δ}(f : Sub Γ Δ)(g : Ren B Γ){σ}(t : Tm B σ) →
         (sub f ∘ ren g) t ≅ sub (f ∘ g) t
subren f g (var x)   = refl
subren f g (app t u) =
  proof
  app (sub f (ren g t)) (sub f (ren g u))
  ≅⟨ cong₂ app (subren f g t) (subren f g u) ⟩
  app (sub (f ∘ g) t) (sub (f ∘ g) u)
  ∎
subren f g (lam t)   =
  proof
  lam (sub (lift f) (ren (wk g) t))
  ≅⟨ cong lam (subren (lift f) (wk g) t) ⟩
  lam (sub (lift f ∘ wk g) t)
  ≅⟨ cong (λ (f : Sub _ _) → lam (sub f t)) (iext (λ _ → ext (liftwk f g))) ⟩
  lam (sub (lift (f ∘ g)) t) ∎

renwklift : ∀{B Γ Δ}(f : Ren Γ Δ)(g : Sub B Γ){σ τ}(x : Var (B < σ) τ) →
               (ren (wk f) ∘ lift g) x ≅ lift (ren f ∘ g) x
renwklift f g vz     = refl
renwklift f g (vs x) =
  proof
  ren (wk f) (ren vs (g x))
  ≅⟨ sym (rencomp (wk f) vs (g x)) ⟩
  ren (wk f ∘ vs) (g x)
  ≅⟨ rencomp vs f (g x) ⟩
  ren vs (ren f (g x))
  ∎

rensub : ∀{B Γ Δ}(f : Ren Γ Δ)(g : Sub B Γ){σ}(t : Tm B σ) →
         (ren f ∘ sub g) t ≅ sub (ren f ∘ g) t
rensub f g (var x)   = refl
rensub f g (app t u) =
  proof
  app (ren f (sub g t)) (ren f (sub g u))
  ≅⟨ cong₂ app (rensub f g t) (rensub f g u) ⟩
  app (sub (ren f ∘ g) t) (sub (ren f ∘ g) u)
  ∎
rensub f g (lam t)   =
  proof
  lam (ren (wk f) (sub (lift g) t))
  ≅⟨ cong lam (rensub (wk f) (lift g) t) ⟩
  lam (sub (ren (wk f) ∘ lift g) t)
  ≅⟨ cong (λ (f₁ : Sub _ _) → lam (sub f₁ t))
          (iext (λ _ → ext (renwklift f g))) ⟩
  lam (sub (lift (ren f ∘ g)) t)
  ∎

liftcomp : ∀{B Γ Δ}(f : Sub Γ Δ)(g : Sub B Γ){σ τ}(x : Var (B < σ) τ) →
           lift (subComp f g) x ≅ subComp (lift f) (lift g) x
liftcomp f g vz     = refl
liftcomp f g (vs x) =
  proof
  ren vs (sub f (g x))
  ≅⟨ rensub vs f (g x) ⟩
  sub (ren vs ∘ f) (g x)
  ≅⟨ sym (subren (lift f) vs (g x)) ⟩
  sub (lift f) (ren vs (g x))
  ∎

subcomp : ∀{B Γ Δ}(f : Sub Γ Δ)(g : Sub B Γ){σ}(t : Tm B σ) →
          sub (subComp f g) t ≅ (sub f ∘ sub g) t
subcomp f g (var x)   = refl
subcomp f g (app t u) =
  proof
  app (sub (subComp f g) t) (sub (subComp f g) u)
  ≅⟨ cong₂ app (subcomp f g t) (subcomp f g u) ⟩
  app (sub f (sub g t)) (sub f (sub g u))
  ∎
subcomp f g (lam t)   =
  proof
  lam (sub (lift (subComp f g)) t)
  ≅⟨ cong (λ (f : Sub _ _) → lam (sub f t))
          (iext λ _ → ext (liftcomp f g)) ⟩
  lam (sub (subComp (lift f) (lift g)) t)
  ≅⟨ cong lam (subcomp (lift f) (lift g) t) ⟩
  lam (sub (lift f) (sub (lift g) t)) ∎

VarF : Fun ConCat (Fam Ty)
VarF = record {
  OMap  = Var;
  HMap  = id;
  fid   = refl;
  fcomp = refl }

TmRMonad : RMonad VarF
TmRMonad = record {
  T    = Tm;
  η    = var;
  bind = sub;
  law1 = iext λ _ → ext subid ;
  law2 = refl;
  law3 = λ{_ _ _ f g} → iext λ σ → ext (subcomp g f)}

-- not needed here
sub<< : ∀{Γ Δ σ}(f : Sub Γ Δ)(t : Tm Δ σ) → Sub (Γ < σ) Δ
sub<< f t vz     = t
sub<< f t (vs x) = f x

lem1 : ∀{B Γ Δ σ}{f : Sub Γ Δ}{g : Ren B Γ}{t : Tm Δ σ}{τ}(x : Var (B < σ) τ) →
        (sub<< f t ∘ wk g) x ≅ (sub<< (f ∘ g) t) x
lem1 vz     = refl
lem1 (vs x) = refl

lem2 : ∀{B Γ Δ σ}{f : Sub Γ Δ}{g : Sub B Γ}{t : Tm Δ σ}{τ}(x : Var (B < σ) τ) →
       (subComp (sub<< f t) (lift g)) x ≅ (sub<< (subComp f g) t) x
lem2 vz     = refl
lem2 {f = f}{g = g}{t = t} (vs x) = subren (sub<< f t) vs (g x)


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

module sets.int.properties where

open import equality
open import function.core
import sets.nat as N
open N using (ℕ; suc)
open import sets.int.definition
open import sets.int.utils
import sets.int.core as Z

private
  module _ where
    open N

    add-right-unit : ∀ n n' → (n + 0) [-] (n' + 0) ≡ n [-] n'
    add-right-unit n n' = ap₂ _[-]_ (+-right-unit n) (+-right-unit n')

    add-left-inverse : ∀ n n' → (n' + n) [-] (n + n') ≡ 0 [-] 0
    add-left-inverse n n' = sym
      $ eq-ℤ 0 0 (n' + n)
      · ap₂ _[-]_ (+-right-unit (n' + n))
                  (+-right-unit (n' + n) · +-commutativity n' n)

module _ where
  open Z

  +-left-unit : ∀ n → zero + n ≡ n
  +-left-unit = elim-prop-ℤ (λ n → hℤ (zero + n) n)
                            (λ n n' → refl)

  +-right-unit : ∀ n → n + zero ≡ n
  +-right-unit = elim-prop-ℤ (λ n → hℤ (n + zero) n)
                             add-right-unit

  +-left-inverse : ∀ n → negate n + n ≡ zero
  +-left-inverse = elim-prop-ℤ (λ n → hℤ (negate n + n) zero)
                               add-left-inverse

  +-right-inverse : ∀ n → n + negate n ≡ zero
  +-right-inverse = elim-prop-ℤ (λ n → hℤ (n + negate n) zero)
                                (λ n n' → add-left-inverse n' n)

  inc-dec : ∀ n → inc (dec n) ≡ n
  inc-dec = elim-prop-ℤ (λ n → hℤ (inc (dec n)) n)
                        (λ n n' → sym (eq-ℤ n n' 1))

  dec-inc : ∀ n → dec (inc n) ≡ n
  dec-inc = elim-prop-ℤ (λ n → hℤ (dec (inc n)) n)
                        (λ n n' → sym (eq-ℤ n n' 1))

  +-associativity : ∀ n m p → n + m + p ≡ n + (m + p)
  +-associativity = elim₃-prop-ℤ
    (λ n m p → hℤ (n + m + p) (n + (m + p)))
    (λ n n' m m' p p' → ap₂ _[-]_ (N.+-associativity n m p)
                                  (N.+-associativity n' m' p'))


{-# OPTIONS --without-K #-}
module distinct where

open import Type
open import Type.Identities
open import Algebra.FunctionProperties.Eq
              renaming (Injective to is-injective)
open import Function.NP
open import Function.Extensionality
open import Data.Fin.NP using (Fin; Fin▹ℕ; _==_)
open import Data.Vec.NP
open import Data.Vec.Properties
open import Data.Product renaming (proj₁ to fst; proj₂ to snd) hiding (map)
open import Data.Zero
open import Data.One
open import Data.Two hiding (_==_)
open import Data.Sum hiding (map)
open import Data.Nat.NP hiding (_==_)
open import Data.Nat.Properties
import Data.List as L
import Data.List.Properties as LP
open L using (List; []; _∷_)
open import Relation.Binary.PropositionalEquality.NP
open import HoTT
open Equivalences
--open import Explore.Fin

is-distinct : {A : Set}{n : ℕ} → Vec A n → Set
is-distinct {n = n} v = is-injective (_‼_ v)
-- is-distinct {n = n} v = {p q : Fin n}(e : v ‼ p ≡ v ‼ q) → p ≡ q

Distinct : (A : Set)(n : ℕ) → Set
Distinct A n = Σ (Vec A n) is-distinct

Injection : (A B : Set) → Set
Injection A B = Σ (A → B) is-injective

Auto : (A : Set) → Set
Auto A = Injection A A

Perm : (n : ℕ) → Set
Perm n = Distinct (Fin n) n

module _ {n} {{_ : FunExt}} where
    Perm→Auto : Perm n → Auto (Fin n)
    Perm→Auto (v , v-dist) = _‼_ v , v-dist

    tabulate-dist : (f : Fin n → Fin n) (f-inj : Injective f) → is-distinct
    tabulate-dist f f-inj e = f-inj (! lookup∘tabulate f _ ∙ e ∙ lookup∘tabulate f _)

    Auto→Perm : Auto (Fin n) → Perm n
    Auto→Perm (f , f-inj)
      = tabulate f , λ e → f-inj (! lookup∘tabulate f _ ∙ e ∙ lookup∘tabulate f _)

Goal: tr is-injective (λ= (lookup∘tabulate f))
      (snd (Auto→Perm (f , f-inj)))
      ≡ f-inj
    Perm→Auto→Perm : ∀ a → Perm→Auto (Auto→Perm a) ≡ a
    Perm→Auto→Perm (f , f-inj) = pair= (λ= (lookup∘tabulate f)) {!!}

    Auto→Perm→Auto : ∀ π → Auto→Perm (Perm→Auto π) ≡ π
    Auto→Perm→Auto (v , v-dist) = pair= (tabulate∘lookup v) {!!}

    Perm≃Auto : Perm n ≃ Auto (Fin n)
    Perm≃Auto = equiv Perm→Auto Auto→Perm Perm→Auto→Perm Auto→Perm→Auto

Arr : (n : ℕ) → Set
Arr n = Vec (Fin n) n

Sum : Set → Set
Sum A = (A → ℕ) → ℕ
Prod = Sum

postulate
    sumFin : (n : ℕ) → Sum (Fin n)
    prodFin : (n : ℕ) → Prod (Fin n)
    -- sumVec : (A : Set)(n : ℕ) (f : Vec A n → ℕ) → ℕ
    sumArr : (n : ℕ) → Sum (Arr n)
    prodFinEq : {n : ℕ}(x y : Fin n) → Prod (x ≡ y)

distinctℕ : (n : ℕ) → (sumArr  n λ v →
                        prodFin n λ p →
                        prodFin n λ q →
                        prodFinEq p q λ e →
                        𝟚▹ℕ (p == q)) ≡ {!!}
distinctℕ = {!!}

{-
ℕ< : ℕ → Set
ℕ< n = Σ ℕ λ x → x < n

sum< : (n : ℕ) (f : ℕ< n → ℕ) → ℕ
sum< n f = {!!}

prod< : (n : ℕ) (f : ℕ< n → ℕ) → ℕ
prod< n f = {!!}

{-
foo : ∀ n a x → sumFin n λ i → a i * x ^ i
foo = ?

bar : ∀ n a x → sumFin n λ i → a i * x ^ i
bar = ?
-}

baz : ∀ n (u : ℕ< n → ℕ) → (sum< n λ { (i , p) → prod< i (λ { (j , q) → u (j , <-trans q p) }) }) ≡ {!!}
baz = {!!}

module _ n (u : ℕ< n → Set) {{_ : UA}} {{_ : FunExt}} where
    open ≡-Reasoning
    Baz : _ ≡ _
    Baz = (Σ (ℕ< n) λ { (i , p) → Π (ℕ< i) λ { (j , q) → u (j , <-trans q p) } })
        ≡⟨ ! Σ-assoc ⟩
          (Σ ℕ λ i → Σ (i < n) λ p → Π (ℕ< i) λ { (j , q) → u (j , <-trans q p) })
        ≡⟨ Σ=′ ℕ (λ i → Σ=′ (i < n) λ p → ΠΣ-curry) ⟩
          (Σ ℕ λ i → Σ (i < n) λ p → Π ℕ λ j → Π (j < i) λ q → u (j , <-trans q p))
        ∎

module DataVersion (A : ★) where
    open import Data.Tree.Binary
    data T : BinTree A → ★ where
      empty : T empty
      _⊕_ : ∀ {t u} → (𝟙 ⊎ T t × T u) → T (fork t u)

module TypeVersion where
    ε = 𝟙
    _⊕_ : ★ → ★ → ★
    _⊕_ = λ u z → ε ⊎ u × z

module ListVersion where
    open L
    open ≡-Reasoning
    map-∘ = LP.map-compose

    sum-lin : ∀ k xs → sum (map (_*_ k) xs) ≡ k * sum xs
    sum-lin k []       = ℕ°.*-comm 0 k
    sum-lin k (x ∷ xs) = k * x + sum (map (_*_ k) xs)
                       ≡⟨ ap (_+_ (k * x)) (sum-lin k xs) ⟩
                          k * x + k * sum xs
                       ≡⟨ ! fst ℕ°.distrib k x (sum xs) ⟩
                          k * (x + sum xs)
                       ∎

    lemma : ∀ x xss → sum (map product (map (_∷_ x) xss)) ≡ x * sum (map product xss)
    lemma x xss = sum (map product (map (_∷_ x) xss))
                ≡⟨ ap sum (! map-∘ xss) ⟩
                  sum (map (product ∘ _∷_ x) xss)
                ≡⟨by-definition⟩
                  sum (map (_*_ x ∘ product) xss)
                ≡⟨ ap sum (map-∘ xss) ⟩
                  sum (map (_*_ x) (map product xss))
                ≡⟨ sum-lin x (map product xss) ⟩
                  x * sum (map product xss)
                ∎

    ε = 1
    _⊕_ = λ u z → ε + u * z
    t3 = ∀ xs → sum (map product (inits xs)) ≡ foldr _⊕_ ε xs
    t4 : t3
    t4 [] = refl
    t4 (x ∷ xs) = ap suc (lemma x (inits xs) ∙ ap (_*_ x) (t4 xs))
-- -}
-- -}
-- -}
-- -}
-- -}


-- Andreas, 2014-11-25, variant of Issue 1366

{-# OPTIONS --copatterns #-}

open import Common.Prelude using (Nat; zero; suc; Unit; unit)

data Vec (A : Set) : Nat → Set where
   []  : Vec A zero
   _∷_ : ∀ {n} → A → Vec A n → Vec A (suc n)

-- Singleton type

data Sg {A : Set} (x : A) : Set where
  sg : Sg x

-- Generalizing Unit → Nat

record DNat : Set₁ where
  field
    D     : Set
    force : D → Nat
open DNat

nonNil : ∀ {n} → Vec Unit n → Nat
nonNil []       = zero
nonNil (i ∷ is) = suc (force f i) where
  f : DNat
  D     f      = Unit
  force f unit = zero

g : ∀ {n} {v : Vec Unit n} → Sg (nonNil v) → Sg v
g sg = sg

one : Sg (suc zero)
one = sg

test : Sg (unit ∷ [])
test = g one


data Bool : Set where
  true false : Bool

data Nat : Set where
  zero : Nat
  suc : Nat → Nat

one : Nat
one = suc zero

two : Nat
two = suc one

data Fin : Nat → Set where
  zero : ∀{n} → Fin (suc n)
  suc  : ∀{n} (i : Fin n) → Fin (suc n)

--This part works as expected:

s : ∀ n → (f : (k : Fin n) → Bool) → Fin (suc n)
s n f = zero

t1 : Fin two
t1 = s one (λ { zero → true ; (suc ()) })

-- But Agda is not able to infer the 1 in this case:

ttwo : Fin two
ttwo = s _ (λ { zero → true ; (suc ()) })

-- Warning:
-- _142 : Nat

-- The problem gets worse when i add arguments to the ttwo function. This gives an error:

t3 : Set → Fin two
t3 A = s _ (λ { zero → true ; (suc ()) })