{-# OPTIONS --cubical #-}

module Data.Binary.Properties.Addition where

open import Data.Binary.Definition
open import Data.Binary.Addition
open import Data.Binary.Conversion
import Agda.Builtin.Nat as 
open import Data.Binary.Properties.Isomorphism
open import Data.Binary.Properties.Helpers
open import Data.Binary.Helpers


+-cong  :  x y   x + y ⇓⟧   x ⇓⟧ ℕ.+  y ⇓⟧
+₁-cong :  x y   1+[ x + y ] ⇓⟧  1 ℕ.+  x ⇓⟧ ℕ.+  y ⇓⟧
+₂-cong :  x y   2+[ x + y ] ⇓⟧  2 ℕ.+  x ⇓⟧ ℕ.+  y ⇓⟧

lemma₁ :  x y   1+[  x + y ] ⇓⟧ ℕ.* 2   x ⇓⟧ ℕ.* 2 ℕ.+ (2 ℕ.+  y ⇓⟧ ℕ.* 2)
lemma₁ x y =
   1+[ x + y ] ⇓⟧ ℕ.* 2               ≡⟨ cong (ℕ._* 2) (+₁-cong x y) 
  2 ℕ.+ ( x ⇓⟧ ℕ.+  y ⇓⟧) ℕ.* 2      ≡⟨ cong (2 ℕ.+_ ) (+-*-distrib  x ⇓⟧  y ⇓⟧ 2) 
  2 ℕ.+  x ⇓⟧ ℕ.* 2 ℕ.+  y ⇓⟧ ℕ.* 2  ≡⟨ cong (ℕ._+ ( y ⇓⟧ ℕ.* 2)) (+-comm 2 ( x ⇓⟧ ℕ.* 2)) 
   x ⇓⟧ ℕ.* 2 ℕ.+ 2 ℕ.+  y ⇓⟧ ℕ.* 2  ≡⟨ +-assoc ( x ⇓⟧ ℕ.* 2) 2 ( y ⇓⟧ ℕ.* 2) 
   x ⇓⟧ ℕ.* 2 ℕ.+ (2 ℕ.+  y ⇓⟧ ℕ.* 2) 

lemma₂ :  x y   1+[ x + y ] ⇓⟧ ℕ.* 2  (1 ℕ.+  x ⇓⟧ ℕ.* 2) ℕ.+ (1 ℕ.+  y ⇓⟧ ℕ.* 2)
lemma₂ x y =
   1+[ x + y ] ⇓⟧ ℕ.* 2              ≡⟨ cong (ℕ._* 2) (+₁-cong x y) 
  (1 ℕ.+  x ⇓⟧ ℕ.+  y ⇓⟧) ℕ.* 2     ≡⟨ +-*-distrib (1 ℕ.+  x ⇓⟧)  y ⇓⟧ 2 
  2 ℕ.+  x ⇓⟧ ℕ.* 2 ℕ.+  y ⇓⟧ ℕ.* 2 ≡˘⟨ +-suc (1 ℕ.+  x ⇓⟧ ℕ.* 2) ( y ⇓⟧ ℕ.* 2) 
  (1 ℕ.+  x ⇓⟧ ℕ.* 2) ℕ.+ (1 ℕ.+  y ⇓⟧ ℕ.* 2) 

lemma₃ :  x y   2+[ x + y ] ⇓⟧ ℕ.* 2  2 ℕ.+ ( x ⇓⟧ ℕ.* 2 ℕ.+ (2 ℕ.+  y ⇓⟧ ℕ.* 2))
lemma₃ x y =
   2+[ x + y ] ⇓⟧ ℕ.* 2                    ≡⟨ cong (ℕ._* 2) (+₂-cong x y) 
  (2 ℕ.+  x ⇓⟧ ℕ.+  y ⇓⟧) ℕ.* 2           ≡˘⟨ cong (ℕ._* 2) (+-suc (1 ℕ.+  x ⇓⟧)  y ⇓⟧) 
  ((1 ℕ.+  x ⇓⟧) ℕ.+ (1 ℕ.+  y ⇓⟧)) ℕ.* 2 ≡⟨ +-*-distrib (1 ℕ.+  x ⇓⟧) (1 ℕ.+  y ⇓⟧) 2 
  2 ℕ.+ ( x ⇓⟧ ℕ.* 2 ℕ.+ (2 ℕ.+  y ⇓⟧ ℕ.* 2)) 

lemma₄ :  x y  2 ℕ.+  x + y ⇓⟧ ℕ.* 2  1 ℕ.+  x ⇓⟧ ℕ.* 2 ℕ.+ (1 ℕ.+  y ⇓⟧ ℕ.* 2)
lemma₄ x y =
  2 ℕ.+  x + y ⇓⟧ ℕ.* 2                ≡⟨ cong  xy  2 ℕ.+ xy ℕ.* 2) (+-cong x y) 
  2 ℕ.+ ( x ⇓⟧ ℕ.+  y ⇓⟧) ℕ.* 2       ≡⟨ cong (2 ℕ.+_) (+-*-distrib  x ⇓⟧  y ⇓⟧ 2) 
  2 ℕ.+ ( x ⇓⟧ ℕ.* 2 ℕ.+  y ⇓⟧ ℕ.* 2) ≡˘⟨ cong suc (+-suc ( x ⇓⟧ ℕ.* 2) ( y ⇓⟧ ℕ.* 2)) 
  1 ℕ.+  x ⇓⟧ ℕ.* 2 ℕ.+ (1 ℕ.+  y ⇓⟧ ℕ.* 2) 

+₁-cong 0ᵇ y = inc-suc y
+₁-cong (1ᵇ x) 0ᵇ = cong (2 ℕ.+_) (sym (+-idʳ ( x ⇓⟧ ℕ.* 2)))
+₁-cong (2ᵇ x) 0ᵇ = cong suc (cong (ℕ._* 2) (inc-suc x)  cong (2 ℕ.+_) (sym (+-idʳ ( x ⇓⟧ ℕ.* 2))))
+₁-cong (1ᵇ x) (1ᵇ y) = cong suc (lemma₂ x y)
+₁-cong (1ᵇ x) (2ᵇ y) = cong (2 ℕ.+_) (lemma₁ x y)
+₁-cong (2ᵇ x) (1ᵇ y) = cong (2 ℕ.+_) (lemma₂ x y)
+₁-cong (2ᵇ x) (2ᵇ y) = cong (1 ℕ.+_) (lemma₃ x y)

+₂-cong 0ᵇ 0ᵇ = refl
+₂-cong 0ᵇ (1ᵇ y) = cong (1 ℕ.+_) (cong (ℕ._* 2) (inc-suc y))
+₂-cong 0ᵇ (2ᵇ y) = cong (2 ℕ.+_) (cong (ℕ._* 2) (inc-suc y))
+₂-cong (1ᵇ x) 0ᵇ = cong (1 ℕ.+_) ((cong (ℕ._* 2) (inc-suc x))  cong (2 ℕ.+_) (sym (+-idʳ _)))
+₂-cong (2ᵇ x) 0ᵇ = cong (2 ℕ.+_) ((cong (ℕ._* 2) (inc-suc x))  cong (2 ℕ.+_) (sym (+-idʳ _)))
+₂-cong (1ᵇ x) (1ᵇ y) = cong (2 ℕ.+_ ) (lemma₂ x y)
+₂-cong (1ᵇ x) (2ᵇ y) = cong (1 ℕ.+_) (lemma₃ x y)
+₂-cong (2ᵇ x) (1ᵇ y) = cong (1 ℕ.+_) (lemma₃ x y  +-suc (2 ℕ.+  x ⇓⟧ ℕ.* 2) (1 ℕ.+  y ⇓⟧ ℕ.* 2))
+₂-cong (2ᵇ x) (2ᵇ y) = cong (2 ℕ.+_) (lemma₃ x y)

+-cong 0ᵇ y = refl
+-cong (1ᵇ x) 0ᵇ = cong suc (sym (+-idʳ ( x ⇓⟧ ℕ.* 2)))
+-cong (2ᵇ x) 0ᵇ = cong  r  suc (suc r)) (sym (+-idʳ ( x ⇓⟧ ℕ.* 2)))
+-cong (1ᵇ x) (1ᵇ y) = lemma₄ x y
+-cong (1ᵇ x) (2ᵇ y) = cong suc (lemma₁ x y)
+-cong (2ᵇ x) (1ᵇ y) = cong suc (lemma₂ x y)
+-cong (2ᵇ x) (2ᵇ y) = cong (2 ℕ.+_) (lemma₁ x y)