Int.order

-- Order on β„€, same shape as natOrder's LeN: the difference between
-- two integers in the right order is a NATURAL, so x ≀ y is a nat
-- witness k with an Id-proof that x + k closes the gap. No quot-elim
-- into π•Œ is ever needed (π•Œ-equality is structural, so a code family
-- BY REPRESENTATIVES has no well-definedness proof): the family is
-- built by COMPOSITION β€” intAdd, intOfNat, Id β€” and the one place
-- representatives must be touched (deciding the order) goes through
-- the honest function intCanon.

import Natural (+, *, plusComm, zeroPlusId, zeroMult)
import Natural.order (≀, leTotal, sumZeroL)
import Int (Int, intZero, intNeg)
import Int.add (+, intAddComm, intAddAssoc)
import Int.mul (*, intMulComm, intMulDistribL, intAddNegL, intAddNegR, intNegNeg, classPairEta)
import Rat.frac (intAddZeroL, intAddZeroR)
import Int.eq (intCanon, intCanonClass)
import Int.effective (intEffective)
import Core.equality (trans, sym, cong, transport)
import Core.id (Id, idToEq, eqToId)

intOfNat : β„• β†’ Int using (Int.Int.unfold)
intOfNat = Ξ»n. class (n, Z)

infixl 4 ≀
≀ : Int β†’ Int β†’ π•Œ
(≀) = Ξ»x y. (k : β„•) Γ— Id _ (x + intOfNat k) y

-- ===== glue: intOfNat is an order-embedding-ready map =====
intOfNatZero : intOfNat Z ≑ intZero using (Int.order.intOfNat.eq, Int.Int.unfold, Int.intZero.eq)
intOfNatZero = ⋆

intOfNatPlus : (a b : β„•) β†’ intOfNat a + intOfNat b ≑ intOfNat (a + b)
  using (Int.order.intOfNat.eq, Int.Int.unfold, Int.add.+.eq, Natural.+.eq)
intOfNatPlus = Ξ»a b. ⋆

intOfNatInjZ : {k : β„•} β†’ (intOfNat k ≑ intZero) β†’ k ≑ Z
  using (hyp.rw,
    Int.order.intOfNat.eq,
    Int.IntR.unfold,
    Int.intZero.eq,
    Natural.plusZeroId,
    Natural.plusZeroId.rw)
intOfNatInjZ = Ξ»k h. let r = intEffective h in ⋆

-- x + ofNat (a + b) ≑ (x + ofNat a) + ofNat b, hypothesis-free
intOfNatSplit : {x : Int} {a b : β„•} β†’ x + intOfNat (a + b) ≑ x + intOfNat a + intOfNat b
  using (Int.Int.unfold)
intOfNatSplit =
  Ξ»x a b. x + intOfNat (a + b)
    β‰‘βŸ¨ cong (Ξ»u. Int) (Ξ»u. x + u) (sym _ _ (intOfNatPlus a b)) ⟩ x + (intOfNat a + intOfNat b)
    β‰‘βŸ¨ sym _ _ (intAddAssoc x (intOfNat a) (intOfNat b)) ⟩ x + intOfNat a + intOfNat b

-- ===== additive-group plumbing =====
intNegAdd : (z w : Int) β†’ intNeg (z + w) ≑ intNeg z + intNeg w
  using (Int.Int.unfold, Int.intNeg.eq, Int.add.+.eq)
intNegAdd = Ξ»z w. quot-elim (p. quot-elim (q. ⋆) w) z

-- βˆ’x + (x + a) ≑ a, hypothesis-free
intNegPlusCancel : (x a : Int) β†’ intNeg x + (x + a) ≑ a using (Int.Int.unfold)
intNegPlusCancel =
  Ξ»x a. intNeg x + (x + a)
    β‰‘βŸ¨ sym _ _ (intAddAssoc (intNeg x) x a) ⟩ intNeg x + x + a
    β‰‘βŸ¨ cong (Ξ»u. Int) (Ξ»u. u + a) (intAddNegL x) ⟩ intZero + a
    β‰‘βŸ¨ intAddZeroL a ⟩ a

intCancelL : (x a b : Int) β†’ (x + a ≑ x + b) β†’ a ≑ b
intCancelL =
  Ξ»x a b h. trans
    _
    _
    _
    sym _ _ (intNegPlusCancel x a)
    trans _ _ _ (cong (Ξ»u. Int) (Ξ»u. intNeg x + u) h) (intNegPlusCancel x b)

-- x + (y βˆ’ x) ≑ y, and y βˆ’ (y βˆ’ x) ≑ x: the two rearrangements the
-- order decision below hangs on
intPlusDiff : (x y : Int) β†’ x + (y + intNeg x) ≑ y using (Int.Int.unfold)
intPlusDiff =
  Ξ»x y. x + (y + intNeg x)
    β‰‘βŸ¨ cong (Ξ»u. Int) (Ξ»u. x + u) (intAddComm y (intNeg x)) ⟩ x + (intNeg x + y)
    β‰‘βŸ¨ sym _ _ (intAddAssoc x (intNeg x) y) ⟩ x + intNeg x + y
    β‰‘βŸ¨ cong (Ξ»u. Int) (Ξ»u. u + y) (intAddNegR x) ⟩ intZero + y
    β‰‘βŸ¨ intAddZeroL y ⟩ y

intPlusNegDiff : (x y : Int) β†’ y + intNeg (y + intNeg x) ≑ x using (Int.Int.unfold)
intPlusNegDiff =
  Ξ»x y. y + intNeg (y + intNeg x)
    β‰‘βŸ¨ cong (Ξ»u. Int) (Ξ»u. y + u) (intNegAdd y (intNeg x)) ⟩ y + (intNeg y + intNeg (intNeg x))
    β‰‘βŸ¨ cong (Ξ»u. Int) (Ξ»u. y + (intNeg y + u)) (intNegNeg x) ⟩ y + (intNeg y + x)
    β‰‘βŸ¨ sym _ _ (intAddAssoc y (intNeg y) x) ⟩ y + intNeg y + x
    β‰‘βŸ¨ cong (Ξ»u. Int) (Ξ»u. u + x) (intAddNegR y) ⟩ intZero + x
    β‰‘βŸ¨ intAddZeroL x ⟩ x

-- ===== the order =====
leZRefl : (x : Int) β†’ x ≀ x using (Int.order.≀.unfold, Int.order.intOfNatZero, Int.Int.unfold)
leZRefl = Ξ»x. Z, eqToId _ _ (intAddZeroR x)

leZOfEq : {x y : Int} β†’ (x ≑ y) β†’ x ≀ y
  using (Int.order.≀.unfold, Int.order.intOfNatZero, Int.Int.unfold)
leZOfEq = Ξ»x y h. Z, eqToId _ _ (trans (x + intOfNat Z) _ _ (intAddZeroR x) h)

leZTrans : {x : Int} (y : Int) {z : Int} β†’ x ≀ y β†’ y ≀ z β†’ x ≀ z using (Int.order.≀.unfold)
leZTrans =
  Ξ»x y z l1 l2. (,)
    l1 .π₁ + l2 .π₁
    eqToId
      _
      _
      trans
        x + intOfNat (l1 .π₁ + l2 .π₁)
        _
        _
        intOfNatSplit
        trans
          _
          _
          _
          cong (Ξ»u. Int) (Ξ»u. u + intOfNat (l2 .π₁)) (idToEq _ _ _ (l1 .Ο€β‚‚))
          idToEq _ _ _ (l2 .Ο€β‚‚)

leZAntisym : (x y : Int) β†’ x ≀ y β†’ y ≀ x β†’ x ≑ y using (Int.order.≀.unfold)
leZAntisym =
  Ξ»x y l1 l2. let h1 = idToEq _ _ _ (l1 .Ο€β‚‚)
                  h2 = idToEq _ _ _ (l2 .Ο€β‚‚)
                  loop : x + intOfNat (l1 .π₁ + l2 .π₁) ≑ x
                    = trans
                      _
                      _
                      _
                      intOfNatSplit
                      trans _ _ _ (cong (Ξ»u. Int) (Ξ»u. u + intOfNat (l2 .π₁)) h1) h2
                  sumz : intOfNat (l1 .π₁ + l2 .π₁) ≑ intZero
                    = intCancelL _ _ _ (trans _ _ _ loop (sym _ _ (intAddZeroR x)))
                  k1z = sumZeroL _ _ (intOfNatInjZ sumz)
                  trans
                    _
                    _
                    _
                    trans
                      _
                      _
                      _
                      sym _ _ (intAddZeroR x)
                      cong
                        Ξ»u. Int
                        Ξ»u. x + u
                        sym _ _ (trans _ _ _ (cong (Ξ»u. Int) (Ξ»u. intOfNat u) k1z) intOfNatZero)
                    h1

-- ===== monotonicity =====
-- the shuffle, HYPOTHESIS-FREE (an equation hypothesis in scope can
-- rewrite inside a stuck-scrutinee position of both chain sides at
-- once; such lockstep steps are unreplayable, so keep the shuffle in
-- a helper and use the hypothesis only through cong)
intAddShiftR : {x w v : Int} β†’ x + w + v ≑ x + v + w using (Int.Int.unfold)
intAddShiftR =
  Ξ»x w v. x + w + v
    β‰‘βŸ¨ intAddAssoc x w v ⟩ x + (w + v)
    β‰‘βŸ¨ cong (Ξ»u. Int) (Ξ»u. x + u) (intAddComm w v) ⟩ x + (v + w)
    β‰‘βŸ¨ sym _ _ (intAddAssoc x v w) ⟩ x + v + w

leZPlusMonoR : {x y : Int} (w : Int) β†’ x ≀ y β†’ x + w ≀ y + w using (Int.order.≀.unfold)
leZPlusMonoR =
  Ξ»x y w le. (,)
    le .π₁
    eqToId
      _
      _
      trans
        x + w + intOfNat (le .π₁)
        _
        _
        intAddShiftR
        cong (Ξ»u. Int) (Ξ»u. u + w) (idToEq _ _ _ (le .Ο€β‚‚))

leZPlusMonoL : (x y w : Int) β†’ x ≀ y β†’ w + x ≀ w + y using (Int.order.≀.unfold)
leZPlusMonoL =
  Ξ»x y w le. leZTrans
    _
    leZOfEq (intAddComm w x)
    leZTrans _ (leZPlusMonoR w le) (leZOfEq (intAddComm y w))

-- again the shuffle first, hypothesis-free
intNegAddCancel : {x v : Int} β†’ intNeg (x + v) + v ≑ intNeg x using (Int.Int.unfold)
intNegAddCancel =
  Ξ»x v. intNeg (x + v) + v
    β‰‘βŸ¨ cong (Ξ»u. Int) (Ξ»u. u + v) (intNegAdd x v) ⟩ intNeg x + intNeg v + v
    β‰‘βŸ¨ intAddAssoc (intNeg x) (intNeg v) v ⟩ intNeg x + (intNeg v + v)
    β‰‘βŸ¨ cong (Ξ»u. Int) (Ξ»u. intNeg x + u) (intAddNegL v) ⟩ intNeg x + intZero
    β‰‘βŸ¨ intAddZeroR (intNeg x) ⟩ intNeg x

leZNegFlip : (x y : Int) β†’ x ≀ y β†’ intNeg y ≀ intNeg x using (Int.order.≀.unfold)
leZNegFlip =
  Ξ»x y le. (,)
    le .π₁
    eqToId
      _
      _
      trans
        _
        _
        intNeg x
        cong (Ξ»u. Int) (Ξ»u. intNeg u + intOfNat (le .π₁)) (sym _ _ (idToEq _ _ _ (le .Ο€β‚‚)))
        intNegAddCancel

-- ===== the β„• bridge =====
leZOfNat : (a b : β„•) β†’ a ≀ b β†’ intOfNat a ≀ intOfNat b
  using (Int.order.≀.unfold, Natural.order.≀.unfold)
leZOfNat =
  Ξ»a b le. (,)
    le .π₁
    eqToId
      _
      _
      trans
        _
        _
        _
        intOfNatPlus a (le .π₁)
        cong (Ξ»u. Int) (Ξ»u. intOfNat u) (idToEq _ _ _ (le .Ο€β‚‚))

-- ===== totality: decide by the canonical form of the difference =====
-- The class-equation legs, kept ABSTRACT in the pair components
-- (ProvingFeedback D-3): instantiated at intCanon's projections the
-- engine would match against the Ξ΄-UNFOLDED canon β€” a bare quot-elim
-- that proof-spine inference cannot type.
-- the represented relation behind clsOfRelGe's class equation, as its
-- own lemma: k + b ≑ Z + a from b + k ≑ a
sumSwapZL : (a b k : β„•) β†’ (b + k ≑ a) β†’ k + b ≑ Z + a
sumSwapZL = Ξ»a b k h. k + b β‰‘βŸ¨ plusComm b k ⟩ b + k β‰‘βŸ¨ h ⟩ a β‰‘βŸ¨ zeroPlusId a ⟩ Z + a

clsOfRelGe : (a b k : β„•) β†’ (b + k ≑ a) β†’ class (k, Z) ≑ class (a, b) ∈ Int
  using (Int.Int.unfold, plusComm, sumSwapZL, zeroPlusId)
clsOfRelGe = Ξ»a b k h. ⋆

clsOfRelLe : (a b k : β„•) β†’ (a + k ≑ b) β†’ class (k, Z) ≑ class (b, a) ∈ Int
  using (Int.order.clsOfRelGe, Int.Int.unfold, plusComm, zeroPlusId)
clsOfRelLe = Ξ»a b k h. ⋆

intNegClass : (p : β„• Γ— β„•) β†’ intNeg (class p) ≑ class (p .Ο€β‚‚, p .π₁)
  using (Int.Int.unfold, Int.intNeg.eq)
intNegClass = Ξ»p. ⋆

leZTotal : (x y : Int) β†’ x ≀ y ⊎ y ≀ x
  using (Core.id.Id.unfold,
    Int.order.≀.unfold,
    Int.order.intOfNat.eq,
    Int.Int.unfold,
    Int.mul.intAddCong2,
    Natural.order.≀.unfold)
leZTotal =
  Ξ»x y. let d = y + intNeg x
            hd = intCanonClass d
            ⊎-elim
              nn. let hb = idToEq _ _ _ (nn .Ο€β‚‚)
                      e1 : intOfNat (nn .π₁) ≑ class (intCanon d)
                        = intOfNat (nn .π₁)
                          β‰‘βŸ¨ clsOfRelGe _ _ _ hb ⟩ class (intCanon d .π₁, intCanon d .Ο€β‚‚)
                          β‰‘βŸ¨ classPairEta (intCanon d) ⟩ class (intCanon d)
                      inj₁
                        (,)
                          nn .π₁
                          eqToId
                            _
                            _
                            trans
                              _
                              _
                              y
                              cong (Ξ»u. Int) (Ξ»u. x + u) (trans _ _ _ e1 hd)
                              intPlusDiff x y
              nn. let ha = idToEq _ _ _ (nn .Ο€β‚‚)
                      e2 : intOfNat (nn .π₁) ≑ intNeg (class (intCanon d))
                        = intOfNat (nn .π₁)
                          β‰‘βŸ¨ clsOfRelLe _ _ _ ha ⟩ class (intCanon d .Ο€β‚‚, intCanon d .π₁)
                          β‰‘βŸ¨ sym _ _ (intNegClass (intCanon d)) ⟩ intNeg (class (intCanon d))
                      injβ‚‚
                        (,)
                          nn .π₁
                          eqToId
                            _
                            _
                            trans
                              _
                              _
                              x
                              cong
                                Ξ»u. Int
                                Ξ»u. y + u
                                trans _ _ _ e2 (cong (Ξ»u. Int) (Ξ»u. intNeg u) hd)
                              intPlusNegDiff x y
              leTotal (intCanon d .Ο€β‚‚) (intCanon d .π₁)

-- ===== multiplicative order =====
-- intOfNat is multiplicative (ZΒ·b needs one zeroMult rewrite)
intOfNatMul : (a b : β„•) β†’ intOfNat a * intOfNat b ≑ intOfNat (a * b)
  using (Int.order.intOfNat.eq,
    Int.mul.*.eq,
    Int.Int.eq,
    zeroMult,
    zeroMult.rw,
    Natural.multZeroId,
    Natural.multZeroId.rw,
    Natural.plusZeroId,
    Natural.plusZeroId.rw,
    zeroPlusId,
    zeroPlusId.rw)
intOfNatMul = Ξ»a b. ⋆

-- right distributivity, from the left one by commutativity
intMulDistribR : (a b c : Int) β†’ (a + b) * c ≑ a * c + b * c using (Int.Int.unfold)
intMulDistribR =
  Ξ»a b c. (a + b) * c
    β‰‘βŸ¨ intMulComm (a + b) c ⟩ c * (a + b)
    β‰‘βŸ¨ intMulDistribL c a b ⟩ c * a + c * b
    β‰‘βŸ¨ cong (Ξ»v. Int) (Ξ»v. v + c * b) (intMulComm c a) ⟩ a * c + c * b
    β‰‘βŸ¨ cong (Ξ»v. Int) (Ξ»v. a * c + v) (intMulComm c b) ⟩ a * c + b * c

-- scaling by a NATURAL preserves the order: the witness scales
leZMultMonoNat : {x y : Int} {m : β„•} β†’ x ≀ y β†’ x * intOfNat m ≀ y * intOfNat m
  using (Int.order.≀.unfold)
leZMultMonoNat =
  Ξ»x y m le. (,)
    le .π₁ * m
    eqToId
      _
      _
      trans
        _
        _
        _
        cong (Ξ»v. Int) (Ξ»v. x * intOfNat m + v) (sym _ _ (intOfNatMul (le .π₁) m))
        trans
          _
          _
          _
          sym _ _ (intMulDistribR x (intOfNat (le .π₁)) (intOfNat m))
          cong (Ξ»v. Int) (Ξ»v. v * intOfNat m) (idToEq _ _ _ (le .Ο€β‚‚))

-- a nonneg integer is a natural, witnessed
leZNonNegView : (c : Int) β†’ intZero ≀ c β†’ (m : β„•) Γ— intOfNat m ≑ c using (Int.order.≀.unfold)
leZNonNegView =
  Ξ»c le. le .π₁, trans _ _ _ (sym _ _ (intAddZeroL (intOfNat (le .π₁)))) (idToEq _ _ _ (le .Ο€β‚‚))

-- scaling by a NONNEGATIVE integer preserves the order: view the
-- scalar as a natural and TRANSPORT the scaled inequality along the
-- view β€” LeZ is a π•Œ-family, so transport applies to it directly
leZMultMono : (x y c : Int) β†’ x ≀ y β†’ intZero ≀ c β†’ x * c ≀ y * c using (Int.order.≀.unfold)
leZMultMono =
  Ξ»x y c le lc. transport
    {Int}
    Ξ»u. x * u ≀ y * u
    {intOfNat (leZNonNegView _ lc .π₁)}
    leZNonNegView _ lc .Ο€β‚‚
    leZMultMonoNat le