------------------------------------------------------------------------
-- The Agda standard library
--
-- Properties related to All
------------------------------------------------------------------------

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

module Data.List.Relation.Unary.All.Properties where

open import Axiom.Extensionality.Propositional using (Extensionality)
open import Data.Bool.Base using (Bool; T; true; false)
open import Data.Bool.Properties using (T-∧)
open import Data.Empty
open import Data.Fin.Base using (Fin) renaming (zero to fzero; suc to fsuc)
open import Data.List.Base as List using
  ( List; []; _∷_; [_]; _∷ʳ_; fromMaybe; null; _++_; concat; map; mapMaybe
  ; inits; tails; drop; take; applyUpTo; applyDownFrom; replicate; tabulate
  ; filter; zipWith; all; derun; deduplicate
  )
open import Data.List.Membership.Propositional
open import Data.List.Membership.Propositional.Properties
open import Data.List.Relation.Unary.All as All using
  ( All; []; _∷_; lookup; updateAt
  ; _[_]=_; here; there
  ; Null
  )
open import Data.List.Relation.Unary.Any as Any using (Any; here; there)
import Data.List.Relation.Binary.Equality.Setoid as ListEq using (_≋_; []; _∷_)
open import Data.List.Relation.Binary.Pointwise using (Pointwise; []; _∷_)
open import Data.List.Relation.Binary.Subset.Propositional using (_⊆_)
open import Data.Maybe.Base as Maybe using (Maybe; just; nothing)
open import Data.Maybe.Relation.Unary.All as MAll using (just; nothing)
open import Data.Nat.Base using (zero; suc; z≤n; s≤s; _<_)
open import Data.Nat.Properties using (≤-refl; ≤-step)
open import Data.Product as Prod using (_×_; _,_; uncurry; uncurry′)
open import Function.Base
open import Function.Equality using (_⟨$⟩_)
open import Function.Equivalence using (_⇔_; equivalence; Equivalence)
open import Function.Inverse using (_↔_; inverse)
open import Function.Surjection using (_↠_; surjection)
open import Level using (Level)
open import Relation.Binary as B using (REL; Setoid; _Respects_)
open import Relation.Binary.PropositionalEquality
  using (_≡_; refl; cong; cong₂; _≗_)
open import Relation.Nullary.Reflects using (invert)
open import Relation.Nullary
open import Relation.Nullary.Negation using (¬?; contradiction; decidable-stable)
open import Relation.Unary
  using (Decidable; Pred; Universal) renaming (_⊆_ to _⋐_)

private
  variable
    a b c p q r ℓ : Level
    A : Set a
    B : Set b
    C : Set c

------------------------------------------------------------------------
-- Properties regarding Null

Null⇒null : ∀ {xs : List A} → Null xs → T (null xs)
Null⇒null [] = _

null⇒Null : ∀ {xs : List A} → T (null xs) → Null xs
null⇒Null {xs = []   } _ = []
null⇒Null {xs = _ ∷ _} ()

------------------------------------------------------------------------
-- Properties of the "points-to" relation _[_]=_

module _ {p} {P : Pred A p} where

  -- Relation _[_]=_ is deterministic: each index points to a single value.

  []=-injective : ∀ {x xs} {px qx : P x} {pxs : All P xs} {i : x ∈ xs} →
                  pxs [ i ]= px →
                  pxs [ i ]= qx →
                  px ≡ qx
  []=-injective here          here          = refl
  []=-injective (there x↦px) (there x↦qx) = []=-injective x↦px x↦qx

  -- See also Data.List.Relation.Unary.All.Properties.WithK.[]=-irrelevant.

------------------------------------------------------------------------
-- Lemmas relating Any, All and negation.

module _ {P : A → Set p} where

  ¬Any⇒All¬ : ∀ xs → ¬ Any P xs → All (¬_ ∘ P) xs
  ¬Any⇒All¬ []       ¬p = []
  ¬Any⇒All¬ (x ∷ xs) ¬p = ¬p ∘ here ∷ ¬Any⇒All¬ xs (¬p ∘ there)

  All¬⇒¬Any : ∀ {xs} → All (¬_ ∘ P) xs → ¬ Any P xs
  All¬⇒¬Any (¬p ∷ _)  (here  p) = ¬p p
  All¬⇒¬Any (_  ∷ ¬p) (there p) = All¬⇒¬Any ¬p p

  ¬All⇒Any¬ : Decidable P → ∀ xs → ¬ All P xs → Any (¬_ ∘ P) xs
  ¬All⇒Any¬ dec []       ¬∀ = ⊥-elim (¬∀ [])
  ¬All⇒Any¬ dec (x ∷ xs) ¬∀ with dec x
  ... |  true because  [p] = there (¬All⇒Any¬ dec xs (¬∀ ∘ _∷_ (invert [p])))
  ... | false because [¬p] = here (invert [¬p])

  Any¬⇒¬All : ∀ {xs} → Any (¬_ ∘ P) xs → ¬ All P xs
  Any¬⇒¬All (here  ¬p) = ¬p           ∘ All.head
  Any¬⇒¬All (there ¬p) = Any¬⇒¬All ¬p ∘ All.tail

  ¬Any↠All¬ : ∀ {xs} → (¬ Any P xs) ↠ All (¬_ ∘ P) xs
  ¬Any↠All¬ = surjection (¬Any⇒All¬ _) All¬⇒¬Any to∘from
    where
    to∘from : ∀ {xs} (¬p : All (¬_ ∘ P) xs) → ¬Any⇒All¬ xs (All¬⇒¬Any ¬p) ≡ ¬p
    to∘from []         = refl
    to∘from (¬p ∷ ¬ps) = cong₂ _∷_ refl (to∘from ¬ps)

    -- If equality of functions were extensional, then the surjection
    -- could be strengthened to a bijection.

    from∘to : Extensionality _ _ →
              ∀ xs → (¬p : ¬ Any P xs) → All¬⇒¬Any (¬Any⇒All¬ xs ¬p) ≡ ¬p
    from∘to ext []       ¬p = ext λ ()
    from∘to ext (x ∷ xs) ¬p = ext λ
      { (here p)  → refl
      ; (there p) → cong (λ f → f p) $ from∘to ext xs (¬p ∘ there)
      }

  Any¬⇔¬All : ∀ {xs} → Decidable P → Any (¬_ ∘ P) xs ⇔ (¬ All P xs)
  Any¬⇔¬All dec = equivalence Any¬⇒¬All (¬All⇒Any¬ dec _)
    where
    -- If equality of functions were extensional, then the logical
    -- equivalence could be strengthened to a surjection.
    to∘from : Extensionality _ _ →
              ∀ {xs} (¬∀ : ¬ All P xs) → Any¬⇒¬All (¬All⇒Any¬ dec xs ¬∀) ≡ ¬∀
    to∘from ext ¬∀ = ext (⊥-elim ∘ ¬∀)

module _ {_~_ : REL A B ℓ} where

  All-swap : ∀ {xs ys} →
             All (λ x → All (x ~_) ys) xs →
             All (λ y → All (_~ y) xs) ys
  All-swap {ys = []}     _   = []
  All-swap {ys = y ∷ ys} []  = All.universal (λ _ → []) (y ∷ ys)
  All-swap {ys = y ∷ ys} ((x~y ∷ x~ys) ∷ pxs) =
    (x~y ∷ (All.map All.head pxs)) ∷
    All-swap (x~ys ∷ (All.map All.tail pxs))

------------------------------------------------------------------------
-- Defining properties of lookup and _[_]=_
--
-- pxs [ i ]= px  if and only if  lookup pxs i = px.

module _ {P : A → Set p} where

  -- `i` points to `lookup pxs i` in `pxs`.

  []=lookup : ∀ {x xs} (pxs : All P xs) (i : x ∈ xs) →
              pxs [ i ]= lookup pxs i
  []=lookup (px ∷ pxs) (here refl) = here
  []=lookup (px ∷ pxs) (there i)   = there ([]=lookup pxs i)

  -- If `i` points to `px` in `pxs`, then `lookup pxs i ≡ px`.

  []=⇒lookup : ∀ {x xs} {px : P x} {pxs : All P xs} {i : x ∈ xs} →
               pxs [ i ]= px →
               lookup pxs i ≡ px
  []=⇒lookup x↦px = []=-injective ([]=lookup _ _) x↦px

  -- If `lookup pxs i ≡ px`, then `i` points to `px` in `pxs`.

  lookup⇒[]= : ∀{x xs} {px : P x} (pxs : All P xs) (i : x ∈ xs) →
               lookup pxs i ≡ px →
               pxs [ i ]= px
  lookup⇒[]= pxs i refl = []=lookup pxs i

------------------------------------------------------------------------
-- Properties of operations over `All`
------------------------------------------------------------------------
-- map

module _ {P : Pred A p} {Q : Pred A q} {f : P ⋐ Q} where

  map-cong : ∀ {xs} {g : P ⋐ Q} (pxs : All P xs) →
             (∀ {x} → f {x} ≗ g) → All.map f pxs ≡ All.map g pxs
  map-cong []         _   = refl
  map-cong (px ∷ pxs) feq = cong₂ _∷_ (feq px) (map-cong pxs feq)

  map-id : ∀ {xs} (pxs : All P xs) → All.map id pxs ≡ pxs
  map-id []         = refl
  map-id (px ∷ pxs) = cong (px ∷_)  (map-id pxs)

  map-compose : ∀ {r} {R : Pred A r} {xs} {g : Q ⋐ R} (pxs : All P xs) →
                All.map g (All.map f pxs) ≡ All.map (g ∘ f) pxs
  map-compose []         = refl
  map-compose (px ∷ pxs) = cong (_ ∷_) (map-compose pxs)

  lookup-map : ∀ {xs x} (pxs : All P xs) (i : x ∈ xs) →
               lookup (All.map f pxs) i ≡ f (lookup pxs i)
  lookup-map (px ∷ pxs) (here refl) = refl
  lookup-map (px ∷ pxs) (there i)   = lookup-map pxs i

------------------------------------------------------------------------
-- _[_]%=_ / updateAt

module _ {P : Pred A p} where

  -- Defining properties of updateAt:

  -- (+) updateAt actually updates the element at the given index.

  updateAt-updates : ∀ {x xs} (i : x ∈ xs) {f : P x → P x} {px : P x} (pxs : All P xs) →
                     pxs              [ i ]= px →
                     updateAt i f pxs [ i ]= f px
  updateAt-updates (here  refl) (px ∷ pxs) here         = here
  updateAt-updates (there i)    (px ∷ pxs) (there x↦px) =
    there (updateAt-updates i pxs x↦px)

  -- (-) updateAt i does not touch the elements at other indices.

  updateAt-minimal : ∀ {x y xs} (i : x ∈ xs) (j : y ∈ xs) →
                     ∀ {f : P y → P y} {px : P x} (pxs : All P xs) →
                     i ≢∈ j →
                     pxs              [ i ]= px →
                     updateAt j f pxs [ i ]= px
  updateAt-minimal (here .refl) (here refl) (px ∷ pxs) i≢j here        =
    ⊥-elim (i≢j refl refl)
  updateAt-minimal (here .refl) (there j)   (px ∷ pxs) i≢j here        = here
  updateAt-minimal (there i)    (here refl) (px ∷ pxs) i≢j (there val) = there val
  updateAt-minimal (there i)    (there j)   (px ∷ pxs) i≢j (there val) =
    there (updateAt-minimal i j pxs (there-injective-≢∈ i≢j) val)

  -- lookup after updateAt reduces.

  -- For same index this is an easy consequence of updateAt-updates
  -- using []=↔lookup.

  lookup∘updateAt : ∀ {x xs} (pxs : All P xs) (i : x ∈ xs) {f : P x → P x} →
                    lookup (updateAt i f pxs) i ≡ f (lookup pxs i)
  lookup∘updateAt pxs i =
    []=⇒lookup (updateAt-updates i pxs (lookup⇒[]= pxs i refl))

  -- For different indices it easily follows from updateAt-minimal.

  lookup∘updateAt′ : ∀ {x y xs} (i : x ∈ xs) (j : y ∈ xs) →
                     ∀ {f : P y → P y} {px : P x} (pxs : All P xs) →
                     i ≢∈ j →
                     lookup (updateAt j f pxs) i ≡ lookup pxs i
  lookup∘updateAt′ i j pxs i≢j =
    []=⇒lookup (updateAt-minimal i j pxs i≢j (lookup⇒[]= pxs i refl))

  -- The other properties are consequences of (+) and (-).
  -- We spell the most natural properties out.
  -- Direct inductive proofs are in most cases easier than just using
  -- the defining properties.

  -- In the explanations, we make use of shorthand  f = g ↾ x
  -- meaning that f and g agree at point x, i.e.  f x ≡ g x.

  -- updateAt (i : x ∈ xs)  is a morphism
  -- from the monoid of endofunctions  P x → P x
  -- to the monoid of endofunctions  All P xs → All P xs.

  -- 1a. relative identity:  f = id ↾ (lookup pxs i)
  --                implies  updateAt i f = id ↾ pxs

  updateAt-id-relative : ∀ {x xs} (i : x ∈ xs) {f : P x → P x} (pxs : All P xs) →
                         f (lookup pxs i) ≡ lookup pxs i →
                         updateAt i f pxs ≡ pxs
  updateAt-id-relative (here refl)(px ∷ pxs) eq = cong (_∷ pxs) eq
  updateAt-id-relative (there i)  (px ∷ pxs) eq = cong (px ∷_) (updateAt-id-relative i pxs eq)

  -- 1b. identity:  updateAt i id ≗ id

  updateAt-id : ∀ {x xs} (i : x ∈ xs) (pxs : All P xs) →
                updateAt i id pxs ≡ pxs
  updateAt-id i pxs = updateAt-id-relative i pxs refl

  -- 2a. relative composition:  f ∘ g = h ↾ (lookup i pxs)
  --                   implies  updateAt i f ∘ updateAt i g = updateAt i h ↾ pxs

  updateAt-compose-relative : ∀ {x xs} (i : x ∈ xs) {f g h : P x → P x} (pxs : All P xs) →
                              f (g (lookup pxs i)) ≡ h (lookup pxs i) →
                              updateAt i f (updateAt i g pxs) ≡ updateAt i h pxs
  updateAt-compose-relative (here refl) (px ∷ pxs) fg=h = cong (_∷ pxs) fg=h
  updateAt-compose-relative (there i)   (px ∷ pxs) fg=h =
    cong (px ∷_) (updateAt-compose-relative i pxs fg=h)

  -- 2b. composition:  updateAt i f ∘ updateAt i g ≗ updateAt i (f ∘ g)

  updateAt-compose : ∀ {x xs} (i : x ∈ xs) {f g : P x → P x} →
                     updateAt {P = P} i f ∘ updateAt i g ≗ updateAt i (f ∘ g)
  updateAt-compose (here refl) (px ∷ pxs) = refl
  updateAt-compose (there i)   (px ∷ pxs) = cong (px ∷_) (updateAt-compose i pxs)

  -- 3. congruence:  updateAt i  is a congruence wrt. extensional equality.

  -- 3a.  If    f = g ↾ (lookup pxs i)
  --      then  updateAt i f = updateAt i g ↾ pxs

  updateAt-cong-relative : ∀ {x xs} (i : x ∈ xs) {f g : P x → P x} (pxs : All P xs) →
                           f (lookup pxs i) ≡ g (lookup pxs i) →
                           updateAt i f pxs ≡ updateAt i g pxs
  updateAt-cong-relative (here refl) (px ∷ pxs) f=g = cong (_∷ pxs) f=g
  updateAt-cong-relative (there i)   (px ∷ pxs) f=g = cong (px ∷_) (updateAt-cong-relative i pxs f=g)

  -- 3b. congruence:  f ≗ g → updateAt i f ≗ updateAt i g

  updateAt-cong : ∀ {x xs} (i : x ∈ xs) {f g : P x → P x} →
                  f ≗ g →
                  updateAt i f ≗ updateAt i g
  updateAt-cong i f≗g pxs = updateAt-cong-relative i pxs (f≗g (lookup pxs i))

  -- The order of updates at different indices i ≢ j does not matter.

  -- This a consequence of updateAt-updates and updateAt-minimal
  -- but easier to prove inductively.

  updateAt-commutes : ∀ {x y xs} (i : x ∈ xs) (j : y ∈ xs) →
                      ∀ {f : P x → P x} {g : P y → P y} →
                      i ≢∈ j →
                      updateAt i f ∘ updateAt j g ≗ updateAt j g ∘ updateAt i f
  updateAt-commutes (here refl) (here refl) i≢j (px ∷ pxs) =
    ⊥-elim (i≢j refl refl)
  updateAt-commutes (here refl) (there j)   i≢j (px ∷ pxs) = refl
  updateAt-commutes (there i)   (here refl) i≢j (px ∷ pxs) = refl
  updateAt-commutes (there i)   (there j)   i≢j (px ∷ pxs) =
    cong (px ∷_) (updateAt-commutes i j (there-injective-≢∈ i≢j) pxs)

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

  map-updateAt : ∀ {x xs} {f : P ⋐ Q} {g : P x → P x} {h : Q x → Q x}
                 (pxs : All P xs) (i : x ∈ xs) →
                 f (g (lookup pxs i)) ≡ h (f (lookup pxs i)) →
                 All.map f (pxs All.[ i ]%= g) ≡ (All.map f pxs) All.[ i ]%= h
  map-updateAt (px ∷ pxs) (here refl) = cong (_∷ _)
  map-updateAt (px ∷ pxs) (there i) feq = cong (_ ∷_) (map-updateAt pxs i feq)

------------------------------------------------------------------------
-- Introduction (⁺) and elimination (⁻) rules for list operations
------------------------------------------------------------------------
-- map

module _ {P : B → Set p} {f : A → B} where

  map⁺ : ∀ {xs} → All (P ∘ f) xs → All P (map f xs)
  map⁺ []       = []
  map⁺ (p ∷ ps) = p ∷ map⁺ ps

  map⁻ : ∀ {xs} → All P (map f xs) → All (P ∘ f) xs
  map⁻ {xs = []}    []       = []
  map⁻ {xs = _ ∷ _} (p ∷ ps) = p ∷ map⁻ ps

-- A variant of All.map.

module _ {P : A → Set p} {Q : B → Set q} {f : A → B} where

  gmap : P ⋐ Q ∘ f → All P ⋐ All Q ∘ map f
  gmap g = map⁺ ∘ All.map g

------------------------------------------------------------------------
-- mapMaybe

module _ (P : B → Set p) {f : A → Maybe B} where

  mapMaybe⁺ : ∀ {xs} → All (MAll.All P) (map f xs) → All P (mapMaybe f xs)
  mapMaybe⁺ {[]}     [] = []
  mapMaybe⁺ {x ∷ xs} (px ∷ pxs) with f x
  ... | nothing = mapMaybe⁺ pxs
  ... | just v with px
  ...   | just pv = pv ∷ mapMaybe⁺ pxs

------------------------------------------------------------------------
-- _++_

module _ {P : A → Set p} where

  ++⁺ : ∀ {xs ys} → All P xs → All P ys → All P (xs ++ ys)
  ++⁺ []         pys = pys
  ++⁺ (px ∷ pxs) pys = px ∷ ++⁺ pxs pys

  ++⁻ˡ : ∀ xs {ys} → All P (xs ++ ys) → All P xs
  ++⁻ˡ []       p          = []
  ++⁻ˡ (x ∷ xs) (px ∷ pxs) = px ∷ (++⁻ˡ _ pxs)

  ++⁻ʳ : ∀ xs {ys} → All P (xs ++ ys) → All P ys
  ++⁻ʳ []       p          = p
  ++⁻ʳ (x ∷ xs) (px ∷ pxs) = ++⁻ʳ xs pxs

  ++⁻ : ∀ xs {ys} → All P (xs ++ ys) → All P xs × All P ys
  ++⁻ []       p          = [] , p
  ++⁻ (x ∷ xs) (px ∷ pxs) = Prod.map (px ∷_) id (++⁻ _ pxs)

  ++↔ : ∀ {xs ys} → (All P xs × All P ys) ↔ All P (xs ++ ys)
  ++↔ {xs} = inverse (uncurry ++⁺) (++⁻ xs) ++⁻∘++⁺ (++⁺∘++⁻ xs)
    where
    ++⁺∘++⁻ : ∀ xs {ys} (p : All P (xs ++ ys)) →
              uncurry′ ++⁺ (++⁻ xs p) ≡ p
    ++⁺∘++⁻ []       p          = refl
    ++⁺∘++⁻ (x ∷ xs) (px ∷ pxs) = cong (_∷_ px) $ ++⁺∘++⁻ xs pxs

    ++⁻∘++⁺ : ∀ {xs ys} (p : All P xs × All P ys) →
              ++⁻ xs (uncurry ++⁺ p) ≡ p
    ++⁻∘++⁺ ([]       , pys) = refl
    ++⁻∘++⁺ (px ∷ pxs , pys) rewrite ++⁻∘++⁺ (pxs , pys) = refl

------------------------------------------------------------------------
-- concat

module _ {P : A → Set p} where

  concat⁺ : ∀ {xss} → All (All P) xss → All P (concat xss)
  concat⁺ []           = []
  concat⁺ (pxs ∷ pxss) = ++⁺ pxs (concat⁺ pxss)

  concat⁻ : ∀ {xss} → All P (concat xss) → All (All P) xss
  concat⁻ {[]}       []  = []
  concat⁻ {xs ∷ xss} pxs = ++⁻ˡ xs pxs ∷ concat⁻ (++⁻ʳ xs pxs)

------------------------------------------------------------------------
-- take and drop

module _ {P : A → Set p} where

  drop⁺ : ∀ {xs} n → All P xs → All P (drop n xs)
  drop⁺ zero    pxs        = pxs
  drop⁺ (suc n) []         = []
  drop⁺ (suc n) (px ∷ pxs) = drop⁺ n pxs

  take⁺ : ∀ {xs} n → All P xs → All P (take n xs)
  take⁺ zero    pxs        = []
  take⁺ (suc n) []         = []
  take⁺ (suc n) (px ∷ pxs) = px ∷ take⁺ n pxs

------------------------------------------------------------------------
-- applyUpTo

module _ {P : A → Set p} where

  applyUpTo⁺₁ : ∀ f n → (∀ {i} → i < n → P (f i)) → All P (applyUpTo f n)
  applyUpTo⁺₁ f zero    Pf = []
  applyUpTo⁺₁ f (suc n) Pf = Pf (s≤s z≤n) ∷ applyUpTo⁺₁ (f ∘ suc) n (Pf ∘ s≤s)

  applyUpTo⁺₂ : ∀ f n → (∀ i → P (f i)) → All P (applyUpTo f n)
  applyUpTo⁺₂ f n Pf = applyUpTo⁺₁ f n (λ _ → Pf _)

  applyUpTo⁻ : ∀ f n → All P (applyUpTo f n) → ∀ {i} → i < n → P (f i)
  applyUpTo⁻ f (suc n) (px ∷ _)   (s≤s z≤n)       = px
  applyUpTo⁻ f (suc n) (_  ∷ pxs) (s≤s (s≤s i<n)) =
    applyUpTo⁻ (f ∘ suc) n pxs (s≤s i<n)

------------------------------------------------------------------------
-- applyDownFrom

module _ {P : A → Set p} where

  applyDownFrom⁺₁ : ∀ f n → (∀ {i} → i < n → P (f i)) → All P (applyDownFrom f n)
  applyDownFrom⁺₁ f zero    Pf = []
  applyDownFrom⁺₁ f (suc n) Pf = Pf ≤-refl ∷ applyDownFrom⁺₁ f n (Pf ∘ ≤-step)

  applyDownFrom⁺₂ : ∀ f n → (∀ i → P (f i)) → All P (applyDownFrom f n)
  applyDownFrom⁺₂ f n Pf = applyDownFrom⁺₁ f n (λ _ → Pf _)

------------------------------------------------------------------------
-- tabulate

module _ {P : A → Set p} where

  tabulate⁺ : ∀ {n} {f : Fin n → A} →
              (∀ i → P (f i)) → All P (tabulate f)
  tabulate⁺ {zero}  Pf = []
  tabulate⁺ {suc n} Pf = Pf fzero ∷ tabulate⁺ (Pf ∘ fsuc)

  tabulate⁻ : ∀ {n} {f : Fin n → A} →
              All P (tabulate f) → (∀ i → P (f i))
  tabulate⁻ {suc n} (px ∷ _) fzero    = px
  tabulate⁻ {suc n} (_ ∷ pf) (fsuc i) = tabulate⁻ pf i

------------------------------------------------------------------------
-- remove

module _ {P : A → Set p} {Q : A → Set q} where

  ─⁺ : ∀ {xs} (p : Any P xs) → All Q xs → All Q (xs Any.─ p)
  ─⁺ (here px) (_ ∷ qs) = qs
  ─⁺ (there p) (q ∷ qs) = q ∷ ─⁺ p qs

  ─⁻ : ∀ {xs} (p : Any P xs) → Q (Any.lookup p) → All Q (xs Any.─ p) → All Q xs
  ─⁻ (here px) q qs        = q ∷ qs
  ─⁻ (there p) q (q′ ∷ qs) = q′ ∷ ─⁻ p q qs

------------------------------------------------------------------------
-- filter

module _ {P : A → Set p} (P? : Decidable P) where

  all-filter : ∀ xs → All P (filter P? xs)
  all-filter []       = []
  all-filter (x ∷ xs) with P? x
  ... |  true because [Px] = invert [Px] ∷ all-filter xs
  ... | false because  _   = all-filter xs

module _ {P : A → Set p} {Q : A → Set q} (P? : Decidable P) where

  filter⁺ : ∀ {xs} → All Q xs → All Q (filter P? xs)
  filter⁺ {xs = _}     [] = []
  filter⁺ {xs = x ∷ _} (Qx ∷ Qxs) with does (P? x)
  ... | false = filter⁺ Qxs
  ... | true  = Qx ∷ filter⁺ Qxs

  filter⁻ : ∀ {xs} → All Q (filter P? xs) → All Q (filter (¬? ∘ P?) xs) → All Q xs
  filter⁻ {[]}         []          []                           = []
  filter⁻ {x ∷ xs}       all⁺        all⁻ with P? x  | ¬? (P? x)
  filter⁻ {x ∷ xs}       all⁺        all⁻  | yes  Px | yes  ¬Px = contradiction Px ¬Px
  filter⁻ {x ∷ xs} (qx ∷ all⁺)       all⁻  | yes  Px | no  ¬¬Px = qx ∷ filter⁻ all⁺ all⁻
  filter⁻ {x ∷ xs}       all⁺  (qx ∷ all⁻) | no    _ | yes  ¬Px = qx ∷ filter⁻ all⁺ all⁻
  filter⁻ {x ∷ xs}       all⁺        all⁻  | no  ¬Px | no  ¬¬Px = contradiction ¬Px ¬¬Px

------------------------------------------------------------------------
-- derun and deduplicate

module _ {P : A → Set p} {R : A → A → Set q} (R? : B.Decidable R) where

  derun⁺ : ∀ {xs} → All P xs → All P (derun R? xs)
  derun⁺ {[]} [] = []
  derun⁺ {x ∷ []} (px ∷ []) = px ∷ []
  derun⁺ {x ∷ y ∷ xs} (px ∷ all[P,y∷xs]) with does (R? x y)
  ... | false = px ∷ derun⁺ all[P,y∷xs]
  ... | true  = derun⁺ all[P,y∷xs]

  deduplicate⁺ : ∀ {xs} → All P xs → All P (deduplicate R? xs)
  deduplicate⁺ [] = []
  deduplicate⁺ {x ∷ _} (px ∷ all[P,xs]) = px ∷ filter⁺ (¬? ∘ R? x) (deduplicate⁺ all[P,xs])

  derun⁻ : P B.Respects (flip R) → ∀ xs → All P (derun R? xs) → All P xs
  derun⁻ P-resp-R []       []          = []
  derun⁻ P-resp-R (x ∷ xs) all[P,x∷xs] = aux x xs all[P,x∷xs] where
    aux : ∀ x xs → All P (derun R? (x ∷ xs)) → All P (x ∷ xs)
    aux x []       (px ∷ []) = px ∷ []
    aux x (y ∷ xs) all[P,x∷y∷xs] with R? x y
    aux x (y ∷ xs) all[P,y∷xs]        | yes Rxy with aux y xs all[P,y∷xs]
    aux x (y ∷ xs) all[P,y∷xs]        | yes Rxy | r@(py ∷ _) = P-resp-R Rxy py ∷ r
    aux x (y ∷ xs) (px ∷ all[P,y∷xs]) | no _ = px ∷ aux y xs all[P,y∷xs]

  module _ (P-resp-R : P B.Respects R) where

    deduplicate⁻ : ∀ xs → All P (deduplicate R? xs) → All P xs
    deduplicate⁻ [] [] = []
    deduplicate⁻ (x ∷ xs) (px ∷ app[P,dedup[xs]]) = px ∷ deduplicate⁻ xs (filter⁻ (¬? ∘ R? x) app[P,dedup[xs]] (All.tabulate aux)) where
      aux : ∀ {z} → z ∈ filter (¬? ∘ ¬? ∘ R? x) (deduplicate R? xs) → P z
      aux {z} z∈filter = P-resp-R (decidable-stable (R? x z) (Prod.proj₂ (∈-filter⁻ (¬? ∘ ¬? ∘ R? x) {z} {deduplicate R? xs} z∈filter))) px

------------------------------------------------------------------------
-- zipWith

module _ (P : C → Set p) (f : A → B → C) where

  zipWith⁺ : ∀ {xs ys} → Pointwise (λ x y → P (f x y)) xs ys →
             All P (zipWith f xs ys)
  zipWith⁺ []              = []
  zipWith⁺ (Pfxy ∷ Pfxsys) = Pfxy ∷ zipWith⁺ Pfxsys

------------------------------------------------------------------------
-- Operations for constructing lists
------------------------------------------------------------------------
-- singleton

module _ {P : A → Set p} where

  singleton⁻ : ∀ {x} → All P [ x ] → P x
  singleton⁻ (px ∷ []) = px

------------------------------------------------------------------------
-- snoc

module _ {P : A → Set p} {x xs} where

  ∷ʳ⁺ : All P xs → P x → All P (xs ∷ʳ x)
  ∷ʳ⁺ pxs px = ++⁺ pxs (px ∷ [])

  ∷ʳ⁻ : All P (xs ∷ʳ x) → All P xs × P x
  ∷ʳ⁻ pxs = Prod.map₂ singleton⁻ $ ++⁻ xs pxs

------------------------------------------------------------------------
-- fromMaybe

module _ {P : A → Set p} where

  fromMaybe⁺ : ∀ {mx} → MAll.All P mx → All P (fromMaybe mx)
  fromMaybe⁺ (just px) = px ∷ []
  fromMaybe⁺ nothing   = []

  fromMaybe⁻ : ∀ mx → All P (fromMaybe mx) → MAll.All P mx
  fromMaybe⁻ (just x) (px ∷ []) = just px
  fromMaybe⁻ nothing  p         = nothing

------------------------------------------------------------------------
-- replicate

module _ {P : A → Set p} where

  replicate⁺ : ∀ n {x} → P x → All P (replicate n x)
  replicate⁺ zero    px = []
  replicate⁺ (suc n) px = px ∷ replicate⁺ n px

  replicate⁻ : ∀ {n x} → All P (replicate (suc n) x) → P x
  replicate⁻ (px ∷ _) = px

------------------------------------------------------------------------
-- inits

module _ {P : A → Set p} where

  inits⁺ : ∀ {xs} → All P xs → All (All P) (inits xs)
  inits⁺ []         = [] ∷ []
  inits⁺ (px ∷ pxs) = [] ∷ gmap (px ∷_) (inits⁺ pxs)

  inits⁻ : ∀ xs → All (All P) (inits xs) → All P xs
  inits⁻ []               pxs                   = []
  inits⁻ (x ∷ [])         ([] ∷ p[x] ∷ [])      = p[x]
  inits⁻ (x ∷ xs@(_ ∷ _)) ([] ∷ pxs@(p[x] ∷ _)) =
    singleton⁻ p[x] ∷ inits⁻ xs (All.map (drop⁺ 1) (map⁻ pxs))

------------------------------------------------------------------------
-- tails

module _ {P : A → Set p} where

  tails⁺ : ∀ {xs} → All P xs → All (All P) (tails xs)
  tails⁺ []             = [] ∷ []
  tails⁺ pxxs@(_ ∷ pxs) = pxxs ∷ tails⁺ pxs

  tails⁻ : ∀ xs → All (All P) (tails xs) → All P xs
  tails⁻ []       pxs        = []
  tails⁻ (x ∷ xs) (pxxs ∷ _) = pxxs

------------------------------------------------------------------------
-- all

module _ (p : A → Bool) where

  all⁺ : ∀ xs → T (all p xs) → All (T ∘ p) xs
  all⁺ []       _     = []
  all⁺ (x ∷ xs) px∷xs with Equivalence.to (T-∧ {p x}) ⟨$⟩ px∷xs
  ... | (px , pxs) = px ∷ all⁺ xs pxs

  all⁻ : ∀ {xs} → All (T ∘ p) xs → T (all p xs)
  all⁻ []         = _
  all⁻ (px ∷ pxs) = Equivalence.from T-∧ ⟨$⟩ (px , all⁻ pxs)

------------------------------------------------------------------------
-- All is anti-monotone.

anti-mono : ∀ {P : A → Set p} {xs ys} → xs ⊆ ys → All P ys → All P xs
anti-mono xs⊆ys pys = All.tabulate (lookup pys ∘ xs⊆ys)

all-anti-mono : ∀ (p : A → Bool) {xs ys} →
                xs ⊆ ys → T (all p ys) → T (all p xs)
all-anti-mono p xs⊆ys = all⁻ p ∘ anti-mono xs⊆ys ∘ all⁺ p _

------------------------------------------------------------------------
-- Interactions with pointwise equality
------------------------------------------------------------------------

module _ {c ℓ} (S : Setoid c ℓ) where

  open Setoid S
  open ListEq S

  respects : {P : Pred Carrier p} → P Respects _≈_ → (All P) Respects _≋_
  respects p≈ []            []         = []
  respects p≈ (x≈y ∷ xs≈ys) (px ∷ pxs) = p≈ x≈y px ∷ respects p≈ xs≈ys pxs

------------------------------------------------------------------------
-- DEPRECATED NAMES
------------------------------------------------------------------------
-- Please use the new names as continuing support for the old names is
-- not guaranteed.

-- Version 0.16

All-all = all⁻
{-# WARNING_ON_USAGE All-all
"Warning: All-all was deprecated in v0.16.
Please use all⁻ instead."
#-}
all-All = all⁺
{-# WARNING_ON_USAGE all-All
"Warning: all-All was deprecated in v0.16.
Please use all⁺ instead."
#-}
All-map = map⁺
{-# WARNING_ON_USAGE All-map
"Warning: All-map was deprecated in v0.16.
Please use map⁺ instead."
#-}
map-All = map⁻
{-# WARNING_ON_USAGE map-All
"Warning: map-All was deprecated in v0.16.
Please use map⁻ instead."
#-}

-- Version 1.0

filter⁺₁ = all-filter
{-# WARNING_ON_USAGE filter⁺₁
"Warning: filter⁺₁ was deprecated in v1.0.
Please use all-filter instead."
#-}
filter⁺₂ = filter⁺
{-# WARNING_ON_USAGE filter⁺₂
"Warning: filter⁺₂ was deprecated in v1.0.
Please use filter⁺ instead."
#-}

-- Version 1.3

Any¬→¬All = Any¬⇒¬All
{-# WARNING_ON_USAGE Any¬→¬All
"Warning: Any¬→¬All was deprecated in v1.3.
Please use Any¬⇒¬All instead."
#-}


import Level
open import Relation.Binary
  using (Decidable ; Rel ; IsStrictTotalOrder ; Tri)
open import Relation.Binary.PropositionalEquality
  using (_≡_ ; refl ; cong ; sym ; module ≡-Reasoning ; inspect ; subst)
open import Data.Nat as Nat
  using (ℕ ; suc ; zero ; _≤′_ ; _≤_ ; _+_ ; s≤s ; z≤n ; ≤′-refl ;
         ≤′-step)
open import Data.Nat.Properties as NatP
  using (≤⇒≤′ ; ≤′⇒≤ ;  m≤m+n ; s≤′s)
open import Data.Nat.Properties.Simple as NatPS
  using (+-comm ; +-suc)
open import Data.Product using (∃)

open import Relation.Nullary using (¬_ ; Dec ; yes ; no)
import Relation.Unary as U

open import Data.MoreNatProp

module Data.OrderedListMap
  (Key : Set)
  (inject : Key → ℕ)
  (Value : Set)
  where

open import utility

open import Data.Maybe using (just ; nothing ; Maybe)

open import Function
  using (_∘_ ; _∋_)
open import Data.Empty
  using (⊥ ; ⊥-elim)
open import Data.List
  using (List ; _++_ ; [_] ; [] ; _∷_ ; map)
open import Data.List.All as All
  using (All)

open import Data.Product
  using (Σ ; Σ-syntax ; _,_ ; proj₁ ; proj₂ ; _×_ ; _,′_ ; ∃-syntax)
open import Data.Sum
  using (inj₁ ; inj₂ ; _⊎_)
open import Data.Unit
  using (⊤ ; tt)
open import Relation.Nullary
  using (yes ; no ; ¬_)
open import Data.List.Any using (here ; there)
open import Data.List.Any.Properties using (∷↔ ; ⊥↔Any[])
  renaming ( ++⁺ˡ  to ++ˡ ; ++⁺ʳ to ++ʳ )
open import Function using (_∘_)
open import Function.Inverse using (_↔_ ; Inverse)
open import Function.Equality using (Π)

open import Data.Bool using (Bool ; true ; false)

infix 6 _U_

LMap : Set
LMap = List (Maybe Value)

m-insert : Maybe Value → ℕ → LMap → LMap
m-insert v zero [] = [ v ]
m-insert nothing zero (x ∷ l) = x ∷ l
m-insert (just v) zero (x ∷ l) = (just v) ∷ l
m-insert v (suc n) [] = nothing ∷ (m-insert v n [])
m-insert v (suc n) (x ∷ l) = x ∷ (m-insert v n l)

insert : Key → Value → LMap → LMap
insert k v l = m-insert (just v) (inject k) l

Dom' : LMap → List ℕ
Dom' [] = []
Dom' ((just _) ∷ l) = 0 ∷ (map suc (Dom' l))
Dom' (nothing ∷ l) = (map suc (Dom' l))

_U_ : LMap → LMap → LMap
[] U m2 = m2
(x ∷ m1) U [] = (x ∷ m1)
(just x ∷ m1) U (just x₁ ∷ m2) = just x₁ ∷ (m1 U m2)
(just x ∷ m1) U (nothing ∷ m2) = just x ∷ (m1 U m2)
(nothing ∷ m1) U (just x ∷ m2) = just x ∷ (m1 U m2)
(nothing ∷ m1) U (nothing ∷ m2) = nothing ∷ (m1 U m2)

dist'::b : ∀{V1 V2 S1 S2} → (distinct'{ℕ} (S1 ∷ V1) (S2 ∷ V2)) → (distinct' V1 V2)
dist'::b d = dist'-sym (dist':: (dist'-sym (dist':: d)))

dist-cons : ∀{k k' l l'} → distinct'{ℕ} (k ∷ l) (k' ∷ l') → ¬ k ≡ k'
dist-cons {k} d refl = d k (here refl) (here refl)

data dist-dom : LMap → LMap → Set where
  el : ∀{l} → dist-dom [] l
  le : ∀{l} → dist-dom l []
  cl : ∀{l1 l2 v} → dist-dom l1 l2 → dist-dom (nothing ∷ l1) ((just v) ∷ l2)
  lc : ∀{l1 l2 v} → dist-dom l1 l2 → dist-dom ((just v) ∷ l1) (nothing ∷ l2)
  nn : ∀{l1 l2} → dist-dom l1 l2 → dist-dom (nothing ∷ l1) (nothing ∷ l2)

∈→⊥ : ∀{A} {z : A} → z ∈ [] → ⊥
∈→⊥{A}{z} ()

-- Π._⟨$⟩_ (Inverse.from (⊥↔Any[]{A = A}{P = _≡_ z})) x

dist-b : ∀{l l' x} → distinct' (x ∷ l) (x ∷ l') → ⊥
dist-b{x = x} d with (dist-cons d)
... | relf = d x (here refl) (here refl)

sucin : ∀{z n} → z ∈ n → (suc z) ∈ (map suc n)
sucin {z} {.(_ ∷ _)} (here px) rewrite px = here refl
sucin {z} {.(_ ∷ _)} (there I) = (there (sucin I))

suc≡ : ∀{z w} → suc z ≡ suc w → z ≡ w
suc≡ refl = refl

sucout : ∀{z n} → (suc z) ∈ (map suc n) → z ∈ n
sucout {z} {[]} ()
sucout {z} {x ∷ n} (here px) rewrite suc≡ px = (here refl)
sucout {z} {x ∷ n} (there I) = (there (sucout I))

dist-map : ∀{m n} → distinct' (map suc m) (map suc n) → distinct' m n
dist-map {[]} {[]} d = λ z x x₁ → ∈→⊥ x
dist-map {[]} {x ∷ n} d = λ z x₁ x₂ →  ∈→⊥ x₁
dist-map {x ∷ m} {[]} d = λ z x₁ x₂ → ∈→⊥ x₂
dist-map {zero ∷ m} {zero ∷ n} d =  ⊥-elim (dist-b d)
dist-map {zero ∷ m} {suc y ∷ n} d z x x₁ = d (suc z) (sucin x) (sucin x₁)
dist-map {suc y ∷ m} {zero ∷ n} d z x x₁ = d (suc z) (sucin x) (sucin x₁)
dist-map {suc _ ∷ m} {suc y ∷ n} d z x x₁ = d (suc z) (sucin x) (sucin x₁)

dist-in : ∀{m1 m2 x y} → distinct' (Dom' (x ∷ m1)) (Dom' (y ∷ m2)) → distinct' (Dom' m1) (Dom' m2)
dist-in {[]} {[]} {x} {y} dd = λ z x₁ x₂ → ∈→⊥ x₁
dist-in {[]} {x ∷ m2} {x₁} {y} dd = λ z x₂ x₃ → ∈→⊥ x₂
dist-in {x ∷ m1} {[]} {x₁} {y} dd = λ z x₂ x₃ → ∈→⊥ x₃
dist-in {h₁ ∷ m1} {h₂ ∷ m2} {just x} {just x₁} dd = ⊥-elim (dist-b dd)
dist-in {h₁ ∷ m1} {h₂ ∷ m2} {just x} {nothing} dd = λ z x₁ x₂ → (dist'-sym (dist':: (dist'-sym dd))) _ (sucin x₁) (sucin x₂)
dist-in {h₁ ∷ m1} {h₂ ∷ m2} {nothing} {just x} dd = λ z x₁ x₂ → (dist':: dd) _ (sucin x₁) (sucin x₂)
dist-in {h₁ ∷ m1} {h₂ ∷ m2} {nothing} {nothing} dd = dist-map dd
-- ((Π._⟨$⟩_ (Inverse.to (∷↔ (_≡_ _)))) (inj₂ x₂))

dist-gen : ∀{m1 m2} → distinct' (Dom' m1) (Dom' m2) → dist-dom m1 m2
dist-gen {[]} {[]} dd = el
dist-gen {[]} {x ∷ m2} dd = el
dist-gen {x ∷ m1} {[]} dd = le
dist-gen {just x ∷ m1} {just x₁ ∷ m2} dd = ⊥-elim (dist-b dd)
dist-gen {just x ∷ m1} {nothing ∷ m2} dd = lc (dist-gen (dist-in{m1}{m2}{just x}{nothing} dd))
dist-gen {nothing ∷ m1} {just x ∷ m2} dd = cl (dist-gen (dist-in{m1}{m2}{nothing}{just x} dd))
dist-gen {nothing ∷ m1} {nothing ∷ m2} dd = nn (dist-gen (dist-in{m1}{m2}{nothing}{nothing} dd))

U-comm' : ∀{m1 m2} → dist-dom m1 m2 → (m1 U m2) ≡ (m2 U m1)
U-comm' {.[]} {[]} el = refl
U-comm' {.[]} {x ∷ m2} el = refl
U-comm' {[]} {.[]} le = refl
U-comm' {x ∷ m1} {.[]} le = refl
U-comm' {.(nothing ∷ _)} {.(just _ ∷ _)} (cl dd) rewrite U-comm' dd = refl
U-comm' {.(just _ ∷ _)} {.(nothing ∷ _)} (lc dd) rewrite U-comm' dd = refl
U-comm' {.(nothing ∷ _)} {.(nothing ∷ _)} (nn dd) rewrite U-comm' dd = refl

U-comm : ∀{m1 m2} → distinct' (Dom' m1) (Dom' m2) → (m1 U m2) ≡ (m2 U m1)
U-comm{m1}{m2} dd = U-comm'{m1}{m2} (dist-gen dd)

U-assoc' : ∀ m1 m2 m3 → m1 U (m2 U m3) ≡ (m1 U m2) U m3
U-assoc' [] m2 m3 = refl
U-assoc' (x ∷ m1) [] m3 = refl
U-assoc' (x ∷ m1) (x₁ ∷ m2) []
  rewrite U-comm {(x ∷ m1) U (x₁ ∷ m2)} {[]} (λ _ _ ()) = refl
U-assoc' (just x ∷ m1) (just x₁ ∷ m2) (just x₂ ∷ m3) rewrite U-assoc' m1 m2 m3 = refl
U-assoc' (just x ∷ m1) (just x₁ ∷ m2) (nothing ∷ m3) rewrite U-assoc' m1 m2 m3 = refl
U-assoc' (just x ∷ m1) (nothing ∷ m2) (just x₁ ∷ m3) rewrite U-assoc' m1 m2 m3 = refl
U-assoc' (just x ∷ m1) (nothing ∷ m2) (nothing ∷ m3) rewrite U-assoc' m1 m2 m3 = refl
U-assoc' (nothing ∷ m1) (just x ∷ m2) (just x₁ ∷ m3) rewrite U-assoc' m1 m2 m3 = refl
U-assoc' (nothing ∷ m1) (just x ∷ m2) (nothing ∷ m3) rewrite U-assoc' m1 m2 m3 = refl
U-assoc' (nothing ∷ m1) (nothing ∷ m2) (just x ∷ m3) rewrite U-assoc' m1 m2 m3 = refl
U-assoc' (nothing ∷ m1) (nothing ∷ m2) (nothing ∷ m3) rewrite U-assoc' m1 m2 m3 = refl

U-assoc : ∀ m1 m2 m3 → m1 U (m2 U m3) ≡ (m1 U m2) U m3
U-assoc m1 m2 m3 = U-assoc' m1 m2 m3

0∈S : ∀{l} → 0 ∈ (map suc l) → ⊥
0∈S {[]} ()
0∈S {x ∷ l} (here ())
0∈S {x ∷ l} (there p) = 0∈S p

n∈S : ∀{n x l} → suc n ∈ Dom' (x ∷ l) → n ∈ Dom' l
n∈S {n} {just x} {[]} (here ())
n∈S {n} {just x} {[]} (there ())
n∈S {n} {just x} {x₁ ∷ l} (here ())
n∈S {n} {just x} {x₁ ∷ l} (there p) = sucout p
n∈S {n} {nothing} {[]} ()
n∈S {n} {nothing} {x ∷ l} p = sucout p

n+1∈S : ∀ {n x m} → n ∈ (Dom' m) → (suc n) ∈ (Dom' (x ∷ m))
n+1∈S {n} {just x} {m} n∈ = there (sucin n∈)
n+1∈S {n} {nothing} {m} n∈ = sucin n∈

deref : (n : ℕ) → (l : LMap) → n ∈ (Dom' l) → Value
deref zero [] ()
deref zero (just x ∷ l) p = x
deref zero (nothing ∷ l) p = ⊥-elim (0∈S p)
deref (suc n) [] ()
deref (suc n) (x ∷ l) p = deref n l (n∈S{n}{x}{l} p)

-- thrms
deref-∈-irr : ∀{k l} → (∈1 : k ∈ (Dom' l)) →  (∈2 : k ∈ (Dom' l)) → deref k l ∈1 ≡ deref k l ∈2
deref-∈-irr {zero} {[]} () ∈2
deref-∈-irr {suc k} {[]} () ∈2
deref-∈-irr {zero} {just x ∷ l} ∈1 ∈2 = refl
deref-∈-irr {zero} {nothing ∷ l} ∈1 ∈2 = ⊥-elim (0∈S ∈1)
deref-∈-irr {suc k} {x ∷ l} ∈1 ∈2 = deref-∈-irr (n∈S{k}{x}{l} ∈1) (n∈S{k}{x}{l} ∈2)

insert-mono' : ∀{l d l' d' n n' v} → n ∈ d → d ≡ (Dom' l) → d' ≡ (Dom' l') → l' ≡ (m-insert (just v) n' l) → n ∈ d'
insert-mono' {[]} {.[]} {[]} {.[]} {zero} {zero} () refl refl l'eq
insert-mono' {[]} {.[]} {[]} {.[]} {zero} {suc n'} () refl refl l'eq
insert-mono' {[]} {.[]} {[]} {.[]} {suc n} {zero} () refl refl l'eq
insert-mono' {[]} {.[]} {[]} {.[]} {suc n} {suc n'} () refl refl l'eq
insert-mono' {[]} {.[]} {x ∷ l'} {.(Dom' (x ∷ l'))} {zero} {zero} () refl refl l'eq
insert-mono' {[]} {.[]} {x ∷ l'} {.(Dom' (x ∷ l'))} {zero} {suc n'} () refl refl l'eq
insert-mono' {[]} {.[]} {x ∷ l'} {.(Dom' (x ∷ l'))} {suc n} {zero} () refl refl l'eq
insert-mono' {[]} {.[]} {x ∷ l'} {.(Dom' (x ∷ l'))} {suc n} {suc n'} () refl refl l'eq
insert-mono' {x ∷ l} {.(Dom' (x ∷ l))} {[]} {.[]} {zero} {zero} n∈ refl refl ()
insert-mono' {x ∷ l} {.(Dom' (x ∷ l))} {[]} {.[]} {zero} {suc n'} n∈ refl refl ()
insert-mono' {x ∷ l} {.(Dom' (x ∷ l))} {[]} {.[]} {suc n} {zero} n∈ refl refl ()
insert-mono' {x ∷ l} {.(Dom' (x ∷ l))} {[]} {.[]} {suc n} {suc n'} n∈ refl refl ()
insert-mono' {just x₁ ∷ l} {.(0 ∷ map suc (Dom' l))} {just x ∷ l'} {.(0 ∷ map suc (Dom' l'))} {zero} {zero} {v} N∈ refl refl l'eq = here refl
insert-mono' {just x₁ ∷ l} {.(0 ∷ map suc (Dom' l))} {nothing ∷ l'} {.(map suc (Dom' l'))} {zero} {zero} {v} N∈ refl refl ()
insert-mono' {nothing ∷ .l'} {.(map suc (Dom' l'))} {.(just _) ∷ l'} {.(0 ∷ map suc (Dom' l'))} {zero} {zero} n∈ refl refl refl = here refl
insert-mono' {just x₁ ∷ l} {.(0 ∷ map suc (Dom' l))} {just x ∷ l'} {.(0 ∷ map suc (Dom' l'))} {zero} {suc n'} n∈ refl refl l'eq = here refl
insert-mono' {nothing ∷ l} {.(map suc (Dom' l))} {just x ∷ l'} {.(0 ∷ map suc (Dom' l'))} {zero} {suc n'} n∈ refl refl l'eq = here refl
insert-mono' {just x₁ ∷ .l'} {.(0 ∷ map suc (Dom' l'))} {just x ∷ l'} {.(0 ∷ map suc (Dom' l'))} {suc n} {zero} (here ()) refl refl refl
insert-mono' {just x₁ ∷ .l'} {.(0 ∷ map suc (Dom' l'))} {just x ∷ l'} {.(0 ∷ map suc (Dom' l'))} {suc n} {zero} (there n∈) refl refl refl
    = there n∈
insert-mono' {nothing ∷ .l'} {.(map suc (Dom' l'))} {just x ∷ l'} {.(0 ∷ map suc (Dom' l'))} {suc n} {zero} n∈ refl refl refl = there n∈
insert-mono' {just x ∷ l} {.(0 ∷ map suc (Dom' l))} {nothing ∷ l'} {.(map suc (Dom' l'))} {suc n} {zero} n∈ refl refl ()
insert-mono' {nothing ∷ l} {.(map suc (Dom' l))} {nothing ∷ l'} {.(map suc (Dom' l'))} {suc n} {zero} n∈ refl refl ()
insert-mono' {just x₁ ∷ l} {.(0 ∷ map suc (Dom' l))} {just x ∷ l'} {.(0 ∷ map suc (Dom' l'))} {suc n} {suc n'} (here ()) refl refl l'eq
insert-mono' {just x₁ ∷ l} {.(0 ∷ map suc (Dom' l))} {just .x₁ ∷ .(m-insert (just _) n' l)} {.(0 ∷ map suc (Dom' (m-insert (just _) n' l)))} {suc n} {suc n'} {v} (there n∈) refl refl refl
  = there (sucin (insert-mono'{l = l}{l' = (m-insert (just _) n' l)}{n = n}{n' = n'}{v} (sucout n∈) refl refl refl))
insert-mono' {just x ∷ l} {.(0 ∷ map suc (Dom' l))} {nothing ∷ l'} {.(map suc (Dom' l'))} {suc n} {suc n'} (here ()) refl refl l'eq
insert-mono' {just x ∷ l} {.(0 ∷ map suc (Dom' l))} {nothing ∷ l'} {.(map suc (Dom' l'))} {suc n} {suc n'} {v} (there n∈) refl refl ()
insert-mono' {nothing ∷ l} {.(map suc (Dom' l))} {just x ∷ l'} {.(0 ∷ map suc (Dom' l'))} {suc n} {suc n'} n∈ refl refl ()
insert-mono' {nothing ∷ l} {.(map suc (Dom' l))} {nothing ∷ l'} {.(map suc (Dom' l'))} {suc n} {suc n'} {v} n∈ refl refl refl
  =  sucin (insert-mono'{l = l}{l' = l'}{n = n}{n' = n'}{v = v} (sucout n∈) refl refl refl)
insert-mono' {just x ∷ l} {.(0 ∷ map suc (Dom' l))} {nothing ∷ l'} {.(map suc (Dom' l'))} {zero} {suc n'} n∈ refl refl ()
insert-mono' {nothing ∷ l} {.(map suc (Dom' l))} {nothing ∷ l'} {.(map suc (Dom' l'))} {zero} {suc n'} n∈ refl refl l'eq = ⊥-elim (0∈S n∈)

insert-mono : ∀{m n n' v} → n ∈ (Dom' m) → n ∈ (Dom' (m-insert (just v) n' m))
insert-mono{m}{n}{n'}{v} i = insert-mono'{m}{Dom' m}{(m-insert (just v) n' m)}{(Dom' (m-insert (just v) n' m))}{n}{n'}{v} i refl refl refl

U-mono : ∀{m m' n} → n ∈ (Dom' m) → n ∈ (Dom' (m U m'))
U-mono {[]} {m'} {n} ()
U-mono {just x ∷ m} {[]} {n} (here px) = here px
U-mono {just x ∷ m} {just x₁ ∷ m'} {n} (here px) = here px
U-mono {just x ∷ m} {nothing ∷ m'} {n} (here px) = here px
U-mono {just x ∷ m} {[]} {n} (there ∈) = there ∈
U-mono {just x ∷ m} {just x₁ ∷ m'} {zero} (there ∈) = here refl
U-mono {just x ∷ m} {nothing ∷ m'} {zero} (there ∈) = here refl
U-mono {just x ∷ m} {just x₁ ∷ m'} {suc n} (there ∈) = there (sucin (U-mono{m}{m'}{n} (sucout ∈)))
U-mono {just x ∷ m} {nothing ∷ m'} {suc n} (there ∈) = there (sucin (U-mono{m}{m'}{n} (sucout ∈)))
U-mono {nothing ∷ m} {[]} {n} ∈ = ∈
U-mono {nothing ∷ m} {just x ∷ m'} {zero} ∈ = here refl
U-mono {nothing ∷ m} {just x ∷ m'} {suc n} ∈ = there (sucin (U-mono{m}{m'}{n} (sucout ∈)))
U-mono {nothing ∷ m} {nothing ∷ m'} {zero} ∈ = ⊥-elim (0∈S ∈)
U-mono {nothing ∷ m} {nothing ∷ m'} {suc n} ∈ = (sucin (U-mono{m}{m'}{n} (sucout ∈)))

U⁻ : ∀ {m m' n} → n ∈ Dom' (m U m') → (n ∈ Dom' m) ⊎ (n ∈ Dom' m')
U⁻ {[]} {[]} {n} n∈Dom⟨mUm'⟩ = inj₁ n∈Dom⟨mUm'⟩
U⁻ {[]} {x ∷ m'} {n} n∈Dom⟨mUm'⟩ = inj₂ n∈Dom⟨mUm'⟩
U⁻ {x ∷ m} {[]} {n} n∈Dom⟨mUm'⟩ = inj₁ n∈Dom⟨mUm'⟩
U⁻ {just x ∷ m} {just x₁ ∷ m'} {zero} n∈Dom⟨mUm'⟩ = inj₁ (here refl)
U⁻ {just x ∷ m} {just x₁ ∷ m'} {suc n} (here ())
U⁻ {just x ∷ m} {just x₁ ∷ m'} {suc n} (there n∈Dom⟨mUm'⟩) with U⁻ {m} {m'} {n} (sucout n∈Dom⟨mUm'⟩)
... | inj₁ ∈Dom-m  = inj₁ (there (sucin ∈Dom-m))
... | inj₂ ∈Dom-m' = inj₂ (there (sucin ∈Dom-m'))
U⁻ {just x ∷ m} {nothing ∷ m'} {zero} n∈Dom⟨mUm'⟩ = inj₁ (here refl)
U⁻ {just x ∷ m} {nothing ∷ m'} {suc n} (here ())
U⁻ {just x ∷ m} {nothing ∷ m'} {suc n} (there n∈Dom⟨mUm'⟩) with U⁻ {m} {m'} {n} (sucout n∈Dom⟨mUm'⟩)
... | inj₁ ∈Dom-m  = inj₁ (there (sucin ∈Dom-m))
... | inj₂ ∈Dom-m' = inj₂ (sucin ∈Dom-m')
U⁻ {nothing ∷ m} {just x ∷ m'} {zero} n∈Dom⟨mUm'⟩ = inj₂ (here refl)
U⁻ {nothing ∷ m} {just x ∷ m'} {suc n} (here ())
U⁻ {nothing ∷ m} {just x ∷ m'} {suc n} (there n∈Dom⟨mUm'⟩) with U⁻ {m} {m'} {n} (sucout n∈Dom⟨mUm'⟩)
... | inj₁ ∈Dom-m  = inj₁ (sucin ∈Dom-m)
... | inj₂ ∈Dom-m' = inj₂ (there (sucin ∈Dom-m'))
U⁻ {nothing ∷ m} {nothing ∷ m'} {zero} n∈Dom⟨mUm'⟩ = ⊥-elim (0∈S n∈Dom⟨mUm'⟩)
U⁻ {nothing ∷ m} {nothing ∷ m'} {suc n} n∈Dom⟨mUm'⟩ with U⁻ {m} {m'} {n} (sucout n∈Dom⟨mUm'⟩)
... | inj₁ ∈Dom-m  = inj₁ (sucin ∈Dom-m)
... | inj₂ ∈Dom-m' = inj₂ (sucin ∈Dom-m')

Dom'-1map : ∀ n v → Dom' (m-insert (just v) n []) ≡ (n ∷ [])
Dom'-1map zero v = refl
Dom'-1map (suc n) v rewrite Dom'-1map n v = refl

Dom'-2map : ∀ n₁ v₁ n₂ v₂ →
  (Dom' (m-insert (just v₂) n₂ (m-insert (just v₁) n₁ [])) ≡ n₁ ∷ [] × n₁ ≡ n₂) ⊎
  (Dom' (m-insert (just v₂) n₂ (m-insert (just v₁) n₁ [])) ≡ n₁ ∷ n₂ ∷ []) ⊎
  (Dom' (m-insert (just v₂) n₂ (m-insert (just v₁) n₁ [])) ≡ n₂ ∷ n₁ ∷ [])
Dom'-2map zero v₁ zero v₂ = inj₁ (refl , refl)
Dom'-2map zero v₁ (suc n₂) v₂ rewrite Dom'-1map n₂ v₂ = inj₂ (inj₁ refl)
Dom'-2map (suc n₁) v₁ zero v₂ rewrite Dom'-1map n₁ v₁ = inj₂ (inj₂ refl)
Dom'-2map (suc n₁) v₁ (suc n₂) v₂ with Dom'-2map n₁ v₁ n₂ v₂
... | inj₁ (xs≡[n₁] , refl)  rewrite xs≡[n₁] = inj₁ (refl , refl)
... | inj₂ (inj₁ xs≡[n₁,n₂]) rewrite xs≡[n₁,n₂] = inj₂ (inj₁ refl)
... | inj₂ (inj₂ xs≡[n₂,n₁]) rewrite xs≡[n₂,n₁] = inj₂ (inj₂ refl)

negsucun : ∀{k k'} → ¬ (suc k ≡ suc k') → k ≡ k' → ⊥
negsucun neg = (λ eq → neg (cong suc eq))

-- sucout : ∀{z n} → (suc z) ∈ (map suc n) → z ∈ n
sucinout : ∀{k nn}
           → (kin : (suc k) ∈ (map suc nn))
           → sucin (sucout kin) ≡ kin
sucinout {zero} {[]} ()
sucinout {zero} {.0 ∷ nn₁} (here refl) = refl
sucinout {zero} {x ∷ nn₁} (there kin) rewrite (sucinout{zero}{nn₁} kin) = refl
sucinout {suc k} {[]} ()
sucinout {suc k} {.(suc k) ∷ nn₁} (here refl) = refl
sucinout {suc k} {x ∷ nn₁} (there kin) rewrite (sucinout{(suc k)}{nn₁} kin) = refl

domzero : ∀{m} → zero ∈ (Dom' (nothing ∷ m)) → ⊥
domzero {[]} = λ ()
domzero {x ∷ m} = 0∈S

domin : ∀{x m z} → x ∈ Dom' m → suc x ∈ Dom' (z ∷ m)
domin {x} {m} {just z}  x∈Dom'm = there (sucin x∈Dom'm)
domin {x} {m} {nothing} x∈Dom'm = sucin x∈Dom'm

n∈Sbij : ∀{k x m} → ∀{kin : (suc k) ∈ (Dom' (x ∷ m))} → ∀{kin2 : (suc k) ∈ (Dom' (x ∷ m))} → (n∈S{k}{x}{m} kin) ≡ (n∈S{k}{x}{m} kin2) → kin ≡ kin2
n∈Sbij {k} {just x} {[]} {here ()} {kin2} eq
n∈Sbij {k} {just x} {[]} {there ()} {kin2} eq
n∈Sbij {k} {just x} {x₁ ∷ m} {here ()} {kin2} eq
n∈Sbij {k} {just x} {x₁ ∷ m} {there kin} {here ()} eq
n∈Sbij {k} {just x} {x₁ ∷ m} {there kin} {there kin2} eq with (cong sucin eq)
... | eq2 rewrite (sucinout kin) | (sucinout kin2) =  cong there eq2
n∈Sbij {k} {nothing} {[]} {kin} {()} eq
n∈Sbij {k} {nothing} {x ∷ m} {kin} {kin2} eq with cong sucin eq
... | eq2 rewrite (sucinout kin) | (sucinout kin2) = eq2

ineq : ∀{k m} → (kin1 : k ∈ (Dom' m)) → (kin2 : k ∈ (Dom' m)) → kin1 ≡ kin2
ineq {zero} {[]} () kin2
ineq {zero} {just x ∷ m} (here refl) (here refl) = refl
ineq {zero} {just x ∷ m} (here refl) (there kin2) = ⊥-elim (0∈S kin2)
ineq {zero} {just x ∷ m} (there kin1) (here px) = ⊥-elim (0∈S kin1)
ineq {zero} {just x ∷ m} (there kin1) (there kin2) = ⊥-elim (0∈S kin1)
ineq {zero} {nothing ∷ m} kin1 kin2 = ⊥-elim (domzero{m} kin1)
ineq {suc k} {[]} () kin2
ineq {suc k} {just x ∷ m} (here ()) (here px₁)
ineq {suc k} {just x ∷ m} (here ()) (there kin2)
ineq {suc k} {just x ∷ m} (there kin1) (here ())
ineq {suc k} {just x ∷ m} (there kin1) (there kin2) rewrite sym (sucinout kin1) | sym (sucinout kin2) = cong there (cong sucin (ineq{k}{m} (sucout kin1) (sucout kin2)))
ineq {suc k} {nothing ∷ m} kin1 kin2 = n∈Sbij{k}{nothing}{m} (ineq{k}{m} (n∈S{x = nothing}{l = m} kin1) (n∈S{x = nothing}{l = m} kin2))

Dom'-comm : ∀ m1 m2 → Dom' (m1 U m2) ≡ Dom' (m2 U m1)
Dom'-comm [] m2 rewrite U-comm {m2} {[]} (λ _ _ ()) = refl
Dom'-comm (x ∷ m1) [] = refl
Dom'-comm (just x ∷ m1) (just x₁ ∷ m2) rewrite Dom'-comm m1 m2 = refl
Dom'-comm (just x ∷ m1) (nothing ∷ m2) rewrite Dom'-comm m1 m2 = refl
Dom'-comm (nothing ∷ m1) (just x ∷ m2) rewrite Dom'-comm m1 m2 = refl
Dom'-comm (nothing ∷ m1) (nothing ∷ m2) rewrite Dom'-comm m1 m2 = refl

Dom'-assoc-comm : ∀ m1 m2 m3 → Dom' ((m1 U m2) U m3) ≡ Dom' ((m1 U m3) U m2)
Dom'-assoc-comm [] m2 m3 = Dom'-comm m2 m3
Dom'-assoc-comm (x ∷ m1) [] m3 rewrite Dom'-comm ((x ∷ m1) U m3) [] = refl
Dom'-assoc-comm (x ∷ m1) (x₁ ∷ m2) [] rewrite Dom'-comm ((x ∷ m1) U (x₁ ∷ m2)) [] = refl
Dom'-assoc-comm (just x ∷ m1) (just x₁ ∷ m2) (just x₂ ∷ m3)
  rewrite Dom'-assoc-comm m1 m2 m3 = refl
Dom'-assoc-comm (just x ∷ m1) (just x₁ ∷ m2) (nothing ∷ m3)
  rewrite Dom'-assoc-comm m1 m2 m3 = refl
Dom'-assoc-comm (just x ∷ m1) (nothing ∷ m2) (just x₁ ∷ m3)
  rewrite Dom'-assoc-comm m1 m2 m3 = refl
Dom'-assoc-comm (just x ∷ m1) (nothing ∷ m2) (nothing ∷ m3)
  rewrite Dom'-assoc-comm m1 m2 m3 = refl
Dom'-assoc-comm (nothing ∷ m1) (just x ∷ m2) (just x₁ ∷ m3)
  rewrite Dom'-assoc-comm m1 m2 m3 = refl
Dom'-assoc-comm (nothing ∷ m1) (just x ∷ m2) (nothing ∷ m3)
  rewrite Dom'-assoc-comm m1 m2 m3 = refl
Dom'-assoc-comm (nothing ∷ m1) (nothing ∷ m2) (just x ∷ m3)
  rewrite Dom'-assoc-comm m1 m2 m3 = refl
Dom'-assoc-comm (nothing ∷ m1) (nothing ∷ m2) (nothing ∷ m3)
  rewrite Dom'-assoc-comm m1 m2 m3 = refl

putputget-neq : ∀{k k' m v v'}
                → ¬ (k ≡ k')
                → (kin : k ∈ (Dom' m))
                → (kin2 : k ∈ (Dom' (m-insert (just v') k' m)))
                → (deref k m kin) ≡ v
                → (deref k (m-insert (just v') k' m) kin2) ≡ v
putputget-neq {k} {k'} {[]} neq () kin2 eq
putputget-neq {.0} {zero} {just x ∷ m} neq (here refl) (here refl) refl = ⊥-elim (neq refl)
putputget-neq {.0} {zero} {just x ∷ m} neq (here refl) (there kin2) refl = ⊥-elim (neq refl)
putputget-neq {.0} {suc k'} {just x ∷ m} neq (here refl) (here refl) refl = refl
putputget-neq {.0} {suc k'} {just x ∷ m} neq (here refl) (there kin2) refl = refl
putputget-neq {zero} {zero} {just x ∷ m} neq (there kin) kin2 refl = ⊥-elim (neq refl)

putputget-neq {zero} {suc k'} {just x ∷ m} neq (there kin) kin2 refl = refl
putputget-neq {suc k} {suc k'} {just x ∷ m} neq (there kin) (here ()) refl
putputget-neq {zero} {zero} {nothing ∷ m} neq kin kin2 refl = ⊥-elim (neq refl)

putputget-neq {zero} {suc k'} {nothing ∷ m} neq kin kin2 refl = ⊥-elim (0∈S kin)
putputget-neq {suc k} {zero} {nothing ∷ m} neq kin (here ()) refl

putputget-neq {suc k} {suc k'} {just x ∷ m} {v} {v'} neq (there kin) (there kin2) refl
  rewrite ineq{k}{m-insert (just v') k' m} (n∈S{k}{just x}{m-insert (just v') k' m} (there kin2)) (sucout kin2)
     =  putputget-neq{k}{k'}{m}{v}{v'} (negsucun neq) (sucout kin) (sucout kin2) (deref-∈-irr{k}{m} (sucout kin) (n∈S{k}{x = just x}{m} (there kin)))
putputget-neq {suc k} {suc k'} {nothing ∷ m} {v} {v'} neq kin kin2 refl
  rewrite ineq{k}{m-insert (just v') k' m} (n∈S{k}{nothing}{m-insert (just v') k' m} kin2) (sucout kin2)
    = putputget-neq{k}{k'}{m} (negsucun neq) (sucout kin) ((sucout kin2)) ((deref-∈-irr{k}{m} (sucout kin) (n∈S{k}{x = nothing}{m} kin)))

putputget-neq {suc k} {zero} {x ∷ m} {v} {v'} neq kin kin2 refl =  deref-∈-irr{k}{m} (n∈S{k}{just v'}{m} kin2) (n∈S{k}{x}{m} kin)

putputget-eq : ∀ k m v v' →
               (m-insert (just v') k (m-insert (just v) k m))
                ≡ (m-insert (just v') k m)
putputget-eq zero [] v v' = refl
putputget-eq zero (x ∷ m) v v' = refl
putputget-eq (suc k) [] v v' rewrite putputget-eq k [] v v' = refl
putputget-eq (suc k) (x ∷ m) v v'  rewrite putputget-eq k m v v' = refl

getput : ∀ k m →
  (kin : k ∈ Dom' m) →
  m ≡ m-insert (just (deref k m kin)) k m
getput k [] ()
getput zero    (just x ∷ m)  kin = refl
getput zero    (nothing ∷ m) kin = ⊥-elim (0∈S kin)
getput (suc k) (just x ∷ m)  kin
  rewrite sym (getput k m (n∈S {k} {just x} {m} kin))
  = refl
getput (suc k) (nothing ∷ m) kin
  rewrite sym (getput k m (n∈S {k} {nothing} {m} kin))
  = refl

putget : ∀{k m v} → (kin : k ∈ (Dom' (m-insert (just v) k m))) → (deref k (m-insert (just v) k m) kin) ≡ v
putget {zero} {[]} {v} (here refl) = refl
putget {zero} {[]} {v} (there ())
putget {zero} {just x ∷ m} {v} (here refl) = refl
putget {zero} {just x ∷ m} {v} (there kin) = refl
putget {zero} {nothing ∷ m} {v} kin = refl
putget {suc k} {[]} {v} kin = putget{k}{[]}{v} ( (n∈S{k}{nothing}{(m-insert (just v) k [])} kin) )
putget {suc k} {x ∷ m} {v} kin = putget{k}{m}{v} ( (n∈S{k}{x} kin) )

put-U-U : ∀ k v m1 m2 →
  (kin1 : k ∈ Dom' m1) →
  (kin2 : k ∈ Dom' m2) →
    m1 U m2 ≡
    (m-insert (just v) k m1) U m2
put-U-U k v [] m2 () kin2
put-U-U k v (x ∷ m1) [] kin1 ()
put-U-U zero v (just x ∷ m1) (just x₁ ∷ m2) kin1 kin2 = refl
put-U-U zero v (just x ∷ m1) (nothing ∷ m2) kin1 kin2 = ⊥-elim (0∈S kin2)
put-U-U zero v (nothing ∷ m1) (x₁ ∷ m2) kin1 kin2 = ⊥-elim (0∈S kin1)
put-U-U (suc k) v (just x ∷ m1) (just x₁ ∷ m2) kin1 kin2
  rewrite put-U-U k v m1 m2
                  (n∈S {k} {just x}  {m1} kin1)
                  (n∈S {k} {just x₁} {m2} kin2)
  = refl
put-U-U (suc k) v (just x ∷ m1) (nothing ∷ m2) kin1 kin2
  rewrite put-U-U k v m1 m2
                  (n∈S {k} {just x}  {m1} kin1)
                  (n∈S {k} {nothing} {m2} kin2)
  = refl
put-U-U (suc k) v (nothing ∷ m1) (just x ∷ m2) kin1 kin2
  rewrite put-U-U k v m1 m2
                  (n∈S {k} {nothing} {m1} kin1)
                  (n∈S {k} {just x}  {m2} kin2)
  = refl
put-U-U (suc k) v (nothing ∷ m1) (nothing ∷ m2) kin1 kin2
  rewrite put-U-U k v m1 m2
                  (n∈S {k} {nothing} {m1} kin1)
                  (n∈S {k} {nothing} {m2} kin2)
  = refl

insert-comm : ∀{m k1 k2 v1 v2}
              → ¬ k1 ≡ k2
              → (m-insert (just v2) k2 (m-insert (just v1) k1 m)) ≡ (m-insert (just v1) k1 (m-insert (just v2) k2 m))
insert-comm {m} {zero} {zero} {v1} {v2} ¬k1≡k2 = ⊥-elim (¬k1≡k2 refl)
insert-comm {[]} {zero} {suc k2} {v1} {v2} ¬k1≡k2 = refl
insert-comm {x ∷ m} {zero} {suc k2} {v1} {v2} ¬k1≡k2 = refl
insert-comm {[]} {suc k1} {zero} {v1} {v2} ¬k1≡k2 = refl
insert-comm {x ∷ m} {suc k1} {zero} {v1} {v2} ¬k1≡k2 = refl
insert-comm {[]} {suc k1} {suc k2} {v1} {v2} ¬k1≡k2 rewrite insert-comm{[]}{k1}{k2}{v1}{v2} (λ x → ¬k1≡k2 (cong suc x)) = refl
insert-comm {x ∷ m} {suc k1} {suc k2} {v1} {v2} ¬k1≡k2 rewrite insert-comm{m}{k1}{k2}{v1}{v2} (λ x → ¬k1≡k2 (cong suc x)) = refl

insert-U-comm : ∀ k v m m' →
  ¬ (k ∈ Dom' m') →
    (m-insert (just v) k m) U m' ≡
    m-insert (just v) k (m U m')
insert-U-comm k v m [] k∉Domm'
  rewrite U-comm {m} {[]} (λ _ _ ())
        | U-comm {m-insert (just v) k m} {[]} (λ _ _ ())
  = refl
insert-U-comm zero v [] (just x ∷ m') k∉Domm' = ⊥-elim (k∉Domm' (here refl))
insert-U-comm zero v [] (nothing ∷ m') k∉Domm' = refl
insert-U-comm (suc k) v [] (just x ∷ m') k∉Domm'
  rewrite insert-U-comm k v [] m' (k∉Domm' ∘ there ∘ sucin {k} {Dom' m'})
  = refl
insert-U-comm (suc k) v [] (nothing ∷ m') k∉Domm'
  rewrite insert-U-comm k v [] m' (k∉Domm' ∘ sucin {k} {Dom' m'})
  = refl
insert-U-comm zero v (x ∷ m) (just x₁ ∷ m') k∉Domm' = ⊥-elim (k∉Domm' (here refl))
insert-U-comm zero v (just x ∷ m)  (nothing ∷ m') k∉Domm' = refl
insert-U-comm zero v (nothing ∷ m) (nothing ∷ m') k∉Domm' = refl
insert-U-comm (suc k) v (just x ∷ m)  (just x₁ ∷ m') k∉Domm'
  rewrite insert-U-comm k v m m' (k∉Domm' ∘ there ∘ sucin {k} {Dom' m'})
  = refl
insert-U-comm (suc k) v (nothing ∷ m) (just x₁ ∷ m') k∉Domm'
  rewrite insert-U-comm k v m m' (k∉Domm' ∘ there ∘ sucin {k} {Dom' m'})
  = refl
insert-U-comm (suc k) v (just x ∷ m)  (nothing ∷ m') k∉Domm'
  rewrite insert-U-comm k v m m' (k∉Domm' ∘ sucin {k} {Dom' m'})
  = refl
insert-U-comm (suc k) v (nothing ∷ m) (nothing ∷ m') k∉Domm'
  rewrite insert-U-comm k v m m' (k∉Domm' ∘ sucin {k} {Dom' m'})
  = refl

deref-U-right-irr : ∀ k m1 m2 kin12 kin2 →
  deref k (m1 U m2) kin12 ≡ deref k m2 kin2
deref-U-right-irr k m1 [] kin12 ()
deref-U-right-irr zero [] (x ∷ m2) kin12 kin2 = deref-∈-irr {l = x ∷ m2} kin12 kin2
deref-U-right-irr zero (just x ∷ m1)  (just x₁ ∷ m2) kin12 kin2 = refl
deref-U-right-irr zero (nothing ∷ m1) (just x ∷ m2)  kin12 kin2 = refl
deref-U-right-irr zero (x ∷ m1)       (nothing ∷ m2) kin12 kin2 = ⊥-elim (0∈S kin2)
deref-U-right-irr (suc k) [] (x ∷ m2) kin12 kin2 =
  deref-∈-irr {l = m2} (n∈S {k} {x} kin12) (n∈S {k} {x} kin2)
deref-U-right-irr (suc k) (just x ∷ m1)  (just x₁ ∷ m2) kin12 kin2 =
  deref-U-right-irr k m1 m2
    (n∈S {k} {just x₁} {m1 U m2} kin12) (n∈S {k} {just x₁} {m2} kin2)
deref-U-right-irr (suc k) (nothing ∷ m1) (just x₁ ∷ m2) kin12 kin2 =
  deref-U-right-irr k m1 m2
    (n∈S {k} {just x₁} {m1 U m2} kin12) (n∈S {k} {just x₁} {m2} kin2)
deref-U-right-irr (suc k) (just x ∷ m1)  (nothing ∷ m2) kin12 kin2 =
  deref-U-right-irr k m1 m2
    (n∈S {k} {just x} {m1 U m2} kin12) (n∈S {k} {nothing} {m2} kin2)
deref-U-right-irr (suc k) (nothing ∷ m1) (nothing ∷ m2) kin12 kin2 =
  deref-U-right-irr k m1 m2
    (n∈S {k} {nothing} {m1 U m2} kin12) (n∈S {k} {nothing} {m2} kin2)

∈-deref-U-irr : ∀ k m1 m2 m3 kin1 kin2 →
  (kin3 : k ∈ Dom' m3) →
  deref k (m1 U m3) kin1 ≡ deref k (m2 U m3) kin2
∈-deref-U-irr k [] m2 m3 kin1 kin2 kin3
  rewrite deref-U-right-irr k m2 m3 kin2 kin3
  = deref-∈-irr kin1 kin3
∈-deref-U-irr k (x ∷ m1) [] m3 kin1 kin2 kin3
  rewrite deref-U-right-irr k (x ∷ m1) m3 kin1 kin3
  = deref-∈-irr kin3 kin2
∈-deref-U-irr k (x ∷ m1) (x₁ ∷ m2) [] kin1 kin2 ()
∈-deref-U-irr zero (x ∷ m1) (x₁ ∷ m2) (nothing ∷ m3) kin1 kin2 kin3 = ⊥-elim (0∈S kin3)
∈-deref-U-irr zero (just x ∷ m1)  (just x₁ ∷ m2) (just x₂ ∷ m3) kin1 kin2 kin3 = refl
∈-deref-U-irr zero (just x ∷ m1)  (nothing ∷ m2) (just x₂ ∷ m3) kin1 kin2 kin3 = refl
∈-deref-U-irr zero (nothing ∷ m1) (just x₁ ∷ m2) (just x₂ ∷ m3) kin1 kin2 kin3 = refl
∈-deref-U-irr zero (nothing ∷ m1) (nothing ∷ m2) (just x₂ ∷ m3) kin1 kin2 kin3 = refl
∈-deref-U-irr (suc k) (just x ∷ m1) (just x₁ ∷ m2) (just x₂ ∷ m3) kin1 kin2 kin3 =
  ∈-deref-U-irr k m1 m2 m3
    (n∈S {k} {just x₂} {m1 U m3} kin1)
    (n∈S {k} {just x₂} {m2 U m3} kin2)
    (n∈S {k} {just x₂} {m3} kin3)
∈-deref-U-irr (suc k) (just x ∷ m1) (nothing ∷ m2) (just x₂ ∷ m3) kin1 kin2 kin3 =
  ∈-deref-U-irr k m1 m2 m3
    (n∈S {k} {just x₂} {m1 U m3} kin1)
    (n∈S {k} {just x₂} {m2 U m3} kin2)
    (n∈S {k} {just x₂} {m3} kin3)
∈-deref-U-irr (suc k) (nothing ∷ m1) (just x₁ ∷ m2) (just x₂ ∷ m3) kin1 kin2 kin3 =
  ∈-deref-U-irr k m1 m2 m3
    (n∈S {k} {just x₂} {m1 U m3} kin1)
    (n∈S {k} {just x₂} {m2 U m3} kin2)
    (n∈S {k} {just x₂} {m3} kin3)
∈-deref-U-irr (suc k) (nothing ∷ m1) (nothing ∷ m2) (just x₂ ∷ m3) kin1 kin2 kin3 =
  ∈-deref-U-irr k m1 m2 m3
    (n∈S {k} {just x₂} {m1 U m3} kin1)
    (n∈S {k} {just x₂} {m2 U m3} kin2)
    (n∈S {k} {just x₂} {m3} kin3)
∈-deref-U-irr (suc k) (just x ∷ m1) (just x₁ ∷ m2) (nothing ∷ m3) kin1 kin2 kin3 =
  ∈-deref-U-irr k m1 m2 m3
    (n∈S {k} {just x} {m1 U m3} kin1)
    (n∈S {k} {just x₁} {m2 U m3} kin2)
    (n∈S {k} {nothing} {m3} kin3)
∈-deref-U-irr (suc k) (just x ∷ m1) (nothing ∷ m2) (nothing ∷ m3) kin1 kin2 kin3 =
  ∈-deref-U-irr k m1 m2 m3
    (n∈S {k} {just x} {m1 U m3} kin1)
    (n∈S {k} {nothing} {m2 U m3} kin2)
    (n∈S {k} {nothing} {m3} kin3)
∈-deref-U-irr (suc k) (nothing ∷ m1) (just x₁ ∷ m2) (nothing ∷ m3) kin1 kin2 kin3 =
  ∈-deref-U-irr k m1 m2 m3
    (n∈S {k} {nothing} {m1 U m3} kin1)
    (n∈S {k} {just x₁} {m2 U m3} kin2)
    (n∈S {k} {nothing} {m3} kin3)
∈-deref-U-irr (suc k) (nothing ∷ m1) (nothing ∷ m2) (nothing ∷ m3) kin1 kin2 kin3 =
  ∈-deref-U-irr k m1 m2 m3
    (n∈S {k} {nothing} {m1 U m3} kin1)
    (n∈S {k} {nothing} {m2 U m3} kin2)
    (n∈S {k} {nothing} {m3} kin3)

deref-U-both-irr : ∀ k m1 m2 m3 kin1 kin2 kin13 kin23 →
  deref k m1 kin1 ≡ deref k m2 kin2 →
  deref k (m1 U m3) kin13 ≡ deref k (m2 U m3) kin23
deref-U-both-irr k [] m2 m3 () kin2 kin13 kin23 m1[k]≡m2[k]
deref-U-both-irr k (x ∷ m1) [] m3 kin1 () kin13 kin23 m1[k]≡m2[k]
deref-U-both-irr k (x ∷ m1) (x₁ ∷ m2) [] kin1 kin2 kin13 kin23 m1[k]≡m2[k]
  rewrite deref-∈-irr {l = x ∷ m1} kin13 kin1 | deref-∈-irr {l = x₁ ∷ m2} kin23 kin2
  = m1[k]≡m2[k]
deref-U-both-irr zero (just x ∷ m1)  (just x₁ ∷ m2) (nothing ∷ m3) _ _ _ _ m1[k]≡m2[k] = m1[k]≡m2[k]
deref-U-both-irr zero (just x ∷ m1)  (just x₁ ∷ m2) (just x₂ ∷ m3) _    _ _ _ _ = refl
deref-U-both-irr zero (just x ∷ m1)  (nothing ∷ m2) (just x₂ ∷ m3) _    _ _ _ _ = refl
deref-U-both-irr zero (nothing ∷ m1) (just x ∷ m2)  (just x₂ ∷ m3) _    _ _ _ _ = refl
deref-U-both-irr zero (nothing ∷ m1) (nothing ∷ m2) (just x₂ ∷ m3) _    _ _ _ _ = refl
deref-U-both-irr zero (nothing ∷ m1) (just x ∷ m2)  (nothing ∷ m3) kin1 _ _ _ _ = ⊥-elim (0∈S kin1)
deref-U-both-irr zero (nothing ∷ m1) (nothing ∷ m2) (nothing ∷ m3) kin1 _ _ _ _ = ⊥-elim (0∈S kin1)
deref-U-both-irr zero (just x ∷ m1)  (nothing ∷ m2) (nothing ∷ m3) _ kin2 _ _ _ = ⊥-elim (0∈S kin2)
deref-U-both-irr (suc k) (just x ∷ m1) (just x₁ ∷ m2) (just x₂ ∷ m3)
               kin1 kin2 kin13 kin23 m1[k]≡m2[k] =
  deref-U-both-irr k m1 m2 m3
    (n∈S {k} {just x} {m1} kin1) (n∈S {k} {just x₁} {m2} kin2)
    (n∈S {k} {just x₂} {m1 U m3} kin13) (n∈S {k} {just x₂} {m2 U m3} kin23)
    m1[k]≡m2[k]
deref-U-both-irr (suc k) (just x ∷ m1) (nothing ∷ m2) (just x₂ ∷ m3)
               kin1 kin2 kin13 kin23 m1[k]≡m2[k] =
  deref-U-both-irr k m1 m2 m3
    (n∈S {k} {just x} {m1} kin1) (n∈S {k} {nothing} {m2} kin2)
    (n∈S {k} {just x₂} {m1 U m3} kin13) (n∈S {k} {just x₂} {m2 U m3} kin23)
    m1[k]≡m2[k]
deref-U-both-irr (suc k) (nothing ∷ m1) (just x ∷ m2) (just x₂ ∷ m3)
               kin1 kin2 kin13 kin23 m1[k]≡m2[k] =
  deref-U-both-irr k m1 m2 m3
    (n∈S {k} {nothing} {m1} kin1) (n∈S {k} {just x} {m2} kin2)
    (n∈S {k} {just x₂} {m1 U m3} kin13) (n∈S {k} {just x₂} {m2 U m3} kin23)
    m1[k]≡m2[k]
deref-U-both-irr (suc k) (nothing ∷ m1) (nothing ∷ m2) (just x₂ ∷ m3)
               kin1 kin2 kin13 kin23 m1[k]≡m2[k] =
  deref-U-both-irr k m1 m2 m3
    (n∈S {k} {nothing} {m1} kin1) (n∈S {k} {nothing} {m2} kin2)
    (n∈S {k} {just x₂} {m1 U m3} kin13) (n∈S {k} {just x₂} {m2 U m3} kin23)
    m1[k]≡m2[k]
deref-U-both-irr (suc k) (just x ∷ m1) (just x₁ ∷ m2) (nothing ∷ m3)
               kin1 kin2 kin13 kin23 m1[k]≡m2[k] =
  deref-U-both-irr k m1 m2 m3
    (n∈S {k} {just x} {m1} kin1) (n∈S {k} {just x₁} {m2} kin2)
    (n∈S {k} {just x} {m1 U m3} kin13) (n∈S {k} {just x₁} {m2 U m3} kin23)
    m1[k]≡m2[k]
deref-U-both-irr (suc k) (just x ∷ m1) (nothing ∷ m2) (nothing ∷ m3)
               kin1 kin2 kin13 kin23 m1[k]≡m2[k] =
  deref-U-both-irr k m1 m2 m3
    (n∈S {k} {just x} {m1} kin1) (n∈S {k} {nothing} {m2} kin2)
    (n∈S {k} {just x} {m1 U m3} kin13) (n∈S {k} {nothing} {m2 U m3} kin23)
    m1[k]≡m2[k]
deref-U-both-irr (suc k) (nothing ∷ m1) (just x ∷ m2) (nothing ∷ m3)
               kin1 kin2 kin13 kin23 m1[k]≡m2[k] =
  deref-U-both-irr k m1 m2 m3
    (n∈S {k} {nothing} {m1} kin1) (n∈S {k} {just x} {m2} kin2)
    (n∈S {k} {nothing} {m1 U m3} kin13) (n∈S {k} {just x} {m2 U m3} kin23)
    m1[k]≡m2[k]
deref-U-both-irr (suc k) (nothing ∷ m1) (nothing ∷ m2) (nothing ∷ m3)
               kin1 kin2 kin13 kin23 m1[k]≡m2[k] =
  deref-U-both-irr k m1 m2 m3
    (n∈S {k} {nothing} {m1} kin1) (n∈S {k} {nothing} {m2} kin2)
    (n∈S {k} {nothing} {m1 U m3} kin13) (n∈S {k} {nothing} {m2 U m3} kin23)
    m1[k]≡m2[k]

U-domout : ∀ {k m₁ m₂} x x₁ → suc k ∈ Dom' ((x ∷ m₁) U (x₁ ∷ m₂)) → k ∈ Dom' (m₁ U m₂)
U-domout (just x₂) (just x₃) (here ())
U-domout (just x₂) (just x₃) (there suck∈'') = sucout suck∈''
U-domout (just x₂) nothing (here ())
U-domout (just x₂) nothing (there suck∈'') = sucout suck∈''
U-domout nothing (just x₂) (here ())
U-domout nothing (just x₂) (there suck∈'') = sucout suck∈''
U-domout nothing   nothing   suck∈'' = sucout suck∈''

U-∉-irr-get-help-help : ∀ k x x₁ m₁ m₂ →
  (k∈ : suc k ∈ Dom' ((x ∷ m₁) U (x₁ ∷ m₂))) →
  deref (suc k) ((x ∷ m₁) U (x₁ ∷ m₂)) k∈ ≡
    deref k (m₁ U m₂) (U-domout x x₁ k∈)
U-∉-irr-get-help-help k (just x) (just x₁) m₁ m₂ (here ())
U-∉-irr-get-help-help k (just x) (just x₁) m₁ m₂ (there k∈) = deref-∈-irr {k} {m₁ U m₂} (n∈S {k} {just x₁} {m₁ U m₂} (there k∈)) (sucout k∈)
U-∉-irr-get-help-help k (just x) nothing m₁ m₂ (here ())
U-∉-irr-get-help-help k (just x) nothing m₁ m₂ (there k∈) = deref-∈-irr {k} {m₁ U m₂} (n∈S {k} {just x} {m₁ U m₂} (there k∈)) (sucout k∈)
U-∉-irr-get-help-help k nothing (just x) m₁ m₂ (here ())
U-∉-irr-get-help-help k nothing (just x) m₁ m₂ (there k∈) = deref-∈-irr {k} {m₁ U m₂} (n∈S {k} {just x} {m₁ U m₂} (there k∈)) (sucout k∈)
U-∉-irr-get-help-help k nothing nothing m₁ m₂ k∈ =  deref-∈-irr {k} {m₁ U m₂} (n∈S {k} {nothing} {m₁ U m₂} k∈) (sucout k∈)

U-∉-irr-get-help : ∀ {m₁ m₂ k} → (k∈ : k ∈ Dom' m₁) → k ∉ Dom' m₂ → (k∈' : k ∈ Dom' (m₁ U m₂))  → deref k m₁ k∈ ≡ deref k (m₁ U m₂) k∈'
U-∉-irr-get-help {[]} {[]} {k} () k∉Dom⟨m₂⟩ k∈Dom⟨m₁Um₂⟩
U-∉-irr-get-help {[]} {x ∷ m₂} {k} () k∉Dom⟨m₂⟩ k∈Dom⟨m₁Um₂⟩
U-∉-irr-get-help {x ∷ m₁} {[]}      {k} k∈Dom⟨m₁⟩ k∉Dom⟨m₂⟩ k∈Dom⟨m₁Um₂⟩
  rewrite deref-∈-irr {k} {x ∷ m₁} k∈Dom⟨m₁⟩ k∈Dom⟨m₁Um₂⟩ = refl
U-∉-irr-get-help {x ∷ m₁} {just x₁ ∷ m₂} {zero} k∈Dom⟨m₁⟩ k∉Dom⟨m₂⟩ k∈Dom⟨m₁Um₂⟩ = ⊥-elim (k∉Dom⟨m₂⟩ (here refl))
U-∉-irr-get-help {just x ∷ m₁}  {nothing ∷ m₂} {zero} k∈Dom⟨m₁⟩ k∉Dom⟨m₂⟩ k∈Dom⟨m₁Um₂⟩ = refl
U-∉-irr-get-help {nothing ∷ m₁} {nothing ∷ m₂} {zero} k∈Dom⟨m₁⟩ k∉Dom⟨m₂⟩ k∈Dom⟨m₁Um₂⟩ = ⊥-elim (0∈S k∈Dom⟨m₁⟩)
U-∉-irr-get-help {x ∷ m₁} {x₁ ∷ m₂} {suc k} k∈Dom⟨m₁⟩ k∉Dom⟨m₂⟩ k∈Dom⟨m₁Um₂⟩
  with U-∉-irr-get-help {m₁} {m₂} {k} (n∈S {k} {x} k∈Dom⟨m₁⟩) (λ x₂ → k∉Dom⟨m₂⟩ (domin {k} {m₂} {x₁} x₂)) (U-domout x x₁ k∈Dom⟨m₁Um₂⟩)
... | eq rewrite U-∉-irr-get-help-help k x x₁ m₁ m₂ k∈Dom⟨m₁Um₂⟩ = eq

U-∉-irr-get : ∀ {m₁ m₂ k} → (k∈ : k ∈ Dom' m₁) → k ∉ Dom' m₂ → ∃ λ k∈' → deref k m₁ k∈ ≡ deref k (m₁ U m₂) k∈'
U-∉-irr-get {m₁} {m₂} {k} k∈Dom'm₁ k∉Dom'm₂ =
  _ , U-∉-irr-get-help {m₁} {m₂} {k} k∈Dom'm₁ k∉Dom'm₂ (U-mono {m₁} {m₂} k∈Dom'm₁)

split : ∀{a b : ℕ} {l l'} → a ∷ l ≡ b ∷ l' → ((_≡_ a b) × (_≡_ l l'))
split refl = refl , refl

mapsuc≡ : ∀{l l'} → map suc l ≡ map suc l' → l ≡ l'
mapsuc≡ {[]} {[]} eq = refl
mapsuc≡ {[]} {x ∷ l'} ()
mapsuc≡ {x ∷ l} {[]} ()
mapsuc≡ {x ∷ l} {x₁ ∷ l'} eq with split eq
... | (a , b) rewrite (suc≡ a) | (mapsuc≡ b) = refl

U-=-irr : ∀ m m' → (Dom' m) ≡ (Dom' m') → ∀ k k∈ k∈2 → deref k (m U m') k∈ ≡ deref k m' k∈2
U-=-irr [] [] eq k ()
U-=-irr [] (nothing ∷ m') eq k x  with (subst (λ l → k ∈ l) (sym eq) x)
... | ()
U-=-irr [] (just x ∷ m') ()
U-=-irr (just x ∷ m) [] ()
U-=-irr (nothing ∷ m) [] eq k k∈ ()
U-=-irr (just x ∷ m) (nothing ∷ m') eq = ⊥-elim (0∈S{Dom' m'} (subst (λ x → 0 ∈ x) eq ((0 ∈ (0 ∷ (map suc (Dom' m)))) ∋ (here refl))))
U-=-irr (nothing ∷ m) (just x ∷ m') eq = ⊥-elim (0∈S{Dom' m} (subst (λ x → 0 ∈ x) (sym eq) ((0 ∈ (0 ∷ (map suc (Dom' m')))) ∋ (here refl))))
U-=-irr (nothing ∷ m) (nothing ∷ m') eq zero k∈ k∈2 = ⊥-elim (0∈S k∈)
U-=-irr (nothing ∷ m) (nothing ∷ m') eq (suc k) k∈ k∈2 = U-=-irr m m' (mapsuc≡ eq) k (n∈S{k}{nothing}{m U m'} k∈) (n∈S{k}{nothing}{m'} k∈2)
U-=-irr (just x ∷ m) (just x₁ ∷ m') eq zero = λ k∈ k∈2 → refl
U-=-irr (just x ∷ m) (just x₁ ∷ m') eq (suc k) k∈ k∈2 = U-=-irr m m' ((mapsuc≡ (proj₂ (split eq)))) k (n∈S{k}{just x₁}{m U m'} k∈) (n∈S{k}{just x₁}{m'} k∈2)

U-single-overwrite : ∀ k v1 v2 → (m-insert (just v1) k [])  U  (m-insert (just v2) k []) ≡ (m-insert (just v2) k [])
U-single-overwrite zero v1 v2 = refl
U-single-overwrite (suc k) v1 v2 rewrite U-single-overwrite k v1 v2 = refl

set-single-val : ∀ k v k∈ → (deref k (m-insert (just v) k []) k∈) ≡ v
set-single-val zero v (here refl) = refl
set-single-val zero v (there ())
set-single-val (suc k) v k∈ = set-single-val k v (n∈S{k}{nothing}{(m-insert (just v) k [])} k∈)

overwrite-single-val : ∀ k v m → k ∈ (Dom' m) → ((m-insert (just v) k []) U m) ≡ m
overwrite-single-val zero v [] ()
overwrite-single-val zero v (just x ∷ m) k∈ = refl
overwrite-single-val zero v (nothing ∷ m) k∈ = ⊥-elim (0∈S k∈)
overwrite-single-val (suc k) v [] ()
overwrite-single-val (suc k) v (just x ∷ m) (here ())
overwrite-single-val (suc k) v (just x ∷ m) (there k∈) rewrite overwrite-single-val k v m (sucout k∈) = refl
overwrite-single-val (suc k) v (nothing ∷ m) k∈ rewrite overwrite-single-val k v m (sucout k∈) = refl

U-insert-eq : ∀ k v m → (m-insert (just v) k m) ≡ m U (m-insert (just v) k [])
U-insert-eq zero v [] = refl
U-insert-eq zero v (just x ∷ m) rewrite U-comm{m}{[]} (λ z _ → λ ()) = refl
U-insert-eq zero v (nothing ∷ m) rewrite U-comm{m}{[]} (λ z _ → λ ()) = refl
U-insert-eq (suc k) v [] = refl
U-insert-eq (suc k) v (just x ∷ m) rewrite U-insert-eq k v m = refl
U-insert-eq (suc k) v (nothing ∷ m) rewrite U-insert-eq k v m = refl

insert-domain-eq : ∀ k v m → k ∈ (Dom' m) → (Dom' m) ≡ (Dom' (m-insert (just v) k m))
insert-domain-eq zero v [] ()
insert-domain-eq zero v (just x ∷ m) k∈ = refl
insert-domain-eq zero v (nothing ∷ m) k∈ = ⊥-elim (0∈S k∈)
insert-domain-eq (suc k) v [] ()
insert-domain-eq (suc k) v (just x ∷ m) (here ())
insert-domain-eq (suc k) v (just x ∷ m) (there k∈) rewrite insert-domain-eq k v m (sucout k∈) = refl
insert-domain-eq (suc k) v (nothing ∷ m) k∈ rewrite insert-domain-eq k v m (sucout k∈) = refl

insert-in-domain : ∀ m k v → k ∈ (Dom' (m-insert (just v) k m))
insert-in-domain [] zero v = here refl
insert-in-domain (x ∷ m) zero v = here refl
insert-in-domain [] (suc k) v = sucin (insert-in-domain [] k v)
insert-in-domain (x ∷ m) (suc k) v = domin{z = x} (insert-in-domain m k v)

insert-one-eq : ∀ k v k∈ → (deref k (m-insert (just v) k []) k∈) ≡ v
insert-one-eq zero v k∈ = refl
insert-one-eq (suc k) v k∈ = insert-one-eq k v (n∈S{k}{nothing}{(m-insert (just v) k [])} k∈)

insert-two-eq-one : ∀ k1 k2 v k1∈ → (deref k1 (m-insert (just v) k2 (m-insert (just v) k1 [])) k1∈) ≡ v
insert-two-eq-one zero zero v k1∈ = refl
insert-two-eq-one zero (suc k2) v k1∈ = refl
insert-two-eq-one (suc k1) zero v k1∈
  = insert-one-eq k1 v ( (n∈S{k1}{just v}{l = (m-insert (just v) k1 [])} k1∈) )
insert-two-eq-one (suc k1) (suc k2) v k1∈
     = insert-two-eq-one k1 k2 v ( (n∈S{k1}{nothing}{(m-insert (just v) k2 (m-insert (just v) k1 []))} k1∈))

insert-two-eq-two : ∀ k1 k2 v k2∈ → (deref k2 (m-insert (just v) k2 (m-insert (just v) k1 [])) k2∈) ≡ v
insert-two-eq-two zero zero v k2∈ = refl
insert-two-eq-two zero (suc k2) v k2∈
  = insert-one-eq k2 v ( (n∈S{k2}{just v}{l = (m-insert (just v) k2 [])} k2∈) )
insert-two-eq-two (suc k1) zero v k2∈ = refl
insert-two-eq-two (suc k1) (suc k2) v k2∈
     = insert-two-eq-two k1 k2 v ( (n∈S{k2}{nothing}{(m-insert (just v) k2 (m-insert (just v) k1 []))} k2∈))

data AllMap {i} (F : Value → Set i) : LMap → Set i where
  all : ∀ (m : LMap) → All (Data.Maybe.All F) m → AllMap F m

allMap : ∀ {i} m → {F : Value → Set i} → U.Decidable F → Dec (AllMap F m)
allMap m {F} dec with All.all (Data.Maybe.allDec dec)  m
allMap m {F} dec | yes p = yes (all m p)
allMap m {F} dec | no ¬p = no (λ { (all _ x) → ¬p x})

-- All.all (Data.Maybe.allDec F) m

deref∈ : ∀ {m} → (n : ℕ) → (n∈ : n ∈ (Dom' m)) → just (deref n m n∈) ∈ m
deref∈ {[]} zero ()
deref∈ {[]} (suc n) ()
deref∈ {just x ∷ m} zero n∈ = here refl
deref∈ {nothing ∷ m} zero n∈ = ⊥-elim (0∈S n∈)
deref∈ {just x ∷ m} (suc n) n∈ = there (deref∈ n (n∈S{n}{just x}{m} n∈))
deref∈ {nothing ∷ m} (suc n) n∈ = there (deref∈ n (n∈S{n}{nothing}{m} n∈))

allMap-lookup : ∀ {i} {F : Value → Set i} {m} → AllMap F m → ∀ (n : ℕ) → (k∈ : n ∈ (Dom' m)) → (F (deref n m k∈ ))
allMap-lookup {i} {F} (all m x) n n∈ with (All.lookup x) {just (deref n m n∈)} (deref∈ n n∈)
allMap-lookup {i} {F} (all m x) n n∈ | just px = px

allMap-tabulate : ∀ {i} {F : Value → Set i} {m} → (∀ (n : ℕ) → (k∈ : n ∈ (Dom' m)) → (F (deref n m k∈ ))) → AllMap F m
allMap-tabulate {m = []} FF = all [] All.[]
allMap-tabulate {F = F} {m = just x ∷ m} FF
   with allMap-tabulate{F = F} {m = m} (λ n k∈ →  subst F (deref-∈-irr (n∈S{n}{just x}{m} (there (sucin k∈))) k∈) (FF (suc n) (n+1∈S{n}{just x}{m} k∈)))
allMap-tabulate {_} {F} {just x ∷ m} FF | all .m x₁ = all (just x ∷ m) ((just (FF 0 (here refl))) All.∷ x₁)
allMap-tabulate {F = F} {m = nothing ∷ m} FF
   with allMap-tabulate {m = m} (λ n k∈ →  subst F (deref-∈-irr (n∈S{n}{nothing}{m} (sucin k∈)) k∈) (FF (suc n) (n+1∈S{n}{nothing}{m} k∈)) )
allMap-tabulate {_} {_} {nothing ∷ m} FF | all .m x = all (nothing ∷ m) (nothing All.∷ x)

andmap : ∀ {i} {F : Value → Set i} → U.Decidable F → (m : LMap) → Dec ∀ (n : ℕ) → (n∈ : n ∈ (Dom' m)) → (F (deref n m n∈))
andmap Fp m with allMap m Fp
andmap Fp m | yes p = yes (allMap-lookup p)
andmap Fp m | no ¬p = no (λ x → ¬p (allMap-tabulate x))

ocount : (Value -> Bool) -> LMap -> ℕ
ocount p [] = 0
ocount p (just x ∷ m) with p x
ocount p (just x ∷ m) | false = ocount p m
ocount p (just x ∷ m) | true = suc (ocount p m)
ocount p (nothing ∷ m) = ocount p m

change-one-ocount-goes-down :
  ∀ (m : LMap) ->
    (p : Value -> Bool) ->
    (n : ℕ) ->
    (k∈ : n ∈ (Dom' m)) ->
    p (deref n m k∈) ≡ true ->
    (v : Value) ->
    p v ≡ false ->
    ocount p m ≡ suc (ocount p (m-insert (just v) n m))
change-one-ocount-goes-down = f where
  f : ∀ (m : LMap) ->
    (p : Value -> Bool) ->
    (n : ℕ) ->
    (k∈ : n ∈ (Dom' m)) ->
    p (deref n m k∈) ≡ true ->
    (v : Value) ->
    p v ≡ false ->
    ocount p m ≡ suc (ocount p (m-insert (just v) n m))
  f [] p zero () p[derefnmk∈]≡true v pv≡false
  f (just x ∷ m) p zero k∈ p[derefnmk∈]≡true v pv≡false with p x
  ... | true with p v
  f (just x ∷ m) p zero k∈ p[derefnmk∈]≡true v () | true | true
  ... | false = refl
  f (just x ∷ m) p zero k∈ () v pv≡false | false
  f (nothing ∷ m) p zero k∈ p[derefnmk∈]≡true v pv≡false = ⊥-elim (0∈S k∈)
  f [] p (suc n) () p[derefnmk∈]≡true v pv≡false
  f (just x ∷ m) p (suc n) k∈ p[derefnmk∈]≡true v pv≡false with p x
  ... | true = cong suc (f m p n
                           (n∈S{x = just x}{l = m} k∈)
                           p[derefnmk∈]≡true v pv≡false)
  ... | false = f m p n (n∈S{x = just x}{l = m} k∈)
                  p[derefnmk∈]≡true v pv≡false
  f (nothing ∷ m) p (suc n) k∈ p[derefnmk∈]≡true v pv≡false
   = f m p n (n∈S{x = nothing}{l = m} k∈) p[derefnmk∈]≡true v pv≡false

change-nothing-ocount-stays-same :
  ∀ (m : LMap) ->
    (p : Value -> Bool) ->
    (n : ℕ) ->
    (k∈ : n ∈ (Dom' m)) ->
    (v : Value) ->
    p (deref n m k∈) ≡ p v ->
    ocount p m ≡ ocount p (m-insert (just v) n m)
change-nothing-ocount-stays-same = f where

  f : ∀ (m : LMap) ->
    (p : Value -> Bool) ->
    (n : ℕ) ->
    (k∈ : n ∈ (Dom' m)) ->
    (v : Value) ->
    p (deref n m k∈) ≡ p v ->
    ocount p m ≡ ocount p (m-insert (just v) n m)
  f [] p zero () v pold≡pv
  f (just x ∷ m) p zero k∈ v pold≡pv with p x | p v
  f (just x ∷ m) p zero k∈ v pold≡pv | false | false = refl
  f (just x ∷ m) p zero k∈ v () | false | true
  f (just x ∷ m) p zero k∈ v () | true | false
  f (just x ∷ m) p zero k∈ v pold≡pv | true | true = refl
  f (nothing ∷ m) p zero k∈ v pold≡pv with p v
  f (nothing ∷ m) p zero k∈ v pold≡pv | false = refl
  f (nothing ∷ m) p zero k∈ v pold≡pv | true = ⊥-elim (0∈S k∈)
  f [] p (suc n) () v pold≡pv
  f (just x ∷ m) p (suc n) k∈ v pold≡pv with p x
  f (just x ∷ m) p (suc n) k∈ v pold≡pv | true =
     cong suc (f m p n (n∈S{x = just x}{l = m} k∈) v pold≡pv)
  f (just x ∷ m) p (suc n) k∈ v pold≡pv | false =
     f m p n (n∈S{x = just x}{l = m} k∈) v pold≡pv
  f (nothing ∷ m) p (suc n) k∈ v pold≡pv =
    f m p n (n∈S{x = nothing}{l = m} k∈) v pold≡pv

ocount-merge≤′sum-ocount :
  ∀ (m1 m2 : LMap) ->
    (p : Value -> Bool) ->
    ocount p (m1 U m2) ≤′ ocount p m1 + ocount p m2
ocount-merge≤′sum-ocount = f where

  f : ∀ (m1 m2 : LMap) ->
        (p : Value -> Bool) ->
        ocount p (m1 U m2) ≤′ ocount p m1 + ocount p m2
  f [] [] p = ≤′-refl
  f [] (x ∷ m2) p = ≤′-refl
  f (x ∷ m1) [] p = ≤⇒≤′ (m≤m+n (ocount p (x ∷ m1)) 0)
  f (just x ∷ m1) (just x₁ ∷ m2) p with p x | p x₁ | f m1 m2 p
  f (just x ∷ m1) (just x₁ ∷ m2) p | false | false | R = R
  f (just x ∷ m1) (just x₁ ∷ m2) p | false | true | R =
     ≤′-trans {y = suc (ocount p m1 + ocount p m2)}
              (s≤′s R) (≡is≤′ (sym (+-suc (ocount p m1) (ocount p m2))))
  f (just x ∷ m1) (just x₁ ∷ m2) p | true | false | R =
    ≤′-step R
  f (just x ∷ m1) (just x₁ ∷ m2) p | true | true | R =
    s≤′s (≤′-trans {y = ocount p m1 + ocount p m2}
         R
         (≤′+r {ocount p m2} {suc (ocount p m2)} {ocount p m1} (≤′-step ≤′-refl)))
  f (just x ∷ m1) (nothing ∷ m2) p with p x | f m1 m2 p
  f (just x ∷ m1) (nothing ∷ m2) p | false | R = R
  f (just x ∷ m1) (nothing ∷ m2) p | true | R = s≤′s R
  f (nothing ∷ m1) (just x ∷ m2) p with p x | f m1 m2 p
  f (nothing ∷ m1) (just x ∷ m2) p | false | R = R
  f (nothing ∷ m1) (just x ∷ m2) p | true | R =
     ≤′-trans {y = suc (ocount p m1 + ocount p m2)}
              (s≤′s R) (≡is≤′ (sym (+-suc (ocount p m1) (ocount p m2))))
  f (nothing ∷ m1) (nothing ∷ m2) p with f m1 m2 p
  ... | R = R

Dom'+∈ : (L : LMap) → List (∃[ n ] (n ∈ (Dom' L)))
Dom'+∈ L = help (Dom' L)
 where
  help : (x : List ℕ) → (List (∃[ y ] (y ∈ x)))
  help [] = []
  help (x ∷ x₁) = (x , here refl) ∷ map (λ {(a , b) → a , there b}) (help x₁)


module Data.Unsigned where

open import Data.Bit using
  (Bit
  ; b0
  ; b1
  ; Bits-num
  ; Overflowing
  ; _overflow:_
  ; result
  ; carry
  ; WithCarry
  ; _with-carry:_
  ; toBool
  ; tryToFinₙ
  ; !ₙ
  ) renaming
  ( _+_ to bit+
  ; _-_ to bit-
  ; ! to bit!
  ; _&_ to bit&
  ; _~|_ to bit|
  ; _^_ to bit^
  ; _>>_ to bit>>
  ; _<<_ to bit<<
  )
open import Data.Unit using (⊤)
open import Data.Nat using (ℕ)
open import Data.Vec using (Vec; head; replicate)
open import Data.Nat.Literal using (Number)
open import Data.Maybe using (just; nothing)

infixl 8 _-_ _+_
infixl 7 _<<_ _>>_
infixl 6 _&_
infixl 5 _^_
infixl 4 _~|_

record Unsigned (n : ℕ) : Set where
  constructor mk-uint
  field
    bits : Vec Bit n

open Unsigned public

instance
  Unsigned-num : {m : ℕ} → Number (Unsigned m)
  Unsigned-num {m} .Number.Constraint = Number.Constraint (Bits-num {m})
  Unsigned-num {m} .Number.fromNat n ⦃ p ⦄ = mk-uint (mk-bits p) where
    mk-bits : Number.Constraint (Bits-num {m}) n → Vec Bit m
    mk-bits p = Number.fromNat Bits-num n ⦃ p ⦄

_+_ : {n : ℕ} → Unsigned n → Unsigned n → Overflowing (Unsigned n)
p + q = mk-uint (result sum) overflow: toBool (head (carry sum)) where
  sum = bit+ (bits p) (bits q)

_-_ : {n : ℕ} → Unsigned n → Unsigned n → Overflowing (Unsigned n)
p - q = mk-uint (result sub) overflow: toBool (bit! (head (carry sub))) where
  sub = bit- (bits p) (bits q)

rotr : {n : ℕ} → Unsigned n → Unsigned n → WithCarry (Unsigned n) (Unsigned n)
rotr {n} (mk-uint ps) (mk-uint qs) = new-result with-carry: new-carry where
  shifted : WithCarry (Vec Bit n) (Vec Bit n)
  shifted with tryToFinₙ qs
  ...        | just i  = bit>> ps i
  ...        | nothing = replicate b0 with-carry: ps
  new-result : Unsigned n
  new-result = mk-uint (result shifted)
  new-carry : Unsigned n
  new-carry = mk-uint (carry shifted)

rotl : {n : ℕ} → Unsigned n → Unsigned n → WithCarry (Unsigned n) (Unsigned n)
rotl {n} (mk-uint ps) (mk-uint qs) = new-result with-carry: new-carry where
  shifted : WithCarry (Vec Bit n) (Vec Bit n)
  shifted with tryToFinₙ qs
  ...        | just i  = bit<< ps i
  ...        | nothing = replicate b0 with-carry: ps
  new-result : Unsigned n
  new-result = mk-uint (result shifted)
  new-carry : Unsigned n
  new-carry = mk-uint (carry shifted)

_>>_ : {n : ℕ} → Unsigned n → Unsigned n → Unsigned n
ps >> qs = result (rotr ps qs)

_<<_ : {n : ℕ} → Unsigned n → Unsigned n → Unsigned n
ps << qs = result (rotl ps qs)

! : {n : ℕ} → Unsigned n → Unsigned n
! (mk-uint ps) = mk-uint (!ₙ ps)

_&_ : {n : ℕ} → Unsigned n → Unsigned n → Unsigned n
(mk-uint ps) & (mk-uint qs) = mk-uint (bit& ps qs)

_~|_ : {n : ℕ} → Unsigned n → Unsigned n → Unsigned n
(mk-uint ps) ~| (mk-uint qs) = mk-uint (bit| ps qs)

_^_ : {n : ℕ} → Unsigned n → Unsigned n → Unsigned n
(mk-uint ps) ^ (mk-uint qs) = mk-uint (bit^ ps qs)