import Base
import ./nat.bend as N
import ./int.bend as C

# int_filled.bend -- fills for int.bend.
#
# The representation is fixed by the claim file: Pos{n} is n and NegS{n}
# is -(1+n). Every operation below is written so that it computes on
# constructors, which is what makes the test cases and the embedding laws
# close with {==}.

# ---- helpers ------------------------------------------------------------

# -n, as an Int (the only place the two zeros have to be reconciled)
def Int.neg_nat(n: Nat) -> C.Int:
  match n:
    case 0n:
      C.Pos{0n}
    case 1n+p:
      C.NegS{p}

# a - b for Nats, as an Int
def Int.sub_nat(a: Nat, b: Nat) -> C.Int:
  match a b:
    case 0n 0n:
      C.Pos{0n}
    case 0n 1n+bp:
      C.NegS{bp}
    case 1n+ap 0n:
      C.Pos{1n+ap}
    case 1n+ap 1n+bp:
      Int.sub_nat(ap, bp)

# ---- operations ---------------------------------------------------------

def C.Int.neg(a):
  match a:
    case C.Pos{n}:
      Int.neg_nat(n)
    case C.NegS{n}:
      C.Pos{1n+n}

def C.Int.add(a, b):
  match a b:
    case C.Pos{x} C.Pos{y}:
      C.Pos{Nat.add(x, y)}
    case C.Pos{x} C.NegS{y}:
      Int.sub_nat(x, 1n+y)
    case C.NegS{x} C.Pos{y}:
      Int.sub_nat(y, 1n+x)
    case C.NegS{x} C.NegS{y}:
      C.NegS{1n+Nat.add(x, y)}

def C.Int.mul(a, b):
  match a b:
    case C.Pos{x} C.Pos{y}:
      C.Pos{Nat.mul(x, y)}
    case C.Pos{x} C.NegS{y}:
      Int.neg_nat(Nat.mul(x, 1n+y))
    case C.NegS{x} C.Pos{y}:
      Int.neg_nat(Nat.mul(1n+x, y))
    case C.NegS{x} C.NegS{y}:
      C.Pos{Nat.mul(1n+x, 1n+y)}

def C.Int.sub(a, b):
  C.Int.add(a, C.Int.neg(b))

def C.Int.abs(a):
  match a:
    case C.Pos{n}:
      n
    case C.NegS{n}:
      1n+n

def C.Int.Le(a, b):
  match a b:
    case C.Pos{x} C.Pos{y}:
      N.Nat.Le(x, y)
    case C.Pos{x} C.NegS{y}:
      Empty
    case C.NegS{x} C.Pos{y}:
      Unit
    case C.NegS{x} C.NegS{y}:
      N.Nat.Le(y, x)

# ---- test cases ---------------------------------------------------------

def C.t_add_1():
  {==}

def C.t_add_2():
  {==}

def C.t_mul_1():
  {==}

def C.t_mul_2():
  {==}

def C.t_sub_1():
  {==}

def C.t_abs_1():
  {==}

# ---- laws ---------------------------------------------------------------

def C.of_add(a, b):
  {==}

def C.of_mul(a, b):
  {==}

def C.sub_def(a, b):
  {==}

# n - n = 0
def Int.sub_nat_self(n: Nat) -> {Int.sub_nat(n, n) == C.Pos{0n} : C.Int}:
  match n:
    case 0n:
      {==}
    case 1n+p:
      Int.sub_nat_self(p)

def C.add_neg(a):
  match a:
    case C.Pos{n}:
      match n:
        case 0n:
          {==}
        case 1n+p:
          Int.sub_nat_self(p)
    case C.NegS{n}:
      Int.sub_nat_self(n)

def C.neg_neg(a):
  match a:
    case C.Pos{n}:
      match n:
        case 0n:
          {==}
        case 1n+p:
          {==}
    case C.NegS{n}:
      {==}

def C.add_zero(a):
  match a:
    case C.Pos{n}:
      %N.Nat.add_zero(n) : {C.Pos{Nat.add(n, 0n)} == C.Pos{_} : C.Int}
      {==}
    case C.NegS{n}:
      {==}

def C.add_comm(a, b):
  match a b:
    case C.Pos{x} C.Pos{y}:
      %N.Nat.add_comm(x, y) : {C.Pos{Nat.add(x, y)} == C.Pos{_} : C.Int}
      {==}
    case C.Pos{x} C.NegS{y}:
      {==}
    case C.NegS{x} C.Pos{y}:
      {==}
    case C.NegS{x} C.NegS{y}:
      %N.Nat.add_comm(x, y) : {C.NegS{1n+Nat.add(x, y)} == C.NegS{1n+_} : C.Int}
      {==}

def C.mul_one(a):
  match a:
    case C.Pos{n}:
      %N.Nat.mul_one(n) : {C.Pos{Nat.mul(n, 1n)} == C.Pos{_} : C.Int}
      {==}
    case C.NegS{n}:
      %N.Nat.mul_one(n) : {C.NegS{Nat.mul(n, 1n)} == C.NegS{_} : C.Int}
      {==}

def C.mul_zero(a):
  match a:
    case C.Pos{n}:
      %N.Nat.mul_zero(n) : {C.Pos{Nat.mul(n, 0n)} == C.Pos{_} : C.Int}
      {==}
    case C.NegS{n}:
      %Equal.sym(Nat, Nat.mul(n, 0n), 0n, N.Nat.mul_zero(n)) : {Int.neg_nat(_) == C.Pos{0n} : C.Int}
      {==}

def C.mul_comm(a, b):
  match a b:
    case C.Pos{x} C.Pos{y}:
      %N.Nat.mul_comm(x, y) : {C.Pos{Nat.mul(x, y)} == C.Pos{_} : C.Int}
      {==}
    case C.Pos{x} C.NegS{y}:
      %N.Nat.mul_comm(x, 1n+y) : {Int.neg_nat(Nat.mul(x, 1n+y)) == Int.neg_nat(_) : C.Int}
      {==}
    case C.NegS{x} C.Pos{y}:
      %N.Nat.mul_comm(1n+x, y) : {Int.neg_nat(Nat.mul(1n+x, y)) == Int.neg_nat(_) : C.Int}
      {==}
    case C.NegS{x} C.NegS{y}:
      %N.Nat.mul_comm(1n+x, 1n+y) : {C.Pos{Nat.mul(1n+x, 1n+y)} == C.Pos{_} : C.Int}
      {==}

def C.le_refl(a):
  match a:
    case C.Pos{n}:
      N.Nat.le_refl(n)
    case C.NegS{n}:
      N.Nat.le_refl(n)

def C.le_antisym(a, b, h1, h2):
  match a b:
    case C.Pos{x} C.Pos{y}:
      %N.Nat.le_antisym(x, y, h1, h2) : {C.Pos{x} == C.Pos{_} : C.Int}
      {==}
    case C.Pos{x} C.NegS{y}:
      match h1:
    case C.NegS{x} C.Pos{y}:
      match h2:
    case C.NegS{x} C.NegS{y}:
      %N.Nat.le_antisym(x, y, h2, h1) : {C.NegS{x} == C.NegS{_} : C.Int}
      {==}

def C.le_trans(a, b, c, h1, h2):
  match a b c:
    case C.Pos{x} C.Pos{y} C.Pos{z}:
      N.Nat.le_trans(x, y, z, h1, h2)
    case C.Pos{x} C.Pos{y} C.NegS{z}:
      match h2:
    case C.Pos{x} C.NegS{y} C.Pos{z}:
      match h1:
    case C.Pos{x} C.NegS{y} C.NegS{z}:
      match h1:
    case C.NegS{x} C.Pos{y} C.Pos{z}:
      Unit{}
    case C.NegS{x} C.Pos{y} C.NegS{z}:
      match h2:
    case C.NegS{x} C.NegS{y} C.Pos{z}:
      Unit{}
    case C.NegS{x} C.NegS{y} C.NegS{z}:
      N.Nat.le_trans(z, y, x, h2, h1)

def C.pos_nonneg(n):
  Unit{}

# ---- the difference view -------------------------------------------------
#
# Every Int is Int.sub_nat(p, m) for a pair of Nats, and the operations
# act on such pairs the way school arithmetic says. Proving that once
# turns the ring laws into Nat lemmas.

def Int.toP(a: C.Int) -> Nat:
  match a:
    case C.Pos{n}:
      n
    case C.NegS{n}:
      0n

def Int.toM(a: C.Int) -> Nat:
  match a:
    case C.Pos{n}:
      0n
    case C.NegS{n}:
      1n+n

def Int.sub_nat_zero(x: Nat) -> {Int.sub_nat(x, 0n) == C.Pos{x} : C.Int}:
  match x:
    case 0n:
      {==}
    case 1n+xp:
      {==}

def Int.sub_nat_zero_l(x: Nat) -> {Int.sub_nat(0n, x) == Int.neg_nat(x) : C.Int}:
  match x:
    case 0n:
      {==}
    case 1n+xp:
      {==}

def Int.split(a: C.Int) -> {Int.sub_nat(Int.toP(a), Int.toM(a)) == a : C.Int}:
  match a:
    case C.Pos{n}:
      Int.sub_nat_zero(n)
    case C.NegS{n}:
      {==}

def Int.by1(-P: C.Int -> Type, +a: C.Int, w: P(Int.sub_nat(Int.toP(a), Int.toM(a)))) -> P(a):
  %Int.split(a) : P(_)
  w

def Int.by2(-P: C.Int -> C.Int -> Type, +a: C.Int, +b: C.Int, w: P(Int.sub_nat(Int.toP(a), Int.toM(a)), Int.sub_nat(Int.toP(b), Int.toM(b)))) -> P(a, b):
  %Int.split(a) : P(_, b)
  %Int.split(b) : P(Int.sub_nat(Int.toP(a), Int.toM(a)), _)
  w

def Int.by3(-P: C.Int -> C.Int -> C.Int -> Type, +a: C.Int, +b: C.Int, +c: C.Int, w: P(Int.sub_nat(Int.toP(a), Int.toM(a)), Int.sub_nat(Int.toP(b), Int.toM(b)), Int.sub_nat(Int.toP(c), Int.toM(c)))) -> P(a, b, c):
  %Int.split(a) : P(_, b, c)
  %Int.split(b) : P(Int.sub_nat(Int.toP(a), Int.toM(a)), _, c)
  %Int.split(c) : P(Int.sub_nat(Int.toP(a), Int.toM(a)), Int.sub_nat(Int.toP(b), Int.toM(b)), _)
  w

# p + (q - n) = (p + q) - n
def Int.add_pos(+x: Nat, q: Nat, n: Nat) -> {C.Int.add(C.Pos{x}, Int.sub_nat(q, n)) == Int.sub_nat(Nat.add(x, q), n) : C.Int}:
  match q n:
    case 0n 0n:
      Equal.sym(C.Int, Int.sub_nat(Nat.add(x, 0n), 0n), C.Pos{Nat.add(x, 0n)}, Int.sub_nat_zero(Nat.add(x, 0n)))
    case 0n 1n+np:
      %N.Nat.add_zero(x) : {Int.sub_nat(_, 1n+np) == Int.sub_nat(Nat.add(x, 0n), 1n+np) : C.Int}
      {==}
    case 1n+qp 0n:
      Equal.sym(C.Int, Int.sub_nat(Nat.add(x, 1n+qp), 0n), C.Pos{Nat.add(x, 1n+qp)}, Int.sub_nat_zero(Nat.add(x, 1n+qp)))
    case 1n+qp 1n+np:
      %Equal.sym(Nat, Nat.add(x, 1n+qp), 1n+Nat.add(x, qp), N.Nat.add_succ(x, qp)) : {C.Int.add(C.Pos{x}, Int.sub_nat(qp, np)) == Int.sub_nat(_, 1n+np) : C.Int}
      Int.add_pos(x, qp, np)

# -(1+m) + (q - n) = q - (1 + m + n)
def Int.add_negs(+m: Nat, q: Nat, n: Nat) -> {C.Int.add(C.NegS{m}, Int.sub_nat(q, n)) == Int.sub_nat(q, 1n+Nat.add(m, n)) : C.Int}:
  match q n:
    case 0n 0n:
      %Equal.sym(Nat, Nat.add(m, 0n), m, N.Nat.add_zero(m)) : {C.NegS{m} == C.NegS{_} : C.Int}
      {==}
    case 0n 1n+np:
      %N.Nat.add_succ(m, np) : {C.NegS{_} == C.NegS{Nat.add(m, 1n+np)} : C.Int}
      {==}
    case 1n+qp 0n:
      %Equal.sym(Nat, Nat.add(m, 0n), m, N.Nat.add_zero(m)) : {Int.sub_nat(qp, m) == Int.sub_nat(qp, _) : C.Int}
      {==}
    case 1n+qp 1n+np:
      %Equal.sym(Nat, Nat.add(m, 1n+np), 1n+Nat.add(m, np), N.Nat.add_succ(m, np)) : {C.Int.add(C.NegS{m}, Int.sub_nat(qp, np)) == Int.sub_nat(qp, _) : C.Int}
      Int.add_negs(m, qp, np)

# (p - m) + (q - n) = (p + q) - (m + n)
def Int.add_sub(p: Nat, m: Nat, +q: Nat, +n: Nat) -> {C.Int.add(Int.sub_nat(p, m), Int.sub_nat(q, n)) == Int.sub_nat(Nat.add(p, q), Nat.add(m, n)) : C.Int}:
  match p m:
    case 0n 0n:
      Int.add_pos(0n, q, n)
    case 0n 1n+mp:
      Int.add_negs(mp, q, n)
    case 1n+pp 0n:
      Int.add_pos(1n+pp, q, n)
    case 1n+pp 1n+mp:
      Int.add_sub(pp, mp, q, n)

def Int.add_assoc.core(+p1: Nat, +m1: Nat, +p2: Nat, +m2: Nat, +p3: Nat, +m3: Nat) -> {C.Int.add(Int.sub_nat(p1, m1), C.Int.add(Int.sub_nat(p2, m2), Int.sub_nat(p3, m3))) == C.Int.add(C.Int.add(Int.sub_nat(p1, m1), Int.sub_nat(p2, m2)), Int.sub_nat(p3, m3)) : C.Int}:
  %Equal.sym(C.Int, C.Int.add(Int.sub_nat(p2, m2), Int.sub_nat(p3, m3)), Int.sub_nat(Nat.add(p2, p3), Nat.add(m2, m3)), Int.add_sub(p2, m2, p3, m3)) : {C.Int.add(Int.sub_nat(p1, m1), _) == C.Int.add(C.Int.add(Int.sub_nat(p1, m1), Int.sub_nat(p2, m2)), Int.sub_nat(p3, m3)) : C.Int}
  %Equal.sym(C.Int, C.Int.add(Int.sub_nat(p1, m1), Int.sub_nat(p2, m2)), Int.sub_nat(Nat.add(p1, p2), Nat.add(m1, m2)), Int.add_sub(p1, m1, p2, m2)) : {C.Int.add(Int.sub_nat(p1, m1), Int.sub_nat(Nat.add(p2, p3), Nat.add(m2, m3))) == C.Int.add(_, Int.sub_nat(p3, m3)) : C.Int}
  %Equal.sym(C.Int, C.Int.add(Int.sub_nat(p1, m1), Int.sub_nat(Nat.add(p2, p3), Nat.add(m2, m3))), Int.sub_nat(Nat.add(p1, Nat.add(p2, p3)), Nat.add(m1, Nat.add(m2, m3))), Int.add_sub(p1, m1, Nat.add(p2, p3), Nat.add(m2, m3))) : {_ == C.Int.add(Int.sub_nat(Nat.add(p1, p2), Nat.add(m1, m2)), Int.sub_nat(p3, m3)) : C.Int}
  %Equal.sym(C.Int, C.Int.add(Int.sub_nat(Nat.add(p1, p2), Nat.add(m1, m2)), Int.sub_nat(p3, m3)), Int.sub_nat(Nat.add(Nat.add(p1, p2), p3), Nat.add(Nat.add(m1, m2), m3)), Int.add_sub(Nat.add(p1, p2), Nat.add(m1, m2), p3, m3)) : {Int.sub_nat(Nat.add(p1, Nat.add(p2, p3)), Nat.add(m1, Nat.add(m2, m3))) == _ : C.Int}
  %N.Nat.add_assoc(p1, p2, p3) : {Int.sub_nat(Nat.add(p1, Nat.add(p2, p3)), Nat.add(m1, Nat.add(m2, m3))) == Int.sub_nat(_, Nat.add(Nat.add(m1, m2), m3)) : C.Int}
  %N.Nat.add_assoc(m1, m2, m3) : {Int.sub_nat(Nat.add(p1, Nat.add(p2, p3)), Nat.add(m1, Nat.add(m2, m3))) == Int.sub_nat(Nat.add(p1, Nat.add(p2, p3)), _) : C.Int}
  {==}

def C.add_assoc(a, b, c):
  Int.by3(x => y => z => {C.Int.add(x, C.Int.add(y, z)) == C.Int.add(C.Int.add(x, y), z) : C.Int}, a, b, c,
    Int.add_assoc.core(Int.toP(a), Int.toM(a), Int.toP(b), Int.toM(b), Int.toP(c), Int.toM(c)))

# (k+u) - (k+v) = u - v
def Int.sub_nat_cancel(k: Nat, -u: Nat, -v: Nat) -> {Int.sub_nat(Nat.add(k, u), Nat.add(k, v)) == Int.sub_nat(u, v) : C.Int}:
  match k:
    case 0n:
      {==}
    case 1n+kp:
      Int.sub_nat_cancel(kp, u, v)

# x * (q - n) = xq - xn
def Int.mul_pos(+x: Nat, q: Nat, n: Nat) -> {C.Int.mul(C.Pos{x}, Int.sub_nat(q, n)) == Int.sub_nat(Nat.mul(x, q), Nat.mul(x, n)) : C.Int}:
  match q n:
    case 0n 0n:
      %Equal.sym(Nat, Nat.mul(x, 0n), 0n, N.Nat.mul_zero(x)) : {C.Pos{_} == Int.sub_nat(_, _) : C.Int}
      {==}
    case 0n 1n+np:
      %Equal.sym(Nat, Nat.mul(x, 0n), 0n, N.Nat.mul_zero(x)) : {Int.neg_nat(Nat.mul(x, 1n+np)) == Int.sub_nat(_, Nat.mul(x, 1n+np)) : C.Int}
      Equal.sym(C.Int, Int.sub_nat(0n, Nat.mul(x, 1n+np)), Int.neg_nat(Nat.mul(x, 1n+np)), Int.sub_nat_zero_l(Nat.mul(x, 1n+np)))
    case 1n+qp 0n:
      %Equal.sym(Nat, Nat.mul(x, 0n), 0n, N.Nat.mul_zero(x)) : {C.Pos{Nat.mul(x, 1n+qp)} == Int.sub_nat(Nat.mul(x, 1n+qp), _) : C.Int}
      Equal.sym(C.Int, Int.sub_nat(Nat.mul(x, 1n+qp), 0n), C.Pos{Nat.mul(x, 1n+qp)}, Int.sub_nat_zero(Nat.mul(x, 1n+qp)))
    case 1n+qp 1n+np:
      +qp2 = qp
      +np2 = np
      %Equal.sym(Nat, Nat.mul(x, 1n+qp2), Nat.add(x, Nat.mul(x, qp2)), N.Nat.mul_succ(x, qp2)) : {C.Int.mul(C.Pos{x}, Int.sub_nat(qp2, np2)) == Int.sub_nat(_, Nat.mul(x, 1n+np2)) : C.Int}
      %Equal.sym(Nat, Nat.mul(x, 1n+np2), Nat.add(x, Nat.mul(x, np2)), N.Nat.mul_succ(x, np2)) : {C.Int.mul(C.Pos{x}, Int.sub_nat(qp2, np2)) == Int.sub_nat(Nat.add(x, Nat.mul(x, qp2)), _) : C.Int}
      Equal.trans(C.Int, C.Int.mul(C.Pos{x}, Int.sub_nat(qp2, np2)), Int.sub_nat(Nat.mul(x, qp2), Nat.mul(x, np2)), Int.sub_nat(Nat.add(x, Nat.mul(x, qp2)), Nat.add(x, Nat.mul(x, np2))), Int.mul_pos(x, qp2, np2), Equal.sym(C.Int, Int.sub_nat(Nat.add(x, Nat.mul(x, qp2)), Nat.add(x, Nat.mul(x, np2))), Int.sub_nat(Nat.mul(x, qp2), Nat.mul(x, np2)), Int.sub_nat_cancel(x, Nat.mul(x, qp2), Nat.mul(x, np2))))

# -(1+m) * (q - n) = (1+m)n - (1+m)q
def Int.mul_negs(+m: Nat, q: Nat, n: Nat) -> {C.Int.mul(C.NegS{m}, Int.sub_nat(q, n)) == Int.sub_nat(Nat.mul(1n+m, n), Nat.mul(1n+m, q)) : C.Int}:
  match q n:
    case 0n 0n:
      %Equal.sym(Nat, Nat.mul(1n+m, 0n), 0n, N.Nat.mul_zero(1n+m)) : {Int.neg_nat(_) == Int.sub_nat(_, _) : C.Int}
      {==}
    case 0n 1n+np:
      %Equal.sym(Nat, Nat.mul(1n+m, 0n), 0n, N.Nat.mul_zero(1n+m)) : {C.Pos{Nat.mul(1n+m, 1n+np)} == Int.sub_nat(Nat.mul(1n+m, 1n+np), _) : C.Int}
      Equal.sym(C.Int, Int.sub_nat(Nat.mul(1n+m, 1n+np), 0n), C.Pos{Nat.mul(1n+m, 1n+np)}, Int.sub_nat_zero(Nat.mul(1n+m, 1n+np)))
    case 1n+qp 0n:
      %Equal.sym(Nat, Nat.mul(1n+m, 0n), 0n, N.Nat.mul_zero(1n+m)) : {Int.neg_nat(Nat.mul(1n+m, 1n+qp)) == Int.sub_nat(_, Nat.mul(1n+m, 1n+qp)) : C.Int}
      Equal.sym(C.Int, Int.sub_nat(0n, Nat.mul(1n+m, 1n+qp)), Int.neg_nat(Nat.mul(1n+m, 1n+qp)), Int.sub_nat_zero_l(Nat.mul(1n+m, 1n+qp)))
    case 1n+qp 1n+np:
      +qp2 = qp
      +np2 = np
      %Equal.sym(Nat, Nat.mul(1n+m, 1n+np2), Nat.add(1n+m, Nat.mul(1n+m, np2)), N.Nat.mul_succ(1n+m, np2)) : {C.Int.mul(C.NegS{m}, Int.sub_nat(qp2, np2)) == Int.sub_nat(_, Nat.mul(1n+m, 1n+qp2)) : C.Int}
      %Equal.sym(Nat, Nat.mul(1n+m, 1n+qp2), Nat.add(1n+m, Nat.mul(1n+m, qp2)), N.Nat.mul_succ(1n+m, qp2)) : {C.Int.mul(C.NegS{m}, Int.sub_nat(qp2, np2)) == Int.sub_nat(Nat.add(1n+m, Nat.mul(1n+m, np2)), _) : C.Int}
      Equal.trans(C.Int, C.Int.mul(C.NegS{m}, Int.sub_nat(qp2, np2)), Int.sub_nat(Nat.mul(1n+m, np2), Nat.mul(1n+m, qp2)), Int.sub_nat(Nat.add(1n+m, Nat.mul(1n+m, np2)), Nat.add(1n+m, Nat.mul(1n+m, qp2))), Int.mul_negs(m, qp2, np2), Equal.sym(C.Int, Int.sub_nat(Nat.add(1n+m, Nat.mul(1n+m, np2)), Nat.add(1n+m, Nat.mul(1n+m, qp2))), Int.sub_nat(Nat.mul(1n+m, np2), Nat.mul(1n+m, qp2)), Int.sub_nat_cancel(1n+m, Nat.mul(1n+m, np2), Nat.mul(1n+m, qp2))))

def Int.mul_add.core(a: C.Int, +p2: Nat, +m2: Nat, +p3: Nat, +m3: Nat) -> {C.Int.mul(a, C.Int.add(Int.sub_nat(p2, m2), Int.sub_nat(p3, m3))) == C.Int.add(C.Int.mul(a, Int.sub_nat(p2, m2)), C.Int.mul(a, Int.sub_nat(p3, m3))) : C.Int}:
  match a:
    case C.Pos{x}:
      +x2 = x
      %Equal.sym(C.Int, C.Int.add(Int.sub_nat(p2, m2), Int.sub_nat(p3, m3)), Int.sub_nat(Nat.add(p2, p3), Nat.add(m2, m3)), Int.add_sub(p2, m2, p3, m3)) : {C.Int.mul(C.Pos{x2}, _) == C.Int.add(C.Int.mul(C.Pos{x2}, Int.sub_nat(p2, m2)), C.Int.mul(C.Pos{x2}, Int.sub_nat(p3, m3))) : C.Int}
      %Equal.sym(C.Int, C.Int.mul(C.Pos{x2}, Int.sub_nat(p2, m2)), Int.sub_nat(Nat.mul(x2, p2), Nat.mul(x2, m2)), Int.mul_pos(x2, p2, m2)) : {C.Int.mul(C.Pos{x2}, Int.sub_nat(Nat.add(p2, p3), Nat.add(m2, m3))) == C.Int.add(_, C.Int.mul(C.Pos{x2}, Int.sub_nat(p3, m3))) : C.Int}
      %Equal.sym(C.Int, C.Int.mul(C.Pos{x2}, Int.sub_nat(p3, m3)), Int.sub_nat(Nat.mul(x2, p3), Nat.mul(x2, m3)), Int.mul_pos(x2, p3, m3)) : {C.Int.mul(C.Pos{x2}, Int.sub_nat(Nat.add(p2, p3), Nat.add(m2, m3))) == C.Int.add(Int.sub_nat(Nat.mul(x2, p2), Nat.mul(x2, m2)), _) : C.Int}
      %Equal.sym(C.Int, C.Int.mul(C.Pos{x2}, Int.sub_nat(Nat.add(p2, p3), Nat.add(m2, m3))), Int.sub_nat(Nat.mul(x2, Nat.add(p2, p3)), Nat.mul(x2, Nat.add(m2, m3))), Int.mul_pos(x2, Nat.add(p2, p3), Nat.add(m2, m3))) : {_ == C.Int.add(Int.sub_nat(Nat.mul(x2, p2), Nat.mul(x2, m2)), Int.sub_nat(Nat.mul(x2, p3), Nat.mul(x2, m3))) : C.Int}
      %Equal.sym(C.Int, C.Int.add(Int.sub_nat(Nat.mul(x2, p2), Nat.mul(x2, m2)), Int.sub_nat(Nat.mul(x2, p3), Nat.mul(x2, m3))), Int.sub_nat(Nat.add(Nat.mul(x2, p2), Nat.mul(x2, p3)), Nat.add(Nat.mul(x2, m2), Nat.mul(x2, m3))), Int.add_sub(Nat.mul(x2, p2), Nat.mul(x2, m2), Nat.mul(x2, p3), Nat.mul(x2, m3))) : {Int.sub_nat(Nat.mul(x2, Nat.add(p2, p3)), Nat.mul(x2, Nat.add(m2, m3))) == _ : C.Int}
      %N.Nat.mul_add(x2, p2, p3) : {Int.sub_nat(Nat.mul(x2, Nat.add(p2, p3)), Nat.mul(x2, Nat.add(m2, m3))) == Int.sub_nat(_, Nat.add(Nat.mul(x2, m2), Nat.mul(x2, m3))) : C.Int}
      %N.Nat.mul_add(x2, m2, m3) : {Int.sub_nat(Nat.mul(x2, Nat.add(p2, p3)), Nat.mul(x2, Nat.add(m2, m3))) == Int.sub_nat(Nat.mul(x2, Nat.add(p2, p3)), _) : C.Int}
      {==}
    case C.NegS{m}:
      +m2b = m
      %Equal.sym(C.Int, C.Int.add(Int.sub_nat(p2, m2), Int.sub_nat(p3, m3)), Int.sub_nat(Nat.add(p2, p3), Nat.add(m2, m3)), Int.add_sub(p2, m2, p3, m3)) : {C.Int.mul(C.NegS{m2b}, _) == C.Int.add(C.Int.mul(C.NegS{m2b}, Int.sub_nat(p2, m2)), C.Int.mul(C.NegS{m2b}, Int.sub_nat(p3, m3))) : C.Int}
      %Equal.sym(C.Int, C.Int.mul(C.NegS{m2b}, Int.sub_nat(p2, m2)), Int.sub_nat(Nat.mul(1n+m2b, m2), Nat.mul(1n+m2b, p2)), Int.mul_negs(m2b, p2, m2)) : {C.Int.mul(C.NegS{m2b}, Int.sub_nat(Nat.add(p2, p3), Nat.add(m2, m3))) == C.Int.add(_, C.Int.mul(C.NegS{m2b}, Int.sub_nat(p3, m3))) : C.Int}
      %Equal.sym(C.Int, C.Int.mul(C.NegS{m2b}, Int.sub_nat(p3, m3)), Int.sub_nat(Nat.mul(1n+m2b, m3), Nat.mul(1n+m2b, p3)), Int.mul_negs(m2b, p3, m3)) : {C.Int.mul(C.NegS{m2b}, Int.sub_nat(Nat.add(p2, p3), Nat.add(m2, m3))) == C.Int.add(Int.sub_nat(Nat.mul(1n+m2b, m2), Nat.mul(1n+m2b, p2)), _) : C.Int}
      %Equal.sym(C.Int, C.Int.mul(C.NegS{m2b}, Int.sub_nat(Nat.add(p2, p3), Nat.add(m2, m3))), Int.sub_nat(Nat.mul(1n+m2b, Nat.add(m2, m3)), Nat.mul(1n+m2b, Nat.add(p2, p3))), Int.mul_negs(m2b, Nat.add(p2, p3), Nat.add(m2, m3))) : {_ == C.Int.add(Int.sub_nat(Nat.mul(1n+m2b, m2), Nat.mul(1n+m2b, p2)), Int.sub_nat(Nat.mul(1n+m2b, m3), Nat.mul(1n+m2b, p3))) : C.Int}
      %Equal.sym(C.Int, C.Int.add(Int.sub_nat(Nat.mul(1n+m2b, m2), Nat.mul(1n+m2b, p2)), Int.sub_nat(Nat.mul(1n+m2b, m3), Nat.mul(1n+m2b, p3))), Int.sub_nat(Nat.add(Nat.mul(1n+m2b, m2), Nat.mul(1n+m2b, m3)), Nat.add(Nat.mul(1n+m2b, p2), Nat.mul(1n+m2b, p3))), Int.add_sub(Nat.mul(1n+m2b, m2), Nat.mul(1n+m2b, p2), Nat.mul(1n+m2b, m3), Nat.mul(1n+m2b, p3))) : {Int.sub_nat(Nat.mul(1n+m2b, Nat.add(m2, m3)), Nat.mul(1n+m2b, Nat.add(p2, p3))) == _ : C.Int}
      %N.Nat.mul_add(1n+m2b, m2, m3) : {Int.sub_nat(Nat.mul(1n+m2b, Nat.add(m2, m3)), Nat.mul(1n+m2b, Nat.add(p2, p3))) == Int.sub_nat(_, Nat.add(Nat.mul(1n+m2b, p2), Nat.mul(1n+m2b, p3))) : C.Int}
      %N.Nat.mul_add(1n+m2b, p2, p3) : {Int.sub_nat(Nat.mul(1n+m2b, Nat.add(m2, m3)), Nat.mul(1n+m2b, Nat.add(p2, p3))) == Int.sub_nat(Nat.mul(1n+m2b, Nat.add(m2, m3)), _) : C.Int}
      {==}

def C.mul_add(a, b, c):
  Int.by2(y => z => {C.Int.mul(a, C.Int.add(y, z)) == C.Int.add(C.Int.mul(a, y), C.Int.mul(a, z)) : C.Int}, b, c,
    Int.mul_add.core(a, Int.toP(b), Int.toM(b), Int.toP(c), Int.toM(c)))

# ---- the order, through the same view -----------------------------------
#
# Int.Le(p - m, q - n) is exactly Nat.Le(p + n, q + m). Both directions
# are needed: the forward one to open a hypothesis, the backward one to
# close a goal.

def Int.le_cast(-x: Nat, -y: Nat, -c: Nat, e: {x == y : Nat}, h: N.Nat.Le(x, c)) -> N.Nat.Le(y, c):
  %e : N.Nat.Le(_, c)
  h

def Int.le_cast_r(-x: Nat, -y: Nat, -c: Nat, e: {x == y : Nat}, h: N.Nat.Le(c, x)) -> N.Nat.Le(c, y):
  %e : N.Nat.Le(c, _)
  h

def Int.le_succ_zero(-k: Nat, h: N.Nat.Le(1n+k, 0n)) -> Empty:
  match h:

def Int.neg_sub(p: Nat, m: Nat) -> {C.Int.neg(Int.sub_nat(p, m)) == Int.sub_nat(m, p) : C.Int}:
  match p m:
    case 0n 0n:
      {==}
    case 0n 1n+mp:
      {==}
    case 1n+pp 0n:
      {==}
    case 1n+pp 1n+mp:
      Int.neg_sub(pp, mp)

def Int.le_pos_f(+x: Nat, q: Nat, n: Nat, h: C.Int.Le(C.Pos{x}, Int.sub_nat(q, n))) -> N.Nat.Le(Nat.add(x, n), q):
  match q n:
    case 0n 0n:
      %Equal.sym(Nat, Nat.add(x, 0n), x, N.Nat.add_zero(x)) : N.Nat.Le(_, 0n)
      h
    case 0n 1n+np:
      match h:
    case 1n+qp 0n:
      %Equal.sym(Nat, Nat.add(x, 0n), x, N.Nat.add_zero(x)) : N.Nat.Le(_, 1n+qp)
      h
    case 1n+qp 1n+np:
      %Equal.sym(Nat, Nat.add(x, 1n+np), 1n+Nat.add(x, np), N.Nat.add_succ(x, np)) : N.Nat.Le(_, 1n+qp)
      Int.le_pos_f(x, qp, np, h)

def Int.le_negs_f(+m: Nat, q: Nat, n: Nat, h: C.Int.Le(C.NegS{m}, Int.sub_nat(q, n))) -> N.Nat.Le(n, Nat.add(q, 1n+m)):
  match q n:
    case 0n 0n:
      Unit{}
    case 0n 1n+np:
      h
    case 1n+qp 0n:
      Unit{}
    case 1n+qp 1n+np:
      Int.le_negs_f(m, qp, np, h)

def Int.le_f(p: Nat, m: Nat, +q: Nat, +n: Nat, h: C.Int.Le(Int.sub_nat(p, m), Int.sub_nat(q, n))) -> N.Nat.Le(Nat.add(p, n), Nat.add(q, m)):
  match p m:
    case 0n 0n:
      %Equal.sym(Nat, Nat.add(q, 0n), q, N.Nat.add_zero(q)) : N.Nat.Le(Nat.add(0n, n), _)
      Int.le_pos_f(0n, q, n, h)
    case 0n 1n+mp:
      Int.le_negs_f(mp, q, n, h)
    case 1n+pp 0n:
      %Equal.sym(Nat, Nat.add(q, 0n), q, N.Nat.add_zero(q)) : N.Nat.Le(Nat.add(1n+pp, n), _)
      Int.le_pos_f(1n+pp, q, n, h)
    case 1n+pp 1n+mp:
      %Equal.sym(Nat, Nat.add(q, 1n+mp), 1n+Nat.add(q, mp), N.Nat.add_succ(q, mp)) : N.Nat.Le(Nat.add(1n+pp, n), _)
      Int.le_f(pp, mp, q, n, h)

def Int.le_pos_b(+x: Nat, q: Nat, n: Nat, h: N.Nat.Le(Nat.add(x, n), q)) -> C.Int.Le(C.Pos{x}, Int.sub_nat(q, n)):
  match q n:
    case 0n 0n:
      %N.Nat.add_zero(x) : N.Nat.Le(_, 0n)
      h
    case 0n 1n+np:
      Empty.absurd(C.Int.Le(C.Pos{x}, C.NegS{np}), Int.le_succ_zero(Nat.add(x, np), Int.le_cast(Nat.add(x, 1n+np), 1n+Nat.add(x, np), 0n, N.Nat.add_succ(x, np), h)))
    case 1n+qp 0n:
      %N.Nat.add_zero(x) : N.Nat.Le(_, 1n+qp)
      h
    case 1n+qp 1n+np:
      Int.le_pos_b(x, qp, np, Int.le_cast(Nat.add(x, 1n+np), 1n+Nat.add(x, np), 1n+qp, N.Nat.add_succ(x, np), h))

def Int.le_negs_b(+m: Nat, q: Nat, n: Nat, h: N.Nat.Le(n, Nat.add(q, 1n+m))) -> C.Int.Le(C.NegS{m}, Int.sub_nat(q, n)):
  match q n:
    case 0n 0n:
      Unit{}
    case 0n 1n+np:
      h
    case 1n+qp 0n:
      Unit{}
    case 1n+qp 1n+np:
      Int.le_negs_b(m, qp, np, h)

def Int.le_b(p: Nat, m: Nat, +q: Nat, +n: Nat, h: N.Nat.Le(Nat.add(p, n), Nat.add(q, m))) -> C.Int.Le(Int.sub_nat(p, m), Int.sub_nat(q, n)):
  match p m:
    case 0n 0n:
      Int.le_pos_b(0n, q, n, Int.le_cast_r(Nat.add(q, 0n), q, Nat.add(0n, n), N.Nat.add_zero(q), h))
    case 0n 1n+mp:
      Int.le_negs_b(mp, q, n, h)
    case 1n+pp 0n:
      Int.le_pos_b(1n+pp, q, n, Int.le_cast_r(Nat.add(q, 0n), q, Nat.add(1n+pp, n), N.Nat.add_zero(q), h))
    case 1n+pp 1n+mp:
      Int.le_b(pp, mp, q, n, Int.le_cast_r(Nat.add(q, 1n+mp), 1n+Nat.add(q, mp), Nat.add(1n+pp, n), N.Nat.add_succ(q, mp), h))

def Int.le_split(+a: C.Int, +b: C.Int, h: C.Int.Le(a, b)) -> C.Int.Le(Int.sub_nat(Int.toP(a), Int.toM(a)), Int.sub_nat(Int.toP(b), Int.toM(b))):
  %Equal.sym(C.Int, Int.sub_nat(Int.toP(a), Int.toM(a)), a, Int.split(a)) : C.Int.Le(_, Int.sub_nat(Int.toP(b), Int.toM(b)))
  %Equal.sym(C.Int, Int.sub_nat(Int.toP(b), Int.toM(b)), b, Int.split(b)) : C.Int.Le(a, _)
  h

def Int.le_neg.core(+pa: Nat, +ma: Nat, +pb: Nat, +mb: Nat, h: N.Nat.Le(Nat.add(pa, mb), Nat.add(pb, ma))) -> C.Int.Le(C.Int.neg(Int.sub_nat(pb, mb)), C.Int.neg(Int.sub_nat(pa, ma))):
  %Equal.sym(C.Int, C.Int.neg(Int.sub_nat(pb, mb)), Int.sub_nat(mb, pb), Int.neg_sub(pb, mb)) : C.Int.Le(_, C.Int.neg(Int.sub_nat(pa, ma)))
  %Equal.sym(C.Int, C.Int.neg(Int.sub_nat(pa, ma)), Int.sub_nat(ma, pa), Int.neg_sub(pa, ma)) : C.Int.Le(Int.sub_nat(mb, pb), _)
  Int.le_b(mb, pb, ma, pa,
    Int.le_cast_r(Nat.add(pb, ma), Nat.add(ma, pb), Nat.add(mb, pa), N.Nat.add_comm(pb, ma),
      Int.le_cast(Nat.add(pa, mb), Nat.add(mb, pa), Nat.add(pb, ma), N.Nat.add_comm(pa, mb), h)))

def C.le_neg(a, b, h):
  Int.by2(x => y => C.Int.Le(C.Int.neg(y), C.Int.neg(x)), a, b,
    Int.le_neg.core(Int.toP(a), Int.toM(a), Int.toP(b), Int.toM(b),
      Int.le_f(Int.toP(a), Int.toM(a), Int.toP(b), Int.toM(b), Int.le_split(a, b, h))))

def Int.le_add.core(+pa: Nat, +ma: Nat, +pb: Nat, +mb: Nat, +pc: Nat, +mc: Nat, h: N.Nat.Le(Nat.add(pa, mb), Nat.add(pb, ma))) -> C.Int.Le(C.Int.add(Int.sub_nat(pa, ma), Int.sub_nat(pc, mc)), C.Int.add(Int.sub_nat(pb, mb), Int.sub_nat(pc, mc))):
  %Equal.sym(C.Int, C.Int.add(Int.sub_nat(pa, ma), Int.sub_nat(pc, mc)), Int.sub_nat(Nat.add(pa, pc), Nat.add(ma, mc)), Int.add_sub(pa, ma, pc, mc)) : C.Int.Le(_, C.Int.add(Int.sub_nat(pb, mb), Int.sub_nat(pc, mc)))
  %Equal.sym(C.Int, C.Int.add(Int.sub_nat(pb, mb), Int.sub_nat(pc, mc)), Int.sub_nat(Nat.add(pb, pc), Nat.add(mb, mc)), Int.add_sub(pb, mb, pc, mc)) : C.Int.Le(Int.sub_nat(Nat.add(pa, pc), Nat.add(ma, mc)), _)
  Int.le_b(Nat.add(pa, pc), Nat.add(ma, mc), Nat.add(pb, pc), Nat.add(mb, mc),
    Int.le_cast_r(Nat.add(Nat.add(pb, ma), Nat.add(pc, mc)), Nat.add(Nat.add(pb, pc), Nat.add(ma, mc)), Nat.add(Nat.add(pa, pc), Nat.add(mb, mc)), N.Nat.add_add_swap(pb, ma, pc, mc),
      Int.le_cast(Nat.add(Nat.add(pa, mb), Nat.add(pc, mc)), Nat.add(Nat.add(pa, pc), Nat.add(mb, mc)), Nat.add(Nat.add(pb, ma), Nat.add(pc, mc)), N.Nat.add_add_swap(pa, mb, pc, mc),
        N.Nat.le_add_both(Nat.add(pa, mb), Nat.add(pb, ma), Nat.add(pc, mc), Nat.add(pc, mc), h, N.Nat.le_refl(Nat.add(pc, mc))))))

def C.le_add(a, b, c, h):
  Int.by3(x => y => z => C.Int.Le(C.Int.add(x, z), C.Int.add(y, z)), a, b, c,
    Int.le_add.core(Int.toP(a), Int.toM(a), Int.toP(b), Int.toM(b), Int.toP(c), Int.toM(c),
      Int.le_f(Int.toP(a), Int.toM(a), Int.toP(b), Int.toM(b), Int.le_split(a, b, h))))

# x <= y gives xc <= yc
def Int.le_mul_r(x: Nat, y: Nat, +c: Nat, h: N.Nat.Le(x, y)) -> N.Nat.Le(Nat.mul(x, c), Nat.mul(y, c)):
  match x y:
    case 0n 0n:
      Unit{}
    case 0n 1n+yp:
      Unit{}
    case 1n+xp 0n:
      match h:
    case 1n+xp 1n+yp:
      N.Nat.le_add_mono_l(c, Nat.mul(xp, c), Nat.mul(yp, c), Int.le_mul_r(xp, yp, c, h))

# (p - m) * k = pk - mk
def Int.mul_sub_r(+p: Nat, +m: Nat, +k: Nat) -> {C.Int.mul(Int.sub_nat(p, m), C.Pos{k}) == Int.sub_nat(Nat.mul(p, k), Nat.mul(m, k)) : C.Int}:
  %Equal.sym(C.Int, C.Int.mul(Int.sub_nat(p, m), C.Pos{k}), C.Int.mul(C.Pos{k}, Int.sub_nat(p, m)), C.mul_comm(Int.sub_nat(p, m), C.Pos{k})) : {_ == Int.sub_nat(Nat.mul(p, k), Nat.mul(m, k)) : C.Int}
  %Equal.sym(C.Int, C.Int.mul(C.Pos{k}, Int.sub_nat(p, m)), Int.sub_nat(Nat.mul(k, p), Nat.mul(k, m)), Int.mul_pos(k, p, m)) : {_ == Int.sub_nat(Nat.mul(p, k), Nat.mul(m, k)) : C.Int}
  %N.Nat.mul_comm(k, p) : {Int.sub_nat(Nat.mul(k, p), Nat.mul(k, m)) == Int.sub_nat(_, Nat.mul(m, k)) : C.Int}
  %N.Nat.mul_comm(k, m) : {Int.sub_nat(Nat.mul(k, p), Nat.mul(k, m)) == Int.sub_nat(Nat.mul(k, p), _) : C.Int}
  {==}

def Int.le_mul.core(+pa: Nat, +ma: Nat, +pb: Nat, +mb: Nat, +k: Nat, h: N.Nat.Le(Nat.add(pa, mb), Nat.add(pb, ma))) -> C.Int.Le(C.Int.mul(Int.sub_nat(pa, ma), C.Pos{k}), C.Int.mul(Int.sub_nat(pb, mb), C.Pos{k})):
  %Equal.sym(C.Int, C.Int.mul(Int.sub_nat(pa, ma), C.Pos{k}), Int.sub_nat(Nat.mul(pa, k), Nat.mul(ma, k)), Int.mul_sub_r(pa, ma, k)) : C.Int.Le(_, C.Int.mul(Int.sub_nat(pb, mb), C.Pos{k}))
  %Equal.sym(C.Int, C.Int.mul(Int.sub_nat(pb, mb), C.Pos{k}), Int.sub_nat(Nat.mul(pb, k), Nat.mul(mb, k)), Int.mul_sub_r(pb, mb, k)) : C.Int.Le(Int.sub_nat(Nat.mul(pa, k), Nat.mul(ma, k)), _)
  Int.le_b(Nat.mul(pa, k), Nat.mul(ma, k), Nat.mul(pb, k), Nat.mul(mb, k),
    Int.le_cast_r(Nat.mul(Nat.add(pb, ma), k), Nat.add(Nat.mul(pb, k), Nat.mul(ma, k)), Nat.add(Nat.mul(pa, k), Nat.mul(mb, k)), N.Nat.add_mul(pb, ma, k),
      Int.le_cast(Nat.mul(Nat.add(pa, mb), k), Nat.add(Nat.mul(pa, k), Nat.mul(mb, k)), Nat.mul(Nat.add(pb, ma), k), N.Nat.add_mul(pa, mb, k),
        Int.le_mul_r(Nat.add(pa, mb), Nat.add(pb, ma), k, h))))

def C.le_mul_nonneg(a, b, n, h):
  Int.by2(x => y => C.Int.Le(C.Int.mul(x, C.Pos{n}), C.Int.mul(y, C.Pos{n})), a, b,
    Int.le_mul.core(Int.toP(a), Int.toM(a), Int.toP(b), Int.toM(b), n,
      Int.le_f(Int.toP(a), Int.toM(a), Int.toP(b), Int.toM(b), Int.le_split(a, b, h))))

# ---- the sign/magnitude view (what multiplication really is) ------------

def Int.sgn(s: Bool, k: Nat) -> C.Int:
  match s:
    case True{}:
      C.Pos{k}
    case False{}:
      Int.neg_nat(k)

def Int.sign(a: C.Int) -> Bool:
  match a:
    case C.Pos{n}:
      True{}
    case C.NegS{n}:
      False{}

def Int.sgn_split(a: C.Int) -> {Int.sgn(Int.sign(a), C.Int.abs(a)) == a : C.Int}:
  match a:
    case C.Pos{n}:
      {==}
    case C.NegS{n}:
      {==}

def Int.mul_pos_neg(+k: Nat, j: Nat) -> {C.Int.mul(C.Pos{k}, Int.neg_nat(j)) == Int.neg_nat(Nat.mul(k, j)) : C.Int}:
  match j:
    case 0n:
      %Equal.sym(Nat, Nat.mul(k, 0n), 0n, N.Nat.mul_zero(k)) : {C.Pos{_} == Int.neg_nat(_) : C.Int}
      {==}
    case 1n+jp:
      {==}

def Int.mul_neg_pos(k: Nat, +j: Nat) -> {C.Int.mul(Int.neg_nat(k), C.Pos{j}) == Int.neg_nat(Nat.mul(k, j)) : C.Int}:
  match k:
    case 0n:
      {==}
    case 1n+kp:
      {==}

def Int.mul_neg_neg(k: Nat, j: Nat) -> {C.Int.mul(Int.neg_nat(k), Int.neg_nat(j)) == C.Pos{Nat.mul(k, j)} : C.Int}:
  match k j:
    case 0n 0n:
      {==}
    case 0n 1n+jp:
      {==}
    case 1n+kp 0n:
      %Equal.sym(Nat, Nat.mul(1n+kp, 0n), 0n, N.Nat.mul_zero(1n+kp)) : {Int.neg_nat(_) == C.Pos{_} : C.Int}
      {==}
    case 1n+kp 1n+jp:
      {==}

def Int.mul_sgn(s: Bool, t: Bool, +k: Nat, +j: Nat) -> {C.Int.mul(Int.sgn(s, k), Int.sgn(t, j)) == Int.sgn(N.Bool.same(s, t), Nat.mul(k, j)) : C.Int}:
  match s t:
    case True{} True{}:
      {==}
    case True{} False{}:
      Int.mul_pos_neg(k, j)
    case False{} True{}:
      Int.mul_neg_pos(k, j)
    case False{} False{}:
      Int.mul_neg_neg(k, j)

def Int.same_assoc(a: Bool, b: Bool, c: Bool) -> {N.Bool.same(a, N.Bool.same(b, c)) == N.Bool.same(N.Bool.same(a, b), c) : Bool}:
  match a b c:
    case True{} True{} True{}:
      {==}
    case True{} True{} False{}:
      {==}
    case True{} False{} True{}:
      {==}
    case True{} False{} False{}:
      {==}
    case False{} True{} True{}:
      {==}
    case False{} True{} False{}:
      {==}
    case False{} False{} True{}:
      {==}
    case False{} False{} False{}:
      {==}

def Int.sby3(-P: C.Int -> C.Int -> C.Int -> Type, +a: C.Int, +b: C.Int, +c: C.Int, w: P(Int.sgn(Int.sign(a), C.Int.abs(a)), Int.sgn(Int.sign(b), C.Int.abs(b)), Int.sgn(Int.sign(c), C.Int.abs(c)))) -> P(a, b, c):
  %Int.sgn_split(a) : P(_, b, c)
  %Int.sgn_split(b) : P(Int.sgn(Int.sign(a), C.Int.abs(a)), _, c)
  %Int.sgn_split(c) : P(Int.sgn(Int.sign(a), C.Int.abs(a)), Int.sgn(Int.sign(b), C.Int.abs(b)), _)
  w

def Int.mul_assoc.core(+sa: Bool, +ka: Nat, +sb: Bool, +kb: Nat, +sc: Bool, +kc: Nat) -> {C.Int.mul(Int.sgn(sa, ka), C.Int.mul(Int.sgn(sb, kb), Int.sgn(sc, kc))) == C.Int.mul(C.Int.mul(Int.sgn(sa, ka), Int.sgn(sb, kb)), Int.sgn(sc, kc)) : C.Int}:
  %Equal.sym(C.Int, C.Int.mul(Int.sgn(sb, kb), Int.sgn(sc, kc)), Int.sgn(N.Bool.same(sb, sc), Nat.mul(kb, kc)), Int.mul_sgn(sb, sc, kb, kc)) : {C.Int.mul(Int.sgn(sa, ka), _) == C.Int.mul(C.Int.mul(Int.sgn(sa, ka), Int.sgn(sb, kb)), Int.sgn(sc, kc)) : C.Int}
  %Equal.sym(C.Int, C.Int.mul(Int.sgn(sa, ka), Int.sgn(sb, kb)), Int.sgn(N.Bool.same(sa, sb), Nat.mul(ka, kb)), Int.mul_sgn(sa, sb, ka, kb)) : {C.Int.mul(Int.sgn(sa, ka), Int.sgn(N.Bool.same(sb, sc), Nat.mul(kb, kc))) == C.Int.mul(_, Int.sgn(sc, kc)) : C.Int}
  %Equal.sym(C.Int, C.Int.mul(Int.sgn(sa, ka), Int.sgn(N.Bool.same(sb, sc), Nat.mul(kb, kc))), Int.sgn(N.Bool.same(sa, N.Bool.same(sb, sc)), Nat.mul(ka, Nat.mul(kb, kc))), Int.mul_sgn(sa, N.Bool.same(sb, sc), ka, Nat.mul(kb, kc))) : {_ == C.Int.mul(Int.sgn(N.Bool.same(sa, sb), Nat.mul(ka, kb)), Int.sgn(sc, kc)) : C.Int}
  %Equal.sym(C.Int, C.Int.mul(Int.sgn(N.Bool.same(sa, sb), Nat.mul(ka, kb)), Int.sgn(sc, kc)), Int.sgn(N.Bool.same(N.Bool.same(sa, sb), sc), Nat.mul(Nat.mul(ka, kb), kc)), Int.mul_sgn(N.Bool.same(sa, sb), sc, Nat.mul(ka, kb), kc)) : {Int.sgn(N.Bool.same(sa, N.Bool.same(sb, sc)), Nat.mul(ka, Nat.mul(kb, kc))) == _ : C.Int}
  %Int.same_assoc(sa, sb, sc) : {Int.sgn(N.Bool.same(sa, N.Bool.same(sb, sc)), Nat.mul(ka, Nat.mul(kb, kc))) == Int.sgn(_, Nat.mul(Nat.mul(ka, kb), kc)) : C.Int}
  %N.Nat.mul_assoc(ka, kb, kc) : {Int.sgn(N.Bool.same(sa, N.Bool.same(sb, sc)), Nat.mul(ka, Nat.mul(kb, kc))) == Int.sgn(N.Bool.same(sa, N.Bool.same(sb, sc)), _) : C.Int}
  {==}

def C.mul_assoc(a, b, c):
  Int.sby3(x => y => z => {C.Int.mul(x, C.Int.mul(y, z)) == C.Int.mul(C.Int.mul(x, y), z) : C.Int}, a, b, c,
    Int.mul_assoc.core(Int.sign(a), C.Int.abs(a), Int.sign(b), C.Int.abs(b), Int.sign(c), C.Int.abs(c)))

# ---- no zero divisors ---------------------------------------------------

def Int.abs_neg_nat(k: Nat) -> {C.Int.abs(Int.neg_nat(k)) == k : Nat}:
  match k:
    case 0n:
      {==}
    case 1n+kp:
      {==}

def Int.abs_mul(a: C.Int, b: C.Int) -> {C.Int.abs(C.Int.mul(a, b)) == Nat.mul(C.Int.abs(a), C.Int.abs(b)) : Nat}:
  match a b:
    case C.Pos{x} C.Pos{y}:
      {==}
    case C.Pos{x} C.NegS{y}:
      Int.abs_neg_nat(Nat.mul(x, 1n+y))
    case C.NegS{x} C.Pos{y}:
      Int.abs_neg_nat(Nat.mul(1n+x, y))
    case C.NegS{x} C.NegS{y}:
      {==}

def Int.abs_zero(a: C.Int, e: {C.Int.abs(a) == 0n : Nat}) -> {a == C.Pos{0n} : C.Int}:
  match a:
    case C.Pos{n}:
      %e : {C.Pos{n} == C.Pos{_} : C.Int}
      {==}
    case C.NegS{n}:
      Empty.absurd({C.NegS{n} == C.Pos{0n} : C.Int}, N.Nat.succ_neq_zero(n, e))

def Int.mul_eq_zero_nat(x: Nat, +y: Nat, e: {Nat.mul(x, y) == 0n : Nat}, nx: {x == 0n : Nat} -> Empty) -> {y == 0n : Nat}:
  match x:
    case 0n:
      Empty.absurd({y == 0n : Nat}, nx({==}))
    case 1n+xp:
      N.Nat.add_eq_zero_l(y, Nat.mul(xp, y), e)

def C.mul_eq_zero(a, b, e, na):
  Int.abs_zero(b, Int.mul_eq_zero_nat(C.Int.abs(a), C.Int.abs(b),
    Equal.trans(Nat, Nat.mul(C.Int.abs(a), C.Int.abs(b)), C.Int.abs(C.Int.mul(a, b)), 0n,
      Equal.sym(Nat, C.Int.abs(C.Int.mul(a, b)), Nat.mul(C.Int.abs(a), C.Int.abs(b)), Int.abs_mul(a, b)),
      Equal.cong(C.Int, Nat, z => C.Int.abs(z), C.Int.mul(a, b), C.Pos{0n}, e)),
    k => na(Int.abs_zero(a, k))))
