{-# OPTIONS --safe #-}

open import Multiset.Prelude
open import Multiset.Functor
open import Multiset.Limit.Chain
open import Multiset.Limit.TerminalChain
open import Multiset.ListQuotient.Base
open import Multiset.Omniscience using (LLPO)

open import Cubical.Foundations.Equiv
open import Cubical.Foundations.Function using (_∘_ ; _$_)
open import Cubical.Data.Bool hiding (_≤_)
open import Cubical.Data.Empty as Empty
open import Cubical.Data.List as List hiding ([_])
open import Cubical.Data.Nat.Base as  hiding (_^_)
open import Cubical.Data.Nat.Order
open import Cubical.Data.Sigma as Sigma
open import Cubical.Data.Sum as Sum using (_⊎_ ; inl ; inr)
open import Cubical.Data.Unit as Unit using (Unit ; tt)
open import Cubical.Relation.Nullary using (Dec ; Discrete ; yes ; no ; ¬_)
open import Cubical.HITs.PropositionalTruncation as PT using (∥_∥₁ ; ∣_∣₁)
open import Cubical.HITs.PropositionalTruncation.Monad using (_>>=_ ; _>>_ ; return)

module Multiset.Axioms.Completeness {}
  (C : Chain )
  (discrete-chain :  n  Discrete (C .Chain.Ob n))
  (is-set-chain :  n  isSet (C .Chain.Ob n))
  where

  open module C = Chain C

  private
    ≤-suc-cases :  k n  k  suc n
       (k  n)  (k  suc n)
    ≤-suc-cases zero n le = inl zero-≤
    ≤-suc-cases (suc k) zero le = inr (cong suc (≤0→≡0 (pred-≤-pred le)))
    ≤-suc-cases (suc k) (suc n) le with ≤-suc-cases k n (pred-≤-pred le)
    ... | inl p = inl (suc-≤-suc p)
    ... | inr p = inr (cong suc p)

    dec-∈-chain : (n : ) (x : Ob n) (ys : List (Ob n))  Dec (x  ys)
    dec-∈-chain n x ys with dec∈ (discrete-chain n) x ys
    ... | yes (y , m , p) = yes (subst  z  z  ys) (sym p) m)
    ... | no ¬p = no  m  ¬p (x , m , refl))

    rep-π :  n k  k  n  Ob n  Ob k
    rep-π zero k k≤n x = J  k _  Ob k) x (sym (≤0→≡0 k≤n))
    rep-π (suc n) k k≤n x with ≤-suc-cases k n k≤n
    ... | inl p = rep-π n k p (π n x)
    ... | inr p = J  k _  Ob k) x (sym p)


  _∣_ : Limit C  (n : )  Ob n
  x  n = x .Limit.elements n
  infix 20 _∣_

  private
    limitPath :  {x y : Limit C}  (∀ n  x  n  y  n)  x  y
    limitPath = isSet→LimitPathExt C is-set-chain

    rep-π-≤ :  (x : Limit C)  (n k : )  (k≤n : k  n)  rep-π n k k≤n (x  n)  x  k
    rep-π-≤ x zero k k≤n =
      J  k eq  J  k _  Ob k) (x  0) eq  x  k) (JRefl {x = 0}  k _  Ob k) _) (sym (≤0→≡0 k≤n))
    rep-π-≤ x (suc n) k k≤n with ≤-suc-cases k n k≤n
    ... | inl p = cong (rep-π n k p) (x .Limit.is-lim n)  rep-π-≤ x n k p
    ... | inr p = J  k eq  J  k _  Ob k) (x  (suc n)) eq  x  k) (JRefl {x = suc n}  k _  Ob k) _) (sym p)

    cut-≤ :  (x y : Limit C) {k n}  k  n  (x  n  y  n)  (x  k  y  k)
    cut-≤ x y k≤n p = sym (rep-π-≤ x _ _ k≤n)  cong (rep-π _ _ k≤n) p  rep-π-≤ y _ _ k≤n

  _≈⟨_⟩_ : (x : Limit C) (n : )  (y : Limit C)  Type _
  x ≈⟨ n  y = (x  n)  (y  n)

  _≈_ : (x y : Limit C)  Type _
  x  y =  n  x ≈⟨ n  y

  Complete : Type _
  Complete = (x y₁ y₂ : Limit C)  (∀ n  (x ≈⟨ n  y₁)  (x ≈⟨ n  y₂))   (x  y₁)  (x  y₂) ∥₁

  _≺⟨_⟩_ : (x : Limit C)  (n : )  (ys : List (Limit C))  Type _
  x ≺⟨ n  ys =  (x  n)  map (_∣ n) ys ∥₁

  _≺_ : (x : Limit C)  (ys : List (Limit C))  Type _
  x  ys =  n  x ≺⟨ n  ys

  -- Completeness: Given a tree (x : Limit C) and a list of trees (ys : List (Limit C)),
  -- LLPO lets us conclude that, if for each depth n, the approximation xₙ of x is (merely) an approximation of one of the ys,
  -- then x is (merely) one of the ys.
  Complete* : Type _
  Complete* = (x : Limit C)  (ys : List (Limit C))  x  ys   x  ys ∥₁

  _∈?⟨_⟩_ : (x : Limit C) (n : ) (ys : List (Limit C))  Dec ((x  n)  (map (_∣ n) ys))
  x ∈?⟨ n  ys = dec-∈-chain n (x  n) (map (_∣ n) ys)

  -- XXX: Complete can be weakened to have a truncation in the assumption
  Complete*⇒Complete : Complete*  Complete
  Complete*⇒Complete complete* x y₁ y₂ approx-≡ = goal where
    lemma : x  (y₁  y₂  [])
    lemma n = do
      return (Sum.rec here (there  here) (approx-≡ n))

    goal :  (x  y₁)  (x  y₂) ∥₁
    goal = do
      here x≡y₂  complete* x (y₁  y₂  []) lemma
        where there (here x≡y₂)  return $ inr x≡y₂
      return $ inl x≡y₂

  private
    parity : (a :   Bool)  Bool    Bool
    parity a b zero = (a 0 and b) or (not (a 0) and (not b))
    parity a b (suc n) =
      if (a 0 and b) or (not (a 0) and (not b))
        then false
        else parity (a  suc) (not b) n

    parity-prop' :  a b (n1 n2 : )
       parity a b n1  true  parity a b n2  true
       n1  n2
    parity-prop' a b zero zero p q = refl
    parity-prop' a b zero (suc n2) p q with a 0
    parity-prop' a false zero (suc n2) p q | false = Empty.rec (false≢true q)
    parity-prop' a true zero (suc n2) p q | false = Empty.rec (false≢true p)
    parity-prop' a false zero (suc n2) p q | true = Empty.rec (false≢true p)
    parity-prop' a true zero (suc n2) p q | true = Empty.rec (false≢true q)
    parity-prop' a b (suc n1) zero p q with a 0
    parity-prop' a false (suc n1) zero p q | false = Empty.rec (false≢true p)
    parity-prop' a true (suc n1) zero p q | false = Empty.rec (false≢true q)
    parity-prop' a false (suc n1) zero p q | true = Empty.rec (false≢true q)
    parity-prop' a true (suc n1) zero p q | true = Empty.rec (false≢true p)
    parity-prop' a b (suc n1) (suc n2) p q with a 0
    parity-prop' a false (suc n1) (suc n2) p q | false = Empty.rec (false≢true p)
    parity-prop' a true (suc n1) (suc n2) p q | false =
      cong suc (parity-prop' (a  suc) false n1 n2 p q)
    parity-prop' a false (suc n1) (suc n2) p q | true =
        cong suc (parity-prop' (a  suc) true n1 n2 p q)
    parity-prop' a true (suc n1) (suc n2) p q | true = Empty.rec (false≢true p)

    parity-prop :  a b  isProp (Σ[ n   ] parity a b n  true)
    parity-prop a b (n1 , eq1) (n2 , eq2) =
      Σ≡Prop  _  isSetBool _ _) (parity-prop' a b n1 n2 eq1 eq2)

    even-not-odd :  n  isEvenT n  isOddT n  
    even-not-odd (suc n) p q = even-not-odd n q p


    isEven? : Bool    Type
    isEven? false n = isOddT n
    isEven? true n = isEvenT n

    decEven :  n  isEvenT n  isOddT n
    decEven zero = inl _
    decEven (suc n) = Sum.rec inr inl (decEven n)

    parity-even : (a :   Bool)
        n  isEvenT n
       parity a true n  false  a n  true
       Σ[ k   ] Σ[ b  Bool ] isEven? b k × (k < n) × (a k  b) × (parity a true k  true)
    parity-even' : (a :   Bool)
        n  isOddT n
       parity a false n  false  a n  true
       Σ[ k   ] Σ[ b  Bool ] isEven? b k × (k < n) × (a k  not b) × (parity a false k  true)


    parity-even a zero ev eqf eqt =
      Empty.rec (true≢false (sym (cong  b  b and true or not b and false) eqt)   eqf))
    parity-even a (suc n) ev eqf eqt with dichotomyBool (a 0)
    ... | inl q = 0 , true , _ , suc-≤-suc zero-≤ , q , cong  b  b and true or not b and false) q
    ... | inr q with parity-even' (a  suc) n ev (sym (cong  b  if b and true or not b and false then false else parity (a  suc) false n) q)  eqf) eqt
    ... | k , false , p , le , eq' , r = _ , _ , p , suc-≤-suc le , eq' , cong  b  if b and true or not b and false then false else parity (a  suc) false k) q  r
    ... | k , true , p , le , eq' , r = _  , _ , p , suc-≤-suc le , eq' , cong  b  if b and true or not b and false then false else parity (a  suc) false k) q  r

    parity-even' a (suc n) ev eqf eqt with dichotomyBool (a 0)
    ... | inr q = 0 , true , _ , suc-≤-suc zero-≤ , q , cong  b  b and false or not b and true) q
    ... | inl q with parity-even (a  suc) n ev (sym (cong  b  if b and false or not b and true then false else parity (a  suc) true n) q)  eqf) eqt
    ... | k , false , p , le , eq' , r = _ , _ , p , suc-≤-suc le , eq' , cong  b  if b and false or not b and true then false else parity (a  suc) true k) q  r
    ... | k , true , p , le , eq' , r = _ , _ , p , suc-≤-suc le , eq' , cong  b  if b and false or not b and true then false else parity (a  suc) true k) q  r

    parity-odd : (a :   Bool)
        n  isOddT n
       parity a true n  false  a n  false
       Σ[ k   ] Σ[ b  Bool ] isEven? b k × (k < n) × (a k  b) × (parity a true k  true)
    parity-odd' : (a :   Bool)
        n  isEvenT n
       parity a false n  false  a n  false
       Σ[ k   ] Σ[ b  Bool ] isEven? b k × (k < n) × (a k  not b) × (parity a false k  true)

    parity-odd a (suc n) odd eqt eqf with dichotomyBool (a 0)
    ... | inl q = 0 , true , _ , suc-≤-suc zero-≤ , q , cong  b  b and true or not b and false) q
    ... | inr q with parity-odd' (a  suc) n odd ((sym (cong  b  if b and true or not b and false then false else parity (a  suc) false n) q)  eqt)) eqf
    ... | k , false , p , le , eq' , r = _ , _ , p , suc-≤-suc le , eq' , cong  b  if b and true or not b and false then false else parity (a  suc) false k) q  r
    ... | k , true , p , le , eq' , r = _ , _ , p , suc-≤-suc le , eq' , cong  b  if b and true or not b and false then false else parity (a  suc) false k) q  r

    parity-odd' a zero _ eqt eqf =
      Empty.rec (true≢false (sym (cong  b  b and false or not b and true) eqf)  eqt))
    parity-odd' a (suc n) odd eqt eqf with dichotomyBool (a 0)
    ... | inr q = 0 , true , _ , suc-≤-suc zero-≤ , q , cong  b  b and false or not b and true) q
    ... | inl q with parity-odd (a  suc) n odd ((sym (cong  b  if b and false or not b and true then false else parity (a  suc) true n) q)  eqt)) eqf
    ... | k , false , p , le , eq' , r = _ , _ , p , suc-≤-suc le , eq' , cong  b  if b and false or not b and true then false else parity (a  suc) true k) q  r
    ... | k , true , p , le , eq' , r = _ , _ , p , suc-≤-suc le , eq' , cong  b  if b and false or not b and true then false else parity (a  suc) true k) q  r

    _≟⟨_⟩_ : (x : Limit C)  (n : )  (y : Limit C)  Dec (x  n  y  n)
    _≟⟨_⟩_ x n y = discrete-chain n (x  n) (y  n)

    ∈-≤-weaken :  x ys k n  k  n
       (x  n)  (map (_∣ n) ys)
       (x  k)  (map (_∣ k) ys)
    ∈-≤-weaken x ys k n k≤n xₙ∈ysₙ using (x* , (x*∈ys , x*≈x))pre∈mapList xₙ∈ysₙ = abs x* x*∈ys x*≈x where
      abs : (x* : Limit C)  x*  ys  (x*  n  x  n)  (x  k)  (map (_∣ k) ys)
      abs x* x*∈ys x*≈x = subst (_∈ (map (_∣ k) ys)) (cut-≤ x* x k≤n x*≈x) (∈mapList x*∈ys)

    ∈-<-weaken :  x ys k n  k < n
       (x  n)  (map (_∣ n) ys)
       (x  k)  (map (_∣ k) ys)
    ∈-<-weaken x ys k n k<n = ∈-≤-weaken x ys k n (<-weaken k<n)

  llpo⇒complete* : LLPO  Complete*
  llpo⇒complete* llpo x = goal where
    goal :  ys  x  ys   x  ys ∥₁
    goal [] x≺[] = do
      x₀∈[]  x≺[] 0
      Empty.rec (∉[] x₀∈[])
    goal (y  ys) x≺y∷ys = ∥x∈y∷ys∥₁ where
      a :   Bool
      a n with decEven n
      ... | inl _ = not $ Dec→Bool $ x ≟⟨ n  y
      ... | inr _ = Dec→Bool $ x ∈?⟨ n  ys

      a-even :  n  isEvenT n  ¬ (x  n  y  n)  a n  true
      a-even n ev mn with decEven n
      ... | inr odd = Empty.rec (even-not-odd n ev odd)
      ... | inl ev' with discrete-chain n (x  n) (y  n)
      ... | yes q = Empty.rec (mn q)
      ... | no ¬q = refl

      a-odd :  n  isOddT n  ¬ ((x  n)  List.map (_∣ n) ys)  a n  false
      a-odd n odd mn with decEven n
      ... | inl ev = Empty.rec (even-not-odd n ev odd)
      ... | inr _ with dec-∈-chain n (x  n) (List.map (_∣ n) ys)
      ... | yes p = Empty.rec (mn p)
      ... | no ¬p = refl

      par :   Bool
      par = parity a true

      magic :  ((n : )  isEvenT n  par n  false) 
                ((n : )  isOddT n  par n  false) ∥₁
      magic = llpo par (parity-prop _ true)

      case-even : ((n : )  isEvenT n  par n  false)  x  y
      case-even par-even = limitPath x≈y
        where
        module is-even {n} (even : isEvenT n) where
          ¬¬x≈y : ¬ ¬ x ≈⟨ n  y
          ¬¬x≈y ¬xₙ≡yₙ with (parity-even a n even (par-even n even) (a-even n even ¬xₙ≡yₙ))
          ... | (k , true , k-even , _ , _ , par≡true) = false≢true false≡true where
            false≡true =
              false ≡⟨ sym $ par-even k k-even 
              par k ≡⟨ par≡true 
              true  
          ... | (k , false , k-odd , k<n , c , _) with decEven k
          ... | inl k-even = Empty.rec (even-not-odd k k-even k-odd)
          ... | inr _ with x ∈?⟨ k  ys
          ... | yes xₖ∈ysₖ = true≢false c
          ... | no ¬xₖ∈ysₖ = PT.rec isProp⊥  ()) bot where
            bot :   ∥₁
            bot = x≺y∷ys n >>= λ where
              (here xₙ≡yₙ)  return $ ¬xₙ≡yₙ xₙ≡yₙ
              (there xₙ∈ysₙ)  do
                return $ ¬xₖ∈ysₖ (∈-<-weaken x ys k n k<n xₙ∈ysₙ)

          x≈y : x ≈⟨ n  y
          x≈y with (discrete-chain n (x  n) (y  n))
          ... | yes xₙ≡yₙ = xₙ≡yₙ
          ... | no ¬xₙ≡yₙ = Empty.rec (¬¬x≈y ¬xₙ≡yₙ)

        x≈y : x  y
        x≈y n with decEven n
        ... | inl even = is-even.x≈y even
        ... | inr odd =
          (x  n) ≡⟨ sym $ x .Limit.is-lim n 
          π n (x  suc n) ≡⟨ cong (π n) (is-even.x≈y {n = suc n} odd) 
          π n (y  suc n) ≡⟨ y .Limit.is-lim n 
          y  n 

      case-odd : ((n : )  isOddT n  par n  false)   x  ys ∥₁
      case-odd par-odd = goal ys x≺ys
        where module _ where
          ¬¬xₙ∈ysₙ-odd :  n  isOddT n  ¬ ¬ (x  n)  map (_∣ n) ys
          ¬¬xₙ∈ysₙ-odd n odd ¬xₙ∈ysₙ with parity-odd a n odd (par-odd n odd) (a-odd n odd ¬xₙ∈ysₙ)
          ... | (k , false , k-odd , k<n , c , par≡true) = false≢true $
            false ≡⟨ sym $ par-odd k k-odd 
            par k ≡⟨ par≡true 
            true  
          ... | (k , true , k-even , k<n , c , _) with decEven k
          ... | inr k-odd = Empty.rec (even-not-odd k k-even k-odd)
          ... | inl _ with x ≟⟨ k  y
          ... | yes xₖ≡yₖ = false≢true c
          ... | no ¬xₖ≡yₖ = PT.rec isProp⊥  ()) bot where
            bot :   ∥₁
            bot = x≺y∷ys n >>= λ where
              (here xₙ≡yₙ)  return $ ¬xₖ≡yₖ $
                x  k ≡⟨ cut-≤ x y (<-weaken k<n) xₙ≡yₙ 
                y  k 
              (there xₙ∈ysₙ)  return $ Empty.rec $ ¬xₙ∈ysₙ $ xₙ∈ysₙ

          x≺ys-odd :  n  isOddT n   (x  n)  map (_∣ n) ys ∥₁
          x≺ys-odd n odd with x ∈?⟨ n  ys
          ... | yes xₙ∈ysₙ = return xₙ∈ysₙ
          ... | no ¬xₙ∈ysₙ = Empty.rec (¬¬xₙ∈ysₙ-odd n odd ¬xₙ∈ysₙ)

          x≺ys-even :  n  isEvenT n   (x  n)  map (_∣ n) ys ∥₁
          x≺ys-even n even = do
            xₙ₊₁∈ysₙ₊₁  x≺ys-odd (suc n) even
            return $ ∈-≤-weaken x ys n (suc n) (≤-sucℕ {n}) xₙ₊₁∈ysₙ₊₁

          x≺ys :  n   (x  n)  map (_∣ n) ys ∥₁
          x≺ys n = Sum.rec (x≺ys-even n) (x≺ys-odd n) (decEven n)

      ∥x∈y∷ys∥₁ :  x  (y  ys) ∥₁
      ∥x∈y∷ys∥₁ = magic >>= λ where
        (inl even)  return $ here $ case-even even
        (inr odd)  do
          x∈ys  case-odd odd
          return $ there x∈ys