open import Cubical.Foundations.Prelude hiding (empty)
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.Function
open import Cubical.Foundations.Univalence

open import Cubical.Data.List
open import Cubical.Data.Sigma hiding (empty)

module Queue.Examples
  (ABS : Type) (ABS-isProp : isProp ABS)
  (E : Type) (e₀ : E) (ESet : isSet E) where

open import Modality.Abstract ABS ABS-isProp
open import Queue.Base ABS ABS-isProp E e₀ ESet public

module Demo where
  demo : Queue  E  E
  demo q e = q .fst .dequeue (q .fst .enqueue e (q .fst .empty)) .fst

  theorem :  q e   (demo q e  e)
  theorem q e abs = modularity queue-isConcrete  q  demo q e) q listQueue abs

module QueueReverse where
  open import Cubical.Data.Nat

  fromList : (q : Queue)  List E  q .fst .X
  fromList q [] = q .fst .empty
  fromList q (e  es) = q .fst .enqueue e (fromList q es)

  toList : (q : Queue)    q .fst .X  List E
  toList q zero x = []
  toList q (suc k) x =
    let e , x' = q .fst .dequeue x in
    e  toList q k x'

  qreverse : Queue  List E  List E
  qreverse q l = toList q (length l) (fromList q l)

  reverse = rev

  length-reverse : (l : List E)  length (reverse l)  length l
  length-reverse [] = refl
  length-reverse (x  l) =
    length++ (reverse l) [ x ] 
    +-suc (length (rev l)) 0 
    cong suc (+-zero (length (rev l))) 
    cong suc (length-reverse l)

  theorem : (q : Queue)   (qreverse q  reverse)
  theorem q abs = funExt λ l 
      qreverse q l
    ≡⟨ modularity queue-isConcrete  q  qreverse q l) q listQueue abs 
      qreverse listQueue l
    ≡⟨ refl 
      toList listQueue (length l) (fromList listQueue l)
    ≡⟨ cong (toList listQueue (length l)) (fromList-lemma l) 
      toList listQueue (length l) (reverse l)
    ≡⟨ cong  k  toList listQueue k (reverse l)) (sym (length-reverse l)) 
      toList listQueue (length (reverse l)) (reverse l)
    ≡⟨ toList-lemma (reverse l) 
      reverse l
    
    where
      fromList-lemma :  l  fromList listQueue l  reverse l
      fromList-lemma [] = refl
      fromList-lemma (e  l) = cong (_++ [ e ]) (fromList-lemma l)

      toList-lemma :  l  toList listQueue (length l) l  l
      toList-lemma [] = refl
      toList-lemma (e  l) = cong (e ∷_) (toList-lemma l)