CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

| Download

Try doing some basic maths questions in the Lean Theorem Prover. Functions, real numbers, equivalence relations and groups. Click on README.md and then on "Open in CoCalc with one click".

Project: Xena
Views: 18536
License: APACHE
/-
Copyright (c) 2014 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Mario Carneiro

Properties of the binary representation of integers.
-/
import data.num.basic data.num.bitwise algebra.ordered_ring
       tactic.interactive data.int.basic data.nat.gcd

namespace pos_num
  variables {α : Type*}

  @[simp] theorem cast_one [has_zero α] [has_one α] [has_add α] : ((1 : pos_num) : α) = 1 := rfl
  @[simp] theorem cast_one' [has_zero α] [has_one α] [has_add α] : (pos_num.one : α) = 1 := rfl
  @[simp] theorem cast_bit0 [has_zero α] [has_one α] [has_add α] (n : pos_num) : (n.bit0 : α) = _root_.bit0 n := rfl
  @[simp] theorem cast_bit1 [has_zero α] [has_one α] [has_add α] (n : pos_num) : (n.bit1 : α) = _root_.bit1 n := rfl

  @[simp] theorem cast_to_nat [add_monoid α] [has_one α] : ∀ n : pos_num, ((n : ℕ) : α) = n
  | 1        := nat.cast_one
  | (bit0 p) := (nat.cast_bit0 _).trans $ congr_arg _root_.bit0 p.cast_to_nat
  | (bit1 p) := (nat.cast_bit1 _).trans $ congr_arg _root_.bit1 p.cast_to_nat

  @[simp] theorem to_nat_to_int (n : pos_num) : ((n : ℕ) : ℤ) = n :=
  by rw [← int.nat_cast_eq_coe_nat, cast_to_nat]

  @[simp] theorem cast_to_int [add_group α] [has_one α] (n : pos_num) : ((n : ℤ) : α) = n :=
  by rw [← to_nat_to_int, int.cast_coe_nat, cast_to_nat]

  theorem succ_to_nat : ∀ n, (succ n : ℕ) = n + 1
  | 1        := rfl
  | (bit0 p) := rfl
  | (bit1 p) := (congr_arg _root_.bit0 (succ_to_nat p)).trans $
    show ↑p + 1 + ↑p + 1 = ↑p + ↑p + 1 + 1, by simp

  theorem one_add (n : pos_num) : 1 + n = succ n := by cases n; refl
  theorem add_one (n : pos_num) : n + 1 = succ n := by cases n; refl

  @[simp] theorem add_to_nat : ∀ m n, ((m + n : pos_num) : ℕ) = m + n
  | 1        b        := by rw [one_add b, succ_to_nat, add_comm]; refl
  | a        1        := by rw [add_one a, succ_to_nat]; refl
  | (bit0 a) (bit0 b) := (congr_arg _root_.bit0 (add_to_nat a b)).trans $
    show ((a + b) + (a + b) : ℕ) = (a + a) + (b + b), by simp
  | (bit0 a) (bit1 b) := (congr_arg _root_.bit1 (add_to_nat a b)).trans $
    show ((a + b) + (a + b) + 1 : ℕ) = (a + a) + (b + b + 1), by simp
  | (bit1 a) (bit0 b) := (congr_arg _root_.bit1 (add_to_nat a b)).trans $
    show ((a + b) + (a + b) + 1 : ℕ) = (a + a + 1) + (b + b), by simp
  | (bit1 a) (bit1 b) :=
    show (succ (a + b) + succ (a + b) : ℕ) = (a + a + 1) + (b + b + 1),
    by rw [succ_to_nat, add_to_nat]; simp

  theorem add_succ : ∀ (m n : pos_num), m + succ n = succ (m + n)
  | 1        b        := by simp [one_add]
  | (bit0 a) 1        := congr_arg bit0 (add_one a)
  | (bit1 a) 1        := congr_arg bit1 (add_one a)
  | (bit0 a) (bit0 b) := rfl
  | (bit0 a) (bit1 b) := congr_arg bit0 (add_succ a b)
  | (bit1 a) (bit0 b) := rfl
  | (bit1 a) (bit1 b) := congr_arg bit1 (add_succ a b)

  theorem bit0_of_bit0 : Π n, _root_.bit0 n = bit0 n
  | 1        := rfl
  | (bit0 p) := congr_arg bit0 (bit0_of_bit0 p)
  | (bit1 p) := show bit0 (succ (_root_.bit0 p)) = _, by rw bit0_of_bit0; refl

  theorem bit1_of_bit1 (n : pos_num) : _root_.bit1 n = bit1 n :=
  show _root_.bit0 n + 1 = bit1 n, by rw [add_one, bit0_of_bit0]; refl

  @[simp] theorem mul_to_nat (m) : ∀ n, ((m * n : pos_num) : ℕ) = m * n
  | 1        := (mul_one _).symm
  | (bit0 p) := show (↑(m * p) + ↑(m * p) : ℕ) = ↑m * (p + p), by rw [mul_to_nat, left_distrib]
  | (bit1 p) := (add_to_nat (bit0 (m * p)) m).trans $
    show (↑(m * p) + ↑(m * p) + ↑m : ℕ) = ↑m * (p + p) + m, by rw [mul_to_nat, left_distrib]

  theorem to_nat_pos : ∀ n : pos_num, 0 < (n : ℕ)
  | 1        := zero_lt_one
  | (bit0 p) := let h := to_nat_pos p in add_pos h h
  | (bit1 p) := nat.succ_pos _

  theorem cmp_to_nat_lemma {m n : pos_num} : (m:ℕ) < n → (bit1 m : ℕ) < bit0 n :=
  show (m:ℕ) < n → (m + m + 1 + 1 : ℕ) ≤ n + n,
  by intro h; rw [nat.add_right_comm m m 1, add_assoc]; exact add_le_add h h

  theorem cmp_swap (m) : ∀n, (cmp m n).swap = cmp n m :=
  by induction m with m IH m IH; intro n;
     cases n with n n; try {unfold cmp}; try {refl}; rw ←IH; cases cmp m n; refl

  theorem cmp_to_nat : ∀ (m n), (ordering.cases_on (cmp m n) ((m:ℕ) < n) (m = n) ((m:ℕ) > n) : Prop)
  | 1        1        := rfl
  | (bit0 a) 1        := let h : (1:ℕ) ≤ a := to_nat_pos a in add_le_add h h
  | (bit1 a) 1        := nat.succ_lt_succ $ to_nat_pos $ bit0 a
  | 1        (bit0 b) := let h : (1:ℕ) ≤ b := to_nat_pos b in add_le_add h h
  | 1        (bit1 b) := nat.succ_lt_succ $ to_nat_pos $ bit0 b
  | (bit0 a) (bit0 b) := begin
      have := cmp_to_nat a b, revert this, cases cmp a b; dsimp; intro,
      { exact add_lt_add this this },
      { rw this },
      { exact add_lt_add this this }
    end
  | (bit0 a) (bit1 b) := begin dsimp [cmp],
      have := cmp_to_nat a b, revert this, cases cmp a b; dsimp; intro,
      { exact nat.le_succ_of_le (add_lt_add this this) },
      { rw this, apply nat.lt_succ_self },
      { exact cmp_to_nat_lemma this }
    end
  | (bit1 a) (bit0 b) := begin dsimp [cmp],
      have := cmp_to_nat a b, revert this, cases cmp a b; dsimp; intro,
      { exact cmp_to_nat_lemma this },
      { rw this, apply nat.lt_succ_self },
      { exact nat.le_succ_of_le (add_lt_add this this) },
    end
  | (bit1 a) (bit1 b) := begin
      have := cmp_to_nat a b, revert this, cases cmp a b; dsimp; intro,
      { exact nat.succ_lt_succ (add_lt_add this this) },
      { rw this },
      { exact nat.succ_lt_succ (add_lt_add this this) }
    end

  @[simp] theorem lt_to_nat {m n : pos_num} : (m:ℕ) < n ↔ m < n :=
  show (m:ℕ) < n ↔ cmp m n = ordering.lt, from
  match cmp m n, cmp_to_nat m n with
  | ordering.lt, h := by simp at h; simp [h]
  | ordering.eq, h := by simp at h; simp [h, lt_irrefl]; exact dec_trivial
  | ordering.gt, h := by simp [not_lt_of_gt h]; exact dec_trivial
  end

  @[simp] theorem le_to_nat {m n : pos_num} : (m:ℕ) ≤ n ↔ m ≤ n :=
  by rw ← not_lt; exact not_congr lt_to_nat

end pos_num

namespace num
  variables {α : Type*}
  open pos_num

  theorem add_zero (n : num) : n + 0 = n := by cases n; refl
  theorem zero_add (n : num) : 0 + n = n := by cases n; refl

  theorem add_one : ∀ n : num, n + 1 = succ n
  | 0       := rfl
  | (pos p) := by cases p; refl

  theorem add_succ : ∀ (m n : num), m + succ n = succ (m + n)
  | 0       n       := by simp [zero_add]
  | (pos p) 0       := show pos (p + 1) = succ (pos p + 0),
                       by rw [pos_num.add_one, add_zero]; refl
  | (pos p) (pos q) := congr_arg pos (pos_num.add_succ _ _)

  @[simp] theorem add_of_nat (m) : ∀ n, ((m + n : ℕ) : num) = m + n
  | 0     := (add_zero _).symm
  | (n+1) := show ((m + n : ℕ) + 1 : num) = m + (↑ n + 1),
             by rw [add_one, add_one, add_succ, add_of_nat]

  theorem bit0_of_bit0 : ∀ n : num, bit0 n = n.bit0
  | 0       := rfl
  | (pos p) := congr_arg pos p.bit0_of_bit0

  theorem bit1_of_bit1 : ∀ n : num, bit1 n = n.bit1
  | 0       := rfl
  | (pos p) := congr_arg pos p.bit1_of_bit1

  @[simp] theorem cast_zero [has_zero α] [has_one α] [has_add α] :
    ((0 : num) : α) = 0 := rfl

  @[simp] theorem cast_zero' [has_zero α] [has_one α] [has_add α] :
    (num.zero : α) = 0 := rfl

  @[simp] theorem cast_one [has_zero α] [has_one α] [has_add α] :
    ((1 : num) : α) = 1 := rfl

  @[simp] theorem cast_pos [has_zero α] [has_one α] [has_add α]
    (n : pos_num) : (num.pos n : α) = n := rfl

  theorem succ'_to_nat : ∀ n, (succ' n : ℕ) = n + 1
  | 0       := (_root_.zero_add _).symm
  | (pos p) := pos_num.succ_to_nat _

  theorem succ_to_nat (n) : (succ n : ℕ) = n + 1 := succ'_to_nat n

  @[simp] theorem cast_to_nat [add_monoid α] [has_one α] : ∀ n : num, ((n : ℕ) : α) = n
  | 0       := nat.cast_zero
  | (pos p) := p.cast_to_nat

  @[simp] theorem to_nat_to_int (n : num) : ((n : ℕ) : ℤ) = n :=
  by rw [← int.nat_cast_eq_coe_nat, cast_to_nat]

  @[simp] theorem cast_to_int [add_group α] [has_one α] (n : num) : ((n : ℤ) : α) = n :=
  by rw [← to_nat_to_int, int.cast_coe_nat, cast_to_nat]

  @[simp] theorem to_of_nat : Π (n : ℕ), ((n : num) : ℕ) = n
  | 0     := rfl
  | (n+1) := by rw [nat.cast_add_one, add_one, succ_to_nat, to_of_nat]

  @[simp] theorem of_nat_cast [add_monoid α] [has_one α] (n : ℕ) : ((n : num) : α) = n :=
  by rw [← cast_to_nat, to_of_nat]

  theorem of_nat_inj {m n : ℕ} : (m : num) = n ↔ m = n :=
  ⟨λ h, function.injective_of_left_inverse to_of_nat h, congr_arg _⟩

  @[simp] theorem add_to_nat : ∀ m n, ((m + n : num) : ℕ) = m + n
  | 0       0       := rfl
  | 0       (pos q) := (_root_.zero_add _).symm
  | (pos p) 0       := rfl
  | (pos p) (pos q) := pos_num.add_to_nat _ _

  @[simp] theorem mul_to_nat : ∀ m n, ((m * n : num) : ℕ) = m * n
  | 0       0       := rfl
  | 0       (pos q) := (zero_mul _).symm
  | (pos p) 0       := rfl
  | (pos p) (pos q) := pos_num.mul_to_nat _ _

  theorem cmp_to_nat : ∀ (m n), (ordering.cases_on (cmp m n) ((m:ℕ) < n) (m = n) ((m:ℕ) > n) : Prop)
  | 0       0       := rfl
  | 0       (pos b) := to_nat_pos _
  | (pos a) 0       := to_nat_pos _
  | (pos a) (pos b) :=
    by { have := pos_num.cmp_to_nat a b; revert this; dsimp [cmp];
         cases pos_num.cmp a b, exacts [id, congr_arg pos, id] }

  @[simp] theorem lt_to_nat {m n : num} : (m:ℕ) < n ↔ m < n :=
  show (m:ℕ) < n ↔ cmp m n = ordering.lt, from
  match cmp m n, cmp_to_nat m n with
  | ordering.lt, h := by simp at h; simp [h]
  | ordering.eq, h := by simp at h; simp [h, lt_irrefl]; exact dec_trivial
  | ordering.gt, h := by simp [not_lt_of_gt h]; exact dec_trivial
  end

  @[simp] theorem le_to_nat {m n : num} : (m:ℕ) ≤ n ↔ m ≤ n :=
  by rw ← not_lt; exact not_congr lt_to_nat

end num

namespace pos_num
  @[simp] theorem of_to_nat : Π (n : pos_num), ((n : ℕ) : num) = num.pos n
  | 1        := rfl
  | (bit0 p) :=
    show ↑(p + p : ℕ) = num.pos p.bit0,
    by rw [num.add_of_nat, of_to_nat];
      exact congr_arg num.pos p.bit0_of_bit0
  | (bit1 p) :=
    show ((p + p : ℕ) : num) + 1 = num.pos p.bit1,
    by rw [num.add_of_nat, of_to_nat];
      exact congr_arg num.pos p.bit1_of_bit1
end pos_num

namespace num

  @[simp] theorem of_to_nat : Π (n : num), ((n : ℕ) : num) = n
  | 0           := rfl
  | (pos p) := p.of_to_nat

  theorem to_nat_inj {m n : num} : (m : ℕ) = n ↔ m = n :=
  ⟨λ h, function.injective_of_left_inverse of_to_nat h, congr_arg _⟩

  meta def transfer_rw : tactic unit :=
  `[repeat {rw ← to_nat_inj <|> rw ← lt_to_nat <|> rw ← le_to_nat},
    repeat {rw add_to_nat <|> rw mul_to_nat <|> rw cast_one <|> rw cast_zero}]

  meta def transfer : tactic unit := `[intros, transfer_rw, try {simp}]

  instance : comm_semiring num :=
  by refine {
    add      := (+),
    zero     := 0,
    zero_add := zero_add,
    add_zero := add_zero,
    mul      := (*),
    one      := 1, .. }; try {transfer}; simp [mul_add, mul_left_comm, mul_comm]

  instance : ordered_cancel_comm_monoid num :=
  { add_left_cancel            := by {intros a b c, transfer_rw, apply add_left_cancel},
    add_right_cancel           := by {intros a b c, transfer_rw, apply add_right_cancel},
    lt                         := (<),
    lt_iff_le_not_le           := by {intros a b, transfer_rw, apply lt_iff_le_not_le},
    le                         := (≤),
    le_refl                    := by transfer,
    le_trans                   := by {intros a b c, transfer_rw, apply le_trans},
    le_antisymm                := by {intros a b, transfer_rw, apply le_antisymm},
    add_le_add_left            := by {intros a b h c, revert h, transfer_rw, exact λ h, add_le_add_left h c},
    le_of_add_le_add_left      := by {intros a b c, transfer_rw, apply le_of_add_le_add_left},
    ..num.comm_semiring }

  instance : decidable_linear_ordered_semiring num :=
  { le_total                   := by {intros a b, transfer_rw, apply le_total},
    zero_lt_one                := dec_trivial,
    mul_le_mul_of_nonneg_left  := by {intros a b c, transfer_rw, apply mul_le_mul_of_nonneg_left},
    mul_le_mul_of_nonneg_right := by {intros a b c, transfer_rw, apply mul_le_mul_of_nonneg_right},
    mul_lt_mul_of_pos_left     := by {intros a b c, transfer_rw, apply mul_lt_mul_of_pos_left},
    mul_lt_mul_of_pos_right    := by {intros a b c, transfer_rw, apply mul_lt_mul_of_pos_right},
    decidable_lt               := num.decidable_lt,
    decidable_le               := num.decidable_le,
    decidable_eq               := num.decidable_eq,
    ..num.comm_semiring, ..num.ordered_cancel_comm_monoid }

  @[simp] theorem dvd_to_nat (m n : num) : (m : ℕ) ∣ n ↔ m ∣ n :=
  ⟨λ ⟨k, e⟩, ⟨k, by rw [← of_to_nat n, e]; simp⟩,
   λ ⟨k, e⟩, ⟨k, by simp [e]⟩⟩

end num

namespace pos_num
  variables {α : Type*}
  open num

  theorem to_nat_inj {m n : pos_num} : (m : ℕ) = n ↔ m = n :=
  ⟨λ h, num.pos.inj $ by rw [← pos_num.of_to_nat, ← pos_num.of_to_nat, h],
   congr_arg _⟩

  theorem pred'_to_nat : ∀ n, (pred' n : ℕ) = nat.pred n
  | 1        := rfl
  | (bit0 n) :=
    have nat.succ ↑(pred' n) = ↑n,
    by rw [pred'_to_nat n, nat.succ_pred_eq_of_pos (to_nat_pos n)],
    match pred' n, this : ∀ k : num, nat.succ ↑k = ↑n →
      ↑(num.cases_on k 1 bit1 : pos_num) = nat.pred (_root_.bit0 n) with
    | 0, (h : ((1:num):ℕ) = n) := by rw ← to_nat_inj.1 h; refl
    | num.pos p, (h : nat.succ ↑p = n) :=
      by rw ← h; exact (nat.succ_add p p).symm
    end
  | (bit1 n) := rfl

  @[simp] theorem pred'_succ' (n) : pred' (succ' n) = n :=
  num.to_nat_inj.1 $ by rw [pred'_to_nat, succ'_to_nat,
    nat.add_one, nat.pred_succ]

  @[simp] theorem succ'_pred' (n) : succ' (pred' n) = n :=
  to_nat_inj.1 $ by rw [succ'_to_nat, pred'_to_nat,
    nat.add_one, nat.succ_pred_eq_of_pos (to_nat_pos _)]

  theorem size_to_nat : ∀ n, (size n : ℕ) = nat.size n
  | 1        := nat.size_one.symm
  | (bit0 n) := by rw [size, succ_to_nat, size_to_nat, cast_bit0,
                       nat.size_bit0 $ ne_of_gt $ to_nat_pos n]
  | (bit1 n) := by rw [size, succ_to_nat, size_to_nat, cast_bit1,
                       nat.size_bit1]

  theorem size_eq_nat_size : ∀ n, (size n : ℕ) = nat_size n
  | 1        := rfl
  | (bit0 n) := by rw [size, succ_to_nat, nat_size, size_eq_nat_size]
  | (bit1 n) := by rw [size, succ_to_nat, nat_size, size_eq_nat_size]

  theorem nat_size_to_nat (n) : nat_size n = nat.size n :=
  by rw [← size_eq_nat_size, size_to_nat]

  theorem nat_size_pos (n) : 0 < nat_size n :=
  by cases n; apply nat.succ_pos

  meta def transfer_rw : tactic unit :=
  `[repeat {rw ← to_nat_inj <|> rw ← lt_to_nat <|> rw ← le_to_nat},
    repeat {rw add_to_nat <|> rw mul_to_nat <|> rw cast_one <|> rw cast_zero}]

  meta def transfer : tactic unit := `[intros, transfer_rw, try {simp [mul_comm, mul_left_comm]}]

  instance : add_comm_semigroup pos_num :=
  by refine {add := (+), ..}; transfer

  instance : comm_monoid pos_num :=
  by refine {mul := (*), one := 1, ..}; transfer


  instance : distrib pos_num :=
  by refine {add := (+), mul := (*), ..}; {transfer, simp [mul_add, mul_comm]}

  instance : decidable_linear_order pos_num :=
  { lt              := (<),
    lt_iff_le_not_le := by {intros a b, transfer_rw, apply lt_iff_le_not_le},
    le              := (≤),
    le_refl         := by transfer,
    le_trans        := by {intros a b c, transfer_rw, apply le_trans},
    le_antisymm     := by {intros a b, transfer_rw, apply le_antisymm},
    le_total        := by {intros a b, transfer_rw, apply le_total},
    decidable_lt    := by apply_instance,
    decidable_le    := by apply_instance,
    decidable_eq    := by apply_instance }

  @[simp] theorem cast_to_num (n : pos_num) : ↑n = num.pos n :=
  by rw [← cast_to_nat, ← of_to_nat n]

  theorem bit_to_nat (b n) : (bit b n : ℕ) = nat.bit b n :=
  by cases b; refl

  @[simp] theorem cast_add [add_monoid α] [has_one α] (m n) : ((m + n : pos_num) : α) = m + n :=
  by rw [← cast_to_nat, add_to_nat, nat.cast_add, cast_to_nat, cast_to_nat]

  @[simp] theorem cast_succ [add_monoid α] [has_one α] (n : pos_num) : (succ n : α) = n + 1 :=
  by rw [← add_one, cast_add, cast_one]

  @[simp] theorem cast_inj [add_monoid α] [has_one α] [char_zero α] {m n : pos_num} : (m:α) = n ↔ m = n :=
  by rw [← cast_to_nat m, ← cast_to_nat n, nat.cast_inj, to_nat_inj]

  theorem one_le_cast [linear_ordered_semiring α] (n : pos_num) : (1 : α) ≤ n :=
  by rw [← cast_to_nat, ← nat.cast_one, nat.cast_le]; apply to_nat_pos

  theorem cast_pos [linear_ordered_semiring α] (n : pos_num) : 0 < (n : α) :=
  lt_of_lt_of_le zero_lt_one (one_le_cast n)

  @[simp] theorem cast_mul [semiring α] (m n) : ((m * n : pos_num) : α) = m * n :=
  by rw [← cast_to_nat, mul_to_nat, nat.cast_mul, cast_to_nat, cast_to_nat]

  theorem cmp_eq (m n) : cmp m n = ordering.eq ↔ m = n :=
  begin
    have := cmp_to_nat m n,
    cases cmp m n; simp at this ⊢; try {exact this};
    { simp [show m ≠ n, from λ e, by rw e at this; exact lt_irrefl _ this] }
  end

  @[simp] theorem cast_lt [linear_ordered_semiring α] {m n : pos_num} : (m:α) < n ↔ m < n :=
  by rw [← cast_to_nat m, ← cast_to_nat n, nat.cast_lt, lt_to_nat]

  @[simp] theorem cast_le [linear_ordered_semiring α] {m n : pos_num} : (m:α) ≤ n ↔ m ≤ n :=
  by rw ← not_lt; exact not_congr cast_lt

end pos_num

namespace num
  variables {α : Type*}
  open pos_num

  theorem bit_to_nat (b n) : (bit b n : ℕ) = nat.bit b n :=
  by cases b; cases n; refl

  theorem cast_succ' [add_monoid α] [has_one α] (n) : (succ' n : α) = n + 1 :=
  by rw [← pos_num.cast_to_nat, succ'_to_nat, nat.cast_add_one, cast_to_nat]

  theorem cast_succ [add_monoid α] [has_one α] (n) : (succ n : α) = n + 1 := cast_succ' n

  @[simp] theorem cast_add [semiring α] (m n) : ((m + n : num) : α) = m + n :=
  by rw [← cast_to_nat, add_to_nat, nat.cast_add, cast_to_nat, cast_to_nat]

  @[simp] theorem cast_bit0 [semiring α] (n : num) : (n.bit0 : α) = _root_.bit0 n :=
  by rw [← bit0_of_bit0, _root_.bit0, cast_add]; refl

  @[simp] theorem cast_bit1 [semiring α] (n : num) : (n.bit1 : α) = _root_.bit1 n :=
  by rw [← bit1_of_bit1, _root_.bit1, bit0_of_bit0, cast_add, cast_bit0]; refl

  @[simp] theorem cast_mul [semiring α] : ∀ m n, ((m * n : num) : α) = m * n
  | 0       0       := (zero_mul _).symm
  | 0       (pos q) := (zero_mul _).symm
  | (pos p) 0       := (mul_zero _).symm
  | (pos p) (pos q) := pos_num.cast_mul _ _

  theorem size_to_nat : ∀ n, (size n : ℕ) = nat.size n
  | 0       := nat.size_zero.symm
  | (pos p) := p.size_to_nat

  theorem size_eq_nat_size : ∀ n, (size n : ℕ) = nat_size n
  | 0       := rfl
  | (pos p) := p.size_eq_nat_size

  theorem nat_size_to_nat (n) : nat_size n = nat.size n :=
  by rw [← size_eq_nat_size, size_to_nat]

  @[simp] theorem of_nat'_eq : ∀ n, num.of_nat' n = n :=
  nat.binary_rec rfl $ λ b n IH, begin
    rw of_nat' at IH ⊢,
    rw [nat.binary_rec_eq, IH],
    { cases b; simp [nat.bit, bit0_of_bit0, bit1_of_bit1] },
    { refl }
  end

  theorem zneg_to_znum (n : num) : -n.to_znum = n.to_znum_neg := by cases n; refl
  theorem zneg_to_znum_neg (n : num) : -n.to_znum_neg = n.to_znum := by cases n; refl

  theorem to_znum_inj {m n : num} : m.to_znum = n.to_znum ↔ m = n :=
  ⟨λ h, by cases m; cases n; cases h; refl, congr_arg _⟩

  @[simp] theorem cast_to_znum [has_zero α] [has_one α] [has_add α] [has_neg α] :
    ∀ n : num, (n.to_znum : α) = n
  | 0           := rfl
  | (num.pos p) := rfl

  @[simp] theorem cast_to_znum_neg [add_group α] [has_one α] :
    ∀ n : num, (n.to_znum_neg : α) = -n
  | 0           := neg_zero.symm
  | (num.pos p) := rfl

  @[simp] theorem add_to_znum (m n : num) : num.to_znum (m + n) = m.to_znum + n.to_znum :=
  by cases m; cases n; refl

end num

namespace pos_num
  open num

  theorem pred_to_nat {n : pos_num} (h : n > 1) : (pred n : ℕ) = nat.pred n :=
  begin
    unfold pred,
    have := pred'_to_nat n,
    cases e : pred' n,
    { have : (1:ℕ) ≤ nat.pred n :=
        nat.pred_le_pred ((@cast_lt ℕ _ _ _).2 h),
      rw [← pred'_to_nat, e] at this,
      exact absurd this dec_trivial },
    { rw [← pred'_to_nat, e], refl }
  end

  theorem sub'_one (a : pos_num) : sub' a 1 = (pred' a).to_znum :=
  by cases a; refl

  theorem one_sub' (a : pos_num) : sub' 1 a = (pred' a).to_znum_neg :=
  by cases a; refl

  theorem lt_iff_cmp {m n} : m < n ↔ cmp m n = ordering.lt := iff.rfl

  theorem le_iff_cmp {m n} : m ≤ n ↔ cmp m n ≠ ordering.gt :=
  not_congr $ lt_iff_cmp.trans $
  by rw ← cmp_swap; cases cmp m n; exact dec_trivial

end pos_num

namespace num
  variables {α : Type*}
  open pos_num

  theorem pred_to_nat : ∀ (n : num), (pred n : ℕ) = nat.pred n
  | 0       := rfl
  | (pos p) := by rw [pred, pos_num.pred'_to_nat]; refl

  theorem ppred_to_nat : ∀ (n : num), coe <$> ppred n = nat.ppred n
  | 0       := rfl
  | (pos p) := by rw [ppred, option.map_some, nat.ppred_eq_some.2];
    rw [pos_num.pred'_to_nat, nat.succ_pred_eq_of_pos (pos_num.to_nat_pos _)]; refl

  theorem cmp_swap (m n) : (cmp m n).swap = cmp n m :=
  by cases m; cases n; try {unfold cmp}; try {refl}; apply pos_num.cmp_swap

  theorem cmp_eq (m n) : cmp m n = ordering.eq ↔ m = n :=
  begin
    have := cmp_to_nat m n,
    cases cmp m n; simp at this ⊢; try {exact this};
    { simp [show m ≠ n, from λ e, by rw e at this; exact lt_irrefl _ this] }
  end

  @[simp] theorem cast_lt [linear_ordered_semiring α] {m n : num} : (m:α) < n ↔ m < n :=
  by rw [← cast_to_nat m, ← cast_to_nat n, nat.cast_lt, lt_to_nat]

  @[simp] theorem cast_le [linear_ordered_semiring α] {m n : num} : (m:α) ≤ n ↔ m ≤ n :=
  by rw ← not_lt; exact not_congr cast_lt

  @[simp] theorem cast_inj [linear_ordered_semiring α] {m n : num} : (m:α) = n ↔ m = n :=
  by rw [← cast_to_nat m, ← cast_to_nat n, nat.cast_inj, to_nat_inj]

  theorem lt_iff_cmp {m n} : m < n ↔ cmp m n = ordering.lt := iff.rfl

  theorem le_iff_cmp {m n} : m ≤ n ↔ cmp m n ≠ ordering.gt :=
  not_congr $ lt_iff_cmp.trans $
  by rw ← cmp_swap; cases cmp m n; exact dec_trivial

  theorem bitwise_to_nat {f : num → num → num} {g : bool → bool → bool}
    (p : pos_num → pos_num → num)
    (gff : g ff ff = ff)
    (f00 : f 0 0 = 0)
    (f0n : ∀ n, f 0 (pos n) = cond (g ff tt) (pos n) 0)
    (fn0 : ∀ n, f (pos n) 0 = cond (g tt ff) (pos n) 0)
    (fnn : ∀ m n, f (pos m) (pos n) = p m n)
    (p11 : p 1 1 = cond (g tt tt) 1 0)
    (p1b : ∀ b n, p 1 (pos_num.bit b n) = bit (g tt b) (cond (g ff tt) (pos n) 0))
    (pb1 : ∀ a m, p (pos_num.bit a m) 1 = bit (g a tt) (cond (g tt ff) (pos m) 0))
    (pbb : ∀ a b m n, p (pos_num.bit a m) (pos_num.bit b n) = bit (g a b) (p m n))
    : ∀ m n : num, (f m n : ℕ) = nat.bitwise g m n :=
  begin
    intros, cases m with m; cases n with n;
    try { change zero with 0 };
    try { change ((0:num):ℕ) with 0 },
    { rw [f00, nat.bitwise_zero]; refl },
    { unfold nat.bitwise, rw [f0n, nat.binary_rec_zero],
      cases g ff tt; refl },
    { unfold nat.bitwise,
      generalize h : (pos m : ℕ) = m', revert h,
      apply nat.bit_cases_on m' _, intros b m' h,
      rw [fn0, nat.binary_rec_eq, nat.binary_rec_zero, ←h],
      cases g tt ff; refl,
      apply nat.bitwise_bit_aux gff },
    { rw fnn,
      have : ∀b (n : pos_num), (cond b ↑n 0 : ℕ) = ↑(cond b (pos n) 0 : num) :=
        by intros; cases b; refl,
      induction m with m IH m IH generalizing n; cases n with n n,
      any_goals { change one with 1 },
      any_goals { change pos 1 with 1 },
      any_goals { change pos_num.bit0 with pos_num.bit ff },
      any_goals { change pos_num.bit1 with pos_num.bit tt },
      any_goals { change ((1:num):ℕ) with nat.bit tt 0 },
      all_goals {
        repeat {
          rw show ∀ b n, (pos (pos_num.bit b n) : ℕ) = nat.bit b ↑n,
             by intros; cases b; refl },
        rw nat.bitwise_bit },
      any_goals { assumption },
      any_goals { rw [nat.bitwise_zero, p11], cases g tt tt; refl },
      any_goals { rw [nat.bitwise_zero_left, this, ← bit_to_nat, p1b] },
      any_goals { rw [nat.bitwise_zero_right _ gff, this, ← bit_to_nat, pb1] },
      all_goals { rw [← show ∀ n, ↑(p m n) = nat.bitwise g ↑m ↑n, from IH],
        rw [← bit_to_nat, pbb] } }
  end

  @[simp] theorem lor_to_nat   : ∀ m n, (lor    m n : ℕ) = nat.lor    m n :=
  by apply bitwise_to_nat (λx y, pos (pos_num.lor x y)); intros; try {cases a}; try {cases b}; refl
  @[simp] theorem land_to_nat  : ∀ m n, (land   m n : ℕ) = nat.land   m n :=
  by apply bitwise_to_nat pos_num.land; intros; try {cases a}; try {cases b}; refl
  @[simp] theorem ldiff_to_nat : ∀ m n, (ldiff  m n : ℕ) = nat.ldiff  m n :=
  by apply bitwise_to_nat pos_num.ldiff; intros; try {cases a}; try {cases b}; refl
  @[simp] theorem lxor_to_nat  : ∀ m n, (lxor   m n : ℕ) = nat.lxor   m n :=
  by apply bitwise_to_nat pos_num.lxor; intros; try {cases a}; try {cases b}; refl

  @[simp] theorem shiftl_to_nat (m n) : (shiftl m n : ℕ) = nat.shiftl m n :=
  begin
    cases m; dunfold shiftl, {symmetry, apply nat.zero_shiftl},
    simp, induction n with n IH, {refl},
    simp [pos_num.shiftl, nat.shiftl_succ], rw ←IH
  end

  @[simp] theorem shiftr_to_nat (m n) : (shiftr m n : ℕ) = nat.shiftr m n :=
  begin
    cases m with m; dunfold shiftr, {symmetry, apply nat.zero_shiftr},
    induction n with n IH generalizing m, {cases m; refl},
    cases m with m m; dunfold pos_num.shiftr,
    { rw [nat.shiftr_eq_div_pow], symmetry, apply nat.div_eq_of_lt,
      exact @nat.pow_lt_pow_of_lt_right 2 dec_trivial 0 (n+1) (nat.succ_pos _) },
    { transitivity, apply IH,
      change nat.shiftr m n = nat.shiftr (bit1 m) (n+1),
      rw [add_comm n 1, nat.shiftr_add],
      apply congr_arg (λx, nat.shiftr x n), unfold nat.shiftr,
      change (bit1 ↑m : ℕ) with nat.bit tt m,
      rw nat.div2_bit },
    { transitivity, apply IH,
      change nat.shiftr m n = nat.shiftr (bit0 m) (n + 1),
      rw [add_comm n 1, nat.shiftr_add],
      apply congr_arg (λx, nat.shiftr x n), unfold nat.shiftr,
      change (bit0 ↑m : ℕ) with nat.bit ff m,
      rw nat.div2_bit }
  end

  @[simp] theorem test_bit_to_nat (m n) : test_bit m n = nat.test_bit m n :=
  begin
    cases m with m; unfold test_bit nat.test_bit,
    { change (zero : nat) with 0, rw nat.zero_shiftr, refl },
    induction n with n IH generalizing m;
    cases m; dunfold pos_num.test_bit, {refl},
    { exact (nat.bodd_bit _ _).symm },
    { exact (nat.bodd_bit _ _).symm },
    { change ff = nat.bodd (nat.shiftr 1 (n + 1)),
      rw [add_comm, nat.shiftr_add], change nat.shiftr 1 1 with 0,
      rw nat.zero_shiftr; refl },
    { change pos_num.test_bit m n = nat.bodd (nat.shiftr (nat.bit tt m) (n + 1)),
      rw [add_comm, nat.shiftr_add], unfold nat.shiftr,
      rw nat.div2_bit, apply IH },
    { change pos_num.test_bit m n = nat.bodd (nat.shiftr (nat.bit ff m) (n + 1)),
      rw [add_comm, nat.shiftr_add], unfold nat.shiftr,
      rw nat.div2_bit, apply IH },
  end

end num

namespace znum
  variables {α : Type*}
  open pos_num

  @[simp] theorem cast_zero [has_zero α] [has_one α] [has_add α] [has_neg α] :
    ((0 : znum) : α) = 0 := rfl

  @[simp] theorem cast_zero' [has_zero α] [has_one α] [has_add α] [has_neg α] :
    (znum.zero : α) = 0 := rfl

  @[simp] theorem cast_one [has_zero α] [has_one α] [has_add α] [has_neg α] :
    ((1 : znum) : α) = 1 := rfl

  @[simp] theorem cast_pos [has_zero α] [has_one α] [has_add α] [has_neg α]
    (n : pos_num) : (pos n : α) = n := rfl

  @[simp] theorem cast_neg [has_zero α] [has_one α] [has_add α] [has_neg α]
    (n : pos_num) : (neg n : α) = -n := rfl

  @[simp] theorem cast_zneg [add_group α] [has_one α] : ∀ n, ((-n : znum) : α) = -n
  | 0       := neg_zero.symm
  | (pos p) := rfl
  | (neg p) := (neg_neg _).symm

  theorem neg_zero : (-0 : znum) = 0 := rfl
  theorem zneg_pos (n : pos_num) : -pos n = neg n := rfl
  theorem zneg_neg (n : pos_num) : -neg n = pos n := rfl
  theorem zneg_zneg (n : znum) : - -n = n := by cases n; refl
  theorem zneg_bit1 (n : znum) : -n.bit1 = (-n).bitm1 := by cases n; refl
  theorem zneg_bitm1 (n : znum) : -n.bitm1 = (-n).bit1 := by cases n; refl

  theorem zneg_succ (n : znum) : -n.succ = (-n).pred :=
  by cases n; try {refl}; rw [succ, num.zneg_to_znum_neg]; refl

  theorem zneg_pred (n : znum) : -n.pred = (-n).succ :=
  by rw [← zneg_zneg (succ (-n)), zneg_succ, zneg_zneg]

  @[simp] theorem neg_of_int : ∀ n, ((-n : ℤ) : znum) = -n
  | (n+1:ℕ) := rfl
  | 0       := rfl
  | -[1+n]  := (zneg_zneg _).symm

  @[simp] theorem abs_to_nat : ∀ n, (abs n : ℕ) = int.nat_abs n
  | 0       := rfl
  | (pos p) := congr_arg int.nat_abs p.to_nat_to_int
  | (neg p) := show int.nat_abs ((p:ℕ):ℤ) = int.nat_abs (- p),
    by rw [p.to_nat_to_int, int.nat_abs_neg]

  @[simp] theorem abs_to_znum : ∀ n : num, abs n.to_znum = n
  | 0           := rfl
  | (num.pos p) := rfl

  @[simp] theorem cast_to_int [add_group α] [has_one α] : ∀ n : znum, ((n : ℤ) : α) = n
  | 0       := rfl
  | (pos p) := by rw [cast_pos, cast_pos, pos_num.cast_to_int]
  | (neg p) := by rw [cast_neg, cast_neg, int.cast_neg, pos_num.cast_to_int]

  theorem bit0_of_bit0 : ∀ n : znum, _root_.bit0 n = n.bit0
  | 0       := rfl
  | (pos a) := congr_arg pos a.bit0_of_bit0
  | (neg a) := congr_arg neg a.bit0_of_bit0

  theorem bit1_of_bit1 : ∀ n : znum, _root_.bit1 n = n.bit1
  | 0       := rfl
  | (pos a) := congr_arg pos a.bit1_of_bit1
  | (neg a) := show pos_num.sub' 1 (_root_.bit0 a) = _,
    by rw [pos_num.one_sub', a.bit0_of_bit0]; refl

  @[simp] theorem cast_bit0 [add_group α] [has_one α] :
    ∀ n : znum, (n.bit0 : α) = bit0 n
  | 0       := (add_zero _).symm
  | (pos p) := by rw [znum.bit0, cast_pos, cast_pos]; refl
  | (neg p) := by rw [znum.bit0, cast_neg, cast_neg, pos_num.cast_bit0,
                      _root_.bit0, _root_.bit0, neg_add_rev]

  @[simp] theorem cast_bit1 [add_group α] [has_one α] :
    ∀ n : znum, (n.bit1 : α) = bit1 n
  | 0       := by simp [znum.bit1, _root_.bit1, _root_.bit0]
  | (pos p) := by rw [znum.bit1, cast_pos, cast_pos]; refl
  | (neg p) := begin
      rw [znum.bit1, cast_neg, cast_neg],
      cases e : pred' p;
      have : p = _ := (succ'_pred' p).symm.trans
        (congr_arg num.succ' e),
      { change p=1 at this, subst p,
        simp [_root_.bit1, _root_.bit0] },
      { rw [num.succ'] at this, subst p,
        have : (↑(-↑a:ℤ) : α) = -1 + ↑(-↑a + 1 : ℤ), {simp},
        simpa [_root_.bit1, _root_.bit0, -add_comm] },
    end

  @[simp] theorem cast_bitm1 [add_group α] [has_one α]
    (n : znum) : (n.bitm1 : α) = bit0 n - 1 :=
  begin
    conv { to_lhs, rw ← zneg_zneg n },
    rw [← zneg_bit1, cast_zneg, cast_bit1],
    have : ((-1 + n + n : ℤ) : α) = (n + n + -1 : ℤ), {simp},
    simpa [_root_.bit1, _root_.bit0, -add_comm]
  end

  theorem add_zero (n : znum) : n + 0 = n := by cases n; refl
  theorem zero_add (n : znum) : 0 + n = n := by cases n; refl

  theorem add_one : ∀ n : znum, n + 1 = succ n
  | 0       := rfl
  | (pos p) := congr_arg pos p.add_one
  | (neg p) := by cases p; refl

end znum

namespace pos_num
  variables {α : Type*}

  theorem cast_to_znum : ∀ n : pos_num, (n : znum) = znum.pos n
  | 1        := rfl
  | (bit0 p) := (znum.bit0_of_bit0 p).trans $ congr_arg _ (cast_to_znum p)
  | (bit1 p) := (znum.bit1_of_bit1 p).trans $ congr_arg _ (cast_to_znum p)

  theorem cast_sub' [add_group α] [has_one α] : ∀ m n : pos_num, (sub' m n : α) = m - n
  | a        1        := by rw [sub'_one, num.cast_to_znum,
                                ← num.cast_to_nat, pred'_to_nat, ← nat.sub_one];
                            simp [pos_num.cast_pos]
  | 1        b        := by rw [one_sub', num.cast_to_znum_neg, ← neg_sub, neg_inj',
                                ← num.cast_to_nat, pred'_to_nat, ← nat.sub_one];
                            simp [pos_num.cast_pos]
  | (bit0 a) (bit0 b) := begin
      rw [sub', znum.cast_bit0, cast_sub'],
      have : ((a + -b + (a + -b) : ℤ) : α) = a + a + (-b + -b), {simp},
      simpa [_root_.bit0, -add_left_comm]
    end
  | (bit0 a) (bit1 b) := begin
      rw [sub', znum.cast_bitm1, cast_sub'],
      have : ((-b + (a + (-b + -1)) : ℤ) : α) = (a + -1 + (-b + -b):ℤ), {simp},
      simpa [_root_.bit1, _root_.bit0, -add_left_comm, -add_comm]
    end
  | (bit1 a) (bit0 b) := begin
      rw [sub', znum.cast_bit1, cast_sub'],
      have : ((-b + (a + (-b + 1)) : ℤ) : α) = (a + 1 + (-b + -b):ℤ), {simp},
      simpa [_root_.bit1, _root_.bit0, -add_left_comm, -add_comm]
    end
  | (bit1 a) (bit1 b) := begin
      rw [sub', znum.cast_bit0, cast_sub'],
      have : ((-b + (a + -b) : ℤ) : α) = a + (-b + -b), {simp},
      simpa [_root_.bit1, _root_.bit0, -add_left_comm, add_neg_cancel_left]
    end

  theorem to_nat_eq_succ_pred (n : pos_num) : (n:ℕ) = n.pred' + 1 :=
  by rw [← num.succ'_to_nat, n.succ'_pred']

  theorem to_int_eq_succ_pred (n : pos_num) : (n:ℤ) = (n.pred' : ℕ) + 1 :=
  by rw [← n.to_nat_to_int, to_nat_eq_succ_pred]; refl

end pos_num

namespace num
  variables {α : Type*}

  @[simp] theorem cast_sub' [add_group α] [has_one α] : ∀ m n : num, (sub' m n : α) = m - n
  | 0       0       := (sub_zero _).symm
  | (pos a) 0       := (sub_zero _).symm
  | 0       (pos b) := (zero_sub _).symm
  | (pos a) (pos b) := pos_num.cast_sub' _ _

  @[simp] theorem of_nat_to_znum : ∀ n : ℕ, to_znum n = n
  | 0     := rfl
  | (n+1) := by rw [nat.cast_add_one, nat.cast_add_one,
    znum.add_one, add_one, ← of_nat_to_znum]; cases (n:num); refl

  @[simp] theorem of_nat_to_znum_neg (n : ℕ) : to_znum_neg n = -n :=
  by rw [← of_nat_to_znum, zneg_to_znum]

  theorem mem_of_znum' : ∀ {m : num} {n : znum}, m ∈ of_znum' n ↔ n = to_znum m
  | 0       0      := ⟨λ _, rfl, λ _, rfl⟩
  | (pos m) 0      := ⟨λ h, by cases h, λ h, by cases h⟩
  | m (znum.pos p) := option.some_inj.trans $
    by cases m; split; intro h; try {cases h}; refl
  | m (znum.neg p) := ⟨λ h, by cases h, λ h, by cases m; cases h⟩

  theorem of_znum'_to_nat : ∀ (n : znum), coe <$> of_znum' n = int.to_nat' n
  | 0            := rfl
  | (znum.pos p) := show _ = int.to_nat' p, by rw [← pos_num.to_nat_to_int p]; refl
  | (znum.neg p) := congr_arg (λ x, int.to_nat' (-x)) $
    show ((p.pred' + 1 : ℕ) : ℤ) = p, by rw ← succ'_to_nat; simp

  @[simp] theorem of_znum_to_nat : ∀ (n : znum), (of_znum n : ℕ) = int.to_nat n
  | 0            := rfl
  | (znum.pos p) := show _ = int.to_nat p, by rw [← pos_num.to_nat_to_int p]; refl
  | (znum.neg p) := congr_arg (λ x, int.to_nat (-x)) $
    show ((p.pred' + 1 : ℕ) : ℤ) = p, by rw ← succ'_to_nat; simp

  @[simp] theorem cast_of_znum [add_group α] [has_one α] (n : znum) :
    (of_znum n : α) = int.to_nat n :=
  by rw [← cast_to_nat, of_znum_to_nat]

  @[simp] theorem sub_to_nat (m n) : ((m - n : num) : ℕ) = m - n :=
  show (of_znum _ : ℕ) = _, by rw [of_znum_to_nat, cast_sub',
    ← to_nat_to_int, ← to_nat_to_int, int.to_nat_sub]

end num

namespace znum
  variables {α : Type*}

  @[simp] theorem cast_add [add_group α] [has_one α] : ∀ m n, ((m + n : znum) : α) = m + n
  | 0       a       := by cases a; exact (_root_.zero_add _).symm
  | b       0       := by cases b; exact (_root_.add_zero _).symm
  | (pos a) (pos b) := pos_num.cast_add _ _
  | (pos a) (neg b) := pos_num.cast_sub' _ _
  | (neg a) (pos b) := (pos_num.cast_sub' _ _).trans $
    show ↑b + -↑a = -↑a + ↑b, by rw [← pos_num.cast_to_int a, ← pos_num.cast_to_int b,
      ← int.cast_neg, ← int.cast_add (-a)]; simp
  | (neg a) (neg b) := show -(↑(a + b) : α) = -a + -b, by rw [
    pos_num.cast_add, neg_eq_iff_neg_eq, neg_add_rev, neg_neg, neg_neg,
    ← pos_num.cast_to_int a, ← pos_num.cast_to_int b, ← int.cast_add]; simp

  @[simp] theorem cast_succ [add_group α] [has_one α] (n) : ((succ n : znum) : α) = n + 1 :=
  by rw [← add_one, cast_add, cast_one]

  @[simp] theorem mul_to_int : ∀ m n, ((m * n : znum) : ℤ) = m * n
  | 0       a       := by cases a; exact (_root_.zero_mul _).symm
  | b       0       := by cases b; exact (_root_.mul_zero _).symm
  | (pos a) (pos b) := pos_num.cast_mul a b
  | (pos a) (neg b) := show -↑(a * b) = ↑a * -↑b, by rw [pos_num.cast_mul, neg_mul_eq_mul_neg]
  | (neg a) (pos b) := show -↑(a * b) = -↑a * ↑b, by rw [pos_num.cast_mul, neg_mul_eq_neg_mul]
  | (neg a) (neg b) := show ↑(a * b) = -↑a * -↑b, by rw [pos_num.cast_mul, neg_mul_neg]

  theorem cast_mul [ring α] (m n) : ((m * n : znum) : α) = m * n :=
  by rw [← cast_to_int, mul_to_int, int.cast_mul, cast_to_int, cast_to_int]

  @[simp] theorem of_to_int : Π (n : znum), ((n : ℤ) : znum) = n
  | 0       := rfl
  | (pos a) := by rw [cast_pos, ← pos_num.cast_to_nat,
    int.cast_coe_nat', ← num.of_nat_to_znum, pos_num.of_to_nat]; refl
  | (neg a) := by rw [cast_neg, neg_of_int, ← pos_num.cast_to_nat,
    int.cast_coe_nat', ← num.of_nat_to_znum_neg, pos_num.of_to_nat]; refl

  @[simp] theorem to_of_int : Π (n : ℤ), ((n : znum) : ℤ) = n
  | (n : ℕ) := by rw [int.cast_coe_nat,
    ← num.of_nat_to_znum, num.cast_to_znum, ← num.cast_to_nat,
    int.nat_cast_eq_coe_nat, num.to_of_nat]
  | -[1+ n] := by rw [int.cast_neg_succ_of_nat, cast_zneg,
    add_one, cast_succ, int.neg_succ_of_nat_eq,
    ← num.of_nat_to_znum, num.cast_to_znum, ← num.cast_to_nat,
    int.nat_cast_eq_coe_nat, num.to_of_nat]

  theorem to_int_inj {m n : znum} : (m : ℤ) = n ↔ m = n :=
  ⟨λ h, function.injective_of_left_inverse of_to_int h, congr_arg _⟩

  @[simp] theorem of_int_cast [add_group α] [has_one α] (n : ℤ) : ((n : znum) : α) = n :=
  by rw [← cast_to_int, to_of_int]

  @[simp] theorem of_nat_cast [add_group α] [has_one α] (n : ℕ) : ((n : znum) : α) = n :=
  of_int_cast n

  @[simp] theorem of_int'_eq : ∀ n, znum.of_int' n = n
  | (n : ℕ) := to_int_inj.1 $ by simp [znum.of_int']
  | -[1+ n] := to_int_inj.1 $ by simp [znum.of_int']

  theorem cmp_to_int : ∀ (m n), (ordering.cases_on (cmp m n) ((m:ℤ) < n) (m = n) ((m:ℤ) > n) : Prop)
  | 0       0       := rfl
  | (pos a) (pos b) := begin
      have := pos_num.cmp_to_nat a b; revert this; dsimp [cmp];
      cases pos_num.cmp a b; dsimp;
      [simp, exact congr_arg pos, simp [gt]]
    end
  | (neg a) (neg b) := begin
      have := pos_num.cmp_to_nat b a; revert this; dsimp [cmp];
      cases pos_num.cmp b a; dsimp;
      [simp, simp {contextual := tt}, simp [gt]]
    end
  | (pos a) 0       := pos_num.cast_pos _
  | (pos a) (neg b) := lt_trans (neg_lt_zero.2 $ pos_num.cast_pos _) (pos_num.cast_pos _)
  | 0       (neg b) := neg_lt_zero.2 $ pos_num.cast_pos _
  | (neg a) 0       := neg_lt_zero.2 $ pos_num.cast_pos _
  | (neg a) (pos b) := lt_trans (neg_lt_zero.2 $ pos_num.cast_pos _) (pos_num.cast_pos _)
  | 0       (pos b) := pos_num.cast_pos _

  @[simp] theorem lt_to_int {m n : znum} : (m:ℤ) < n ↔ m < n :=
  show (m:ℤ) < n ↔ cmp m n = ordering.lt, from
  match cmp m n, cmp_to_int m n with
  | ordering.lt, h := by simp at h; simp [h]
  | ordering.eq, h := by simp at h; simp [h, lt_irrefl]; exact dec_trivial
  | ordering.gt, h := by simp [not_lt_of_gt h]; exact dec_trivial
  end

  @[simp] theorem le_to_int {m n : znum} : (m:ℤ) ≤ n ↔ m ≤ n :=
  by rw ← not_lt; exact not_congr lt_to_int

  @[simp] theorem cast_lt [linear_ordered_ring α] {m n : znum} : (m:α) < n ↔ m < n :=
  by rw [← cast_to_int m, ← cast_to_int n, int.cast_lt, lt_to_int]

  @[simp] theorem cast_le [linear_ordered_ring α] {m n : znum} : (m:α) ≤ n ↔ m ≤ n :=
  by rw ← not_lt; exact not_congr cast_lt

  @[simp] theorem cast_inj [linear_ordered_ring α] {m n : znum} : (m:α) = n ↔ m = n :=
  by rw [← cast_to_int m, ← cast_to_int n, int.cast_inj, to_int_inj]

  meta def transfer_rw : tactic unit :=
  `[repeat {rw ← to_int_inj <|> rw ← lt_to_int <|> rw ← le_to_int},
    repeat {rw cast_add <|> rw mul_to_int <|> rw cast_one <|> rw cast_zero}]

  meta def transfer : tactic unit := `[intros, transfer_rw, try {simp [mul_comm, mul_left_comm]}]

  instance : decidable_linear_order znum :=
  { lt               := (<),
    lt_iff_le_not_le := by {intros a b, transfer_rw, apply lt_iff_le_not_le},
    le               := (≤),
    le_refl          := by transfer,
    le_trans         := by {intros a b c, transfer_rw, apply le_trans},
    le_antisymm      := by {intros a b, transfer_rw, apply le_antisymm},
    le_total         := by {intros a b, transfer_rw, apply le_total},
    decidable_eq     := znum.decidable_eq,
    decidable_le     := znum.decidable_le,
    decidable_lt     := znum.decidable_lt }

  instance : add_comm_group znum :=
  { add              := (+),
    add_assoc        := by transfer,
    zero             := 0,
    zero_add         := zero_add,
    add_zero         := add_zero,
    add_comm         := by transfer,
    neg              := has_neg.neg,
    add_left_neg     := by transfer }

  instance : decidable_linear_ordered_comm_ring znum :=
  { mul              := (*),
    mul_assoc        := by transfer,
    one              := 1,
    one_mul          := by transfer,
    mul_one          := by transfer,
    left_distrib     := by {transfer, simp [mul_add]},
    right_distrib    := by {transfer, simp [mul_add, mul_comm]},
    mul_comm         := by transfer,
    zero_ne_one      := dec_trivial,
    add_le_add_left  := by {intros a b h c, revert h, transfer_rw, exact λ h, add_le_add_left h c},
    add_lt_add_left  := by {intros a b h c, revert h, transfer_rw, exact λ h, add_lt_add_left h c},
    mul_pos          := by {intros a b, transfer_rw, apply mul_pos},
    mul_nonneg       := by {intros x y,
      change 0 ≤ x → 0 ≤ y → 0 ≤ x * y,
      transfer_rw, apply mul_nonneg},
    zero_lt_one      := dec_trivial,
    ..znum.decidable_linear_order, ..znum.add_comm_group }

  @[simp] theorem dvd_to_int (m n : znum) : (m : ℤ) ∣ n ↔ m ∣ n :=
  ⟨λ ⟨k, e⟩, ⟨k, by rw [← of_to_int n, e]; simp⟩,
   λ ⟨k, e⟩, ⟨k, by simp [e]⟩⟩

end znum

namespace pos_num

  theorem divmod_to_nat_aux {n d : pos_num} {q r : num}
    (h₁ : (r:ℕ) + d * _root_.bit0 q = n)
    (h₂ : (r:ℕ) < 2 * d) :
    ((divmod_aux d q r).2 + d * (divmod_aux d q r).1 : ℕ) = ↑n ∧
    ((divmod_aux d q r).2 : ℕ) < d :=
  begin
    unfold divmod_aux,
    have : ∀ {r₂}, num.of_znum' (num.sub' r (num.pos d)) = some r₂ ↔ (r : ℕ) = r₂ + d,
    { intro r₂,
      apply num.mem_of_znum'.trans,
      rw [← znum.to_int_inj, num.cast_to_znum,
        num.cast_sub', sub_eq_iff_eq_add, ← int.coe_nat_inj'],
      simp },
    cases e : num.of_znum' (num.sub' r (num.pos d)) with r₂;
      simp [divmod_aux],
    { refine ⟨h₁, lt_of_not_ge (λ h, _)⟩,
      cases nat.le.dest h with r₂ e',
      rw [← num.to_of_nat r₂, add_comm] at e',
      cases e.symm.trans (this.2 e'.symm) },
    { have := this.1 e,
      split,
      { rwa [_root_.bit1, add_comm _ 1, mul_add, mul_one,
          ← add_assoc, ← this] },
      { rwa [this, two_mul, add_lt_add_iff_right] at h₂ } }
  end

  theorem divmod_to_nat (d n : pos_num) :
    (n / d : ℕ) = (divmod d n).1 ∧
    (n % d : ℕ) = (divmod d n).2 :=
  begin
    rw nat.div_mod_unique (pos_num.cast_pos _),
    induction n with n IH n IH,
    { exact divmod_to_nat_aux (by simp; refl)
        (nat.mul_le_mul_left 2
          (pos_num.cast_pos d : (0 : ℕ) < d)) },
    { unfold divmod,
      cases divmod d n with q r, simp only [divmod] at IH ⊢,
      apply divmod_to_nat_aux; simp,
      { rw [_root_.bit1, _root_.bit1, add_right_comm,
          bit0_eq_two_mul ↑n, ← IH.1,
          mul_add, ← bit0_eq_two_mul,
          mul_left_comm, ← bit0_eq_two_mul] },
      { rw ← bit0_eq_two_mul,
        exact nat.bit1_lt_bit0 IH.2 } },
    { unfold divmod,
      cases divmod d n with q r, simp only [divmod] at IH ⊢,
      apply divmod_to_nat_aux; simp,
      { rw [bit0_eq_two_mul ↑n, ← IH.1,
          mul_add, ← bit0_eq_two_mul,
          mul_left_comm, ← bit0_eq_two_mul] },
      { rw ← bit0_eq_two_mul,
        exact nat.bit0_lt IH.2 } }
  end

  @[simp] theorem div'_to_nat (n d) : (div' n d : ℕ) = n / d :=
  (divmod_to_nat _ _).1.symm

  @[simp] theorem mod'_to_nat (n d) : (mod' n d : ℕ) = n % d :=
  (divmod_to_nat _ _).2.symm

end pos_num

namespace num

  @[simp] theorem div_to_nat : ∀ n d, ((n / d : num) : ℕ) = n / d
  | 0       0       := rfl
  | 0       (pos d) := (nat.zero_div _).symm
  | (pos n) 0       := (nat.div_zero _).symm
  | (pos n) (pos d) := pos_num.div'_to_nat _ _

  @[simp] theorem mod_to_nat : ∀ n d, ((n % d : num) : ℕ) = n % d
  | 0       0       := rfl
  | 0       (pos d) := (nat.zero_mod _).symm
  | (pos n) 0       := (nat.mod_zero _).symm
  | (pos n) (pos d) := pos_num.mod'_to_nat _ _

  theorem gcd_to_nat_aux : ∀ {n} {a b : num},
    a ≤ b → (a * b).nat_size ≤ n → (gcd_aux n a b : ℕ) = nat.gcd a b
  | 0            0       b       ab h := (nat.gcd_zero_left _).symm
  | 0            (pos a) 0       ab h := (not_lt_of_ge ab).elim rfl
  | 0            (pos a) (pos b) ab h :=
    (not_lt_of_le h).elim $ pos_num.nat_size_pos _
  | (nat.succ n) 0       b       ab h := (nat.gcd_zero_left _).symm
  | (nat.succ n) (pos a) b       ab h := begin
    simp [gcd_aux],
    rw [nat.gcd_rec, gcd_to_nat_aux, mod_to_nat], {refl},
    { rw [← le_to_nat, mod_to_nat],
      exact le_of_lt (nat.mod_lt _ (pos_num.cast_pos _)) },
    rw [nat_size_to_nat, mul_to_nat, nat.size_le] at h ⊢,
    rw [mod_to_nat, mul_comm],
    rw [nat.pow_succ, ← nat.mod_add_div b (pos a)] at h,
    refine lt_of_mul_lt_mul_right (lt_of_le_of_lt _ h) (nat.zero_le 2),
    rw [mul_two, mul_add],
    refine add_le_add_left (nat.mul_le_mul_left _
      (le_trans (le_of_lt (nat.mod_lt _ (pos_num.cast_pos _))) _)) _,
    suffices : 1 ≤ _, simpa using nat.mul_le_mul_left (pos a) this,
    rw [nat.le_div_iff_mul_le _ _ a.cast_pos, one_mul],
    exact le_to_nat.2 ab
  end

  @[simp] theorem gcd_to_nat : ∀ a b, (gcd a b : ℕ) = nat.gcd a b :=
  have ∀ a b : num, (a * b).nat_size ≤ a.nat_size + b.nat_size,
  begin
    intros,
    simp [nat_size_to_nat],
    rw [nat.size_le, nat.pow_add],
    exact mul_lt_mul'' (nat.lt_size_self _)
      (nat.lt_size_self _) (nat.zero_le _) (nat.zero_le _)
  end,
  begin
    intros, unfold gcd, split_ifs,
    { exact gcd_to_nat_aux h (this _ _) },
    { rw nat.gcd_comm,
      exact gcd_to_nat_aux (le_of_not_le h) (this _ _) }
  end

  theorem dvd_iff_mod_eq_zero {m n : num} : m ∣ n ↔ n % m = 0 :=
  by rw [← dvd_to_nat, nat.dvd_iff_mod_eq_zero,
    ← to_nat_inj, mod_to_nat]; refl

  instance : decidable_rel ((∣) : num → num → Prop)
  | a b := decidable_of_iff' _ dvd_iff_mod_eq_zero

end num

namespace znum

  @[simp] theorem div_to_int : ∀ n d, ((n / d : znum) : ℤ) = n / d
  | 0       0       := rfl
  | 0       (pos d) := (int.zero_div _).symm
  | 0       (neg d) := (int.zero_div _).symm
  | (pos n) 0       := (int.div_zero _).symm
  | (neg n) 0       := (int.div_zero _).symm
  | (pos n) (pos d) := (num.cast_to_znum _).trans $
    by rw ← num.to_nat_to_int; simp
  | (pos n) (neg d) := (num.cast_to_znum_neg _).trans $
    by rw ← num.to_nat_to_int; simp
  | (neg n) (pos d) := show - _ = (-_/↑d), begin
      rw [n.to_int_eq_succ_pred, d.to_int_eq_succ_pred,
        ← pos_num.to_nat_to_int, num.succ'_to_nat,
        num.div_to_nat],
      change -[1+ n.pred' / ↑d] = -[1+ n.pred' / (d.pred' + 1)],
      rw d.to_nat_eq_succ_pred
    end
  | (neg n) (neg d) := show ↑(pos_num.pred' n / num.pos d).succ' = (-_ / -↑d), begin
      rw [n.to_int_eq_succ_pred, d.to_int_eq_succ_pred,
        ← pos_num.to_nat_to_int, num.succ'_to_nat,
        num.div_to_nat],
      change (nat.succ (_/d) : ℤ) = nat.succ (n.pred'/(d.pred' + 1)),
      rw d.to_nat_eq_succ_pred
    end

  @[simp] theorem mod_to_int : ∀ n d, ((n % d : znum) : ℤ) = n % d
  | 0       d := (int.zero_mod _).symm
  | (pos n) d := (num.cast_to_znum _).trans $
    by rw [← num.to_nat_to_int, cast_pos, num.mod_to_nat,
      ← pos_num.to_nat_to_int, abs_to_nat]; refl
  | (neg n) d := (num.cast_sub' _ _).trans $
    by rw [← num.to_nat_to_int, cast_neg, ← num.to_nat_to_int,
      num.succ_to_nat, num.mod_to_nat, abs_to_nat,
      ← int.sub_nat_nat_eq_coe, n.to_int_eq_succ_pred]; refl

  @[simp] theorem gcd_to_nat (a b) : (gcd a b : ℕ) = int.gcd a b :=
  (num.gcd_to_nat _ _).trans $ by simpa

  theorem dvd_iff_mod_eq_zero {m n : znum} : m ∣ n ↔ n % m = 0 :=
  by rw [← dvd_to_int, int.dvd_iff_mod_eq_zero,
    ← to_int_inj, mod_to_int]; refl

  instance : decidable_rel ((∣) : znum → znum → Prop)
  | a b := decidable_of_iff' _ dvd_iff_mod_eq_zero

end znum