module Calf.Solver.Nat.Arithmetic where

open import Cubical.Foundations.Prelude
open import Cubical.Data.Nat using (ℕ; zero; suc; _+_; _∸_; _·_)
import Cubical.Data.Nat.Properties as Nat
open import Cubical.Data.Nat.Order using
  (_≤_; isProp≤; zero-≤; ≤-refl; suc-≤-suc; ≤-trans; ≤-+-≤; ≤SumLeft; ≤SumRight;
   ≤-∸-+-cancel; ≤-∸-≤; ≤-∸-k)
open import Cubical.Data.Sigma using (fst; snd)

∸-witness : ∀ {m n : ℕ} → (h : m ≤ n) → n ∸ m ≡ fst h
∸-witness {m} {n} h =
  cong fst (isProp≤ ((n ∸ m) , ≤-∸-+-cancel h) h)

natRefl : (x : ℕ) → x ≡ x
natRefl x = refl

natCongSuc : (x y : ℕ) → x ≡ y → suc x ≡ suc y
natCongSuc x y p = cong suc p

natCong₂+ :
  (a a′ b b′ : ℕ)
  → a ≡ a′
  → b ≡ b′
  → a + b ≡ a′ + b′
natCong₂+ a a′ b b′ p q = cong₂ _+_ p q

natCong₂· :
  (a a′ b b′ : ℕ)
  → a ≡ a′
  → b ≡ b′
  → a · b ≡ a′ · b′
natCong₂· a a′ b b′ p q = cong₂ _·_ p q

natCong₂∸ :
  (a a′ b b′ : ℕ)
  → a ≡ a′
  → b ≡ b′
  → a ∸ b ≡ a′ ∸ b′
natCong₂∸ a a′ b b′ p q = cong₂ _∸_ p q

natComp :
  (x y z : ℕ)
  → x ≡ y
  → y ≡ z
  → x ≡ z
natComp x y z p q = p ∙ q

finishNatExplicit :
  (lhs lhs′ rhs rhs′ : ℕ)
  → lhs ≡ lhs′
  → lhs′ ≡ rhs′
  → rhs ≡ rhs′
  → lhs ≡ rhs
finishNatExplicit lhs lhs′ rhs rhs′ lhs-step middle rhs-step =
  lhs-step ∙ middle ∙ sym rhs-step

finishLeExplicit :
  (lower lower′ upper upper′ : ℕ)
  → lower ≡ lower′
  → lower′ ≤ upper′
  → upper ≡ upper′
  → lower ≤ upper
finishLeExplicit lower lower′ upper upper′ lower-step middle upper-step =
  subst (λ n → lower ≤ n) (sym upper-step)
    (subst (λ n → n ≤ upper′) (sym lower-step) middle)

leRefl : (m : ℕ) → m ≤ m
leRefl m = ≤-refl

leZero : (n : ℕ) → 0 ≤ n
leZero n = zero-≤

leSuc : (m n : ℕ) → m ≤ n → suc m ≤ suc n
leSuc m n p = suc-≤-suc p

leTrans : (k m n : ℕ) → k ≤ m → m ≤ n → k ≤ n
leTrans k m n p q = ≤-trans p q

lePlus :
  (m n l k : ℕ)
  → m ≤ n
  → l ≤ k
  → m + l ≤ n + k
lePlus m n l k p q = ≤-+-≤ p q

leSumLeft : (n k : ℕ) → n ≤ n + k
leSumLeft n k = ≤SumLeft

leSumRight : (n k : ℕ) → n ≤ k + n
leSumRight n k = ≤SumRight

leMulRight : (m n k : ℕ) → m ≤ n → m ≤ suc k · n
leMulRight m n k h = ≤-trans h (≤SumLeft {n} {k · n})

leWitnessEq : (lower upper : ℕ) → (p : lower ≤ upper) → upper ≡ fst p + lower
leWitnessEq lower upper p = sym (snd p)

leMinusPlusEq : (lower upper : ℕ) → (p : lower ≤ upper) → upper ≡ (upper ∸ lower) + lower
leMinusPlusEq lower upper p = sym (≤-∸-+-cancel p)

minusZeroRight : (x : ℕ) → x ∸ 0 ≡ x
minusZeroRight x = refl

minusZeroLeft : (x : ℕ) → 0 ∸ x ≡ 0
minusZeroLeft x = Nat.zero∸ x

minusSelf : (x : ℕ) → x ∸ x ≡ 0
minusSelf x = Nat.n∸n x

minusPullLeft :
  (m n k : ℕ)
  → m ≤ n
  → (k + n) ∸ m ≡ k + (n ∸ m)
minusPullLeft m n k p = sym (≤-∸-k p)

minusPullRight :
  (m n k : ℕ)
  → m ≤ n
  → (n + k) ∸ m ≡ (n ∸ m) + k
minusPullRight m n k p =
  cong (_∸ m) (Nat.+-comm n k)
  ∙ minusPullLeft m n k p
  ∙ Nat.+-comm k (n ∸ m)

minusPlusRight : (x k : ℕ) → (x + k) ∸ x ≡ k
minusPlusRight x k = Nat.∸+ k x

minusPlusLeft : (k x : ℕ) → (k + x) ∸ x ≡ k
minusPlusLeft k x = Nat.+∸ k x

leMinusRight : (a b c : ℕ) → a + c ≤ b → a ≤ b ∸ c
leMinusRight a b c p =
  subst (λ x → x ≤ b ∸ c) (Nat.+∸ a c) (≤-∸-≤ (a + c) b c p)

leMinusRightComm : (a b c : ℕ) → c + a ≤ b → a ≤ b ∸ c
leMinusRightComm a b c p =
  leMinusRight a b c (subst (_≤ b) (Nat.+-comm c a) p)

leWitnessLower : (a c b : ℕ) → a + c ≤ b → (p : c ≤ b) → a ≤ fst p
leWitnessLower a c b h p =
  subst (a ≤_) (∸-witness p) (leMinusRight a b c h)

leWitnessLowerComm : (a c b : ℕ) → c + a ≤ b → (p : c ≤ b) → a ≤ fst p
leWitnessLowerComm a c b h p =
  subst (a ≤_) (∸-witness p) (leMinusRightComm a b c h)