/-
Copyright (c) 2014 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro

! This file was ported from Lean 3 source module data.num.lemmas
! leanprover-community/mathlib commit 2196ab363eb097c008d4497125e0dde23fb36db2
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Num.Bitwise
import Mathbin.Data.Int.CharZero
import Mathbin.Data.Nat.Gcd.Basic
import Mathbin.Data.Nat.Psub
import Mathbin.Data.Nat.Size

/-!
# Properties of the binary representation of integers
-/

attribute [local simp] add_assoc

namespace PosNum

variable {α : Type _}

@[simp, norm_cast]
theorem cast_one [One α] [Add α] : ((1 : PosNum) : α) = 1 :=
  rfl
#align pos_num.cast_one PosNum.cast_one

@[simp]
theorem cast_one' [One α] [Add α] : (PosNum.one : α) = 1 :=
  rfl
#align pos_num.cast_one' PosNum.cast_one'

@[simp, norm_cast]
theorem cast_bit0 [One α] [Add α] (n : PosNum) : (n.bit0 : α) = bit0 n :=
  rfl
#align pos_num.cast_bit0 PosNum.cast_bit0

@[simp, norm_cast]
theorem cast_bit1 [One α] [Add α] (n : PosNum) : (n.bit1 : α) = bit1 n :=
  rfl
#align pos_num.cast_bit1 PosNum.cast_bit1

@[simp, norm_cast]
theorem cast_to_nat [AddMonoidWithOne α] : ∀ n : PosNum, ((n : ℕ) : α) = n
  | 1 => Nat.cast_one
  | bit0 p => (Nat.cast_bit0 _).trans <| congr_arg bit0 p.cast_to_nat
  | bit1 p => (Nat.cast_bit1 _).trans <| congr_arg bit1 p.cast_to_nat
#align pos_num.cast_to_nat PosNum.cast_to_nat

@[simp, norm_cast]
theorem to_nat_to_int (n : PosNum) : ((n : ℕ) : ℤ) = n :=
  cast_to_nat _
#align pos_num.to_nat_to_int PosNum.to_nat_to_int

@[simp, norm_cast]
theorem cast_to_int [AddGroupWithOne α] (n : PosNum) : ((n : ℤ) : α) = n := by
  rw [← to_nat_to_int, Int.cast_ofNat, cast_to_nat]
#align pos_num.cast_to_int PosNum.cast_to_int

theorem succ_to_nat : ∀ n, (succ n : ℕ) = n + 1
  | 1 => rfl
  | bit0 p => rfl
  | bit1 p =>
    (congr_arg bit0 (succ_to_nat p)).trans <|
      show ↑p + 1 + ↑p + 1 = ↑p + ↑p + 1 + 1 by simp [add_left_comm]
#align pos_num.succ_to_nat PosNum.succ_to_nat

theorem one_add (n : PosNum) : 1 + n = succ n := by cases n <;> rfl
#align pos_num.one_add PosNum.one_add

theorem add_one (n : PosNum) : n + 1 = succ n := by cases n <;> rfl
#align pos_num.add_one PosNum.add_one

@[norm_cast]
theorem add_to_nat : ∀ m n, ((m + n : PosNum) : ℕ) = m + n
  | 1, b => by rw [one_add b, succ_to_nat, add_comm] <;> rfl
  | a, 1 => by rw [add_one a, succ_to_nat] <;> rfl
  | bit0 a, bit0 b => (congr_arg bit0 (add_to_nat a b)).trans <| add_add_add_comm _ _ _ _
  | bit0 a, bit1 b =>
    (congr_arg bit1 (add_to_nat a b)).trans <|
      show (a + b + (a + b) + 1 : ℕ) = a + a + (b + b + 1) by simp [add_left_comm]
  | bit1 a, bit0 b =>
    (congr_arg bit1 (add_to_nat a b)).trans <|
      show (a + b + (a + b) + 1 : ℕ) = a + a + 1 + (b + b) by simp [add_comm, add_left_comm]
  | 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 [add_left_comm]
#align pos_num.add_to_nat PosNum.add_to_nat

theorem add_succ : ∀ m n : PosNum, 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)
#align pos_num.add_succ PosNum.add_succ

theorem bit0_of_bit0 : ∀ n, bit0 n = bit0 n
  | 1 => rfl
  | bit0 p => congr_arg bit0 (bit0_of_bit0 p)
  | bit1 p => show bit0 (succ (bit0 p)) = _ by rw [bit0_of_bit0] <;> rfl
#align pos_num.bit0_of_bit0 PosNum.bit0_of_bit0

theorem bit1_of_bit1 (n : PosNum) : bit1 n = bit1 n :=
  show bit0 n + 1 = bit1 n by rw [add_one, bit0_of_bit0] <;> rfl
#align pos_num.bit1_of_bit1 PosNum.bit1_of_bit1

@[norm_cast]
theorem mul_to_nat (m) : ∀ n, ((m * n : PosNum) : ℕ) = 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]
#align pos_num.mul_to_nat PosNum.mul_to_nat

theorem to_nat_pos : ∀ n : PosNum, 0 < (n : ℕ)
  | 1 => zero_lt_one
  | bit0 p =>
    let h := to_nat_pos p
    add_pos h h
  | bit1 p => Nat.succ_pos _
#align pos_num.to_nat_pos PosNum.to_nat_pos

theorem cmp_to_nat_lemma {m n : PosNum} : (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
#align pos_num.cmp_to_nat_lemma PosNum.cmp_to_nat_lemma

theorem cmp_swap (m) : ∀ n, (cmp m n).symm = cmp n m := by
  induction' m with m IH m IH <;> intro n <;> cases' n with n n <;> try unfold cmp <;> try rfl <;>
        rw [← IH] <;>
      cases cmp m n <;>
    rfl
#align pos_num.cmp_swap PosNum.cmp_swap

theorem cmp_to_nat : ∀ m n, (Ordering.casesOn (cmp m n) ((m : ℕ) < n) (m = n) ((n : ℕ) < m) : Prop)
  | 1, 1 => rfl
  | bit0 a, 1 =>
    let h : (1 : ℕ) ≤ a := to_nat_pos a
    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
    add_le_add h h
  | 1, bit1 b => Nat.succ_lt_succ <| to_nat_pos <| bit0 b
  | bit0 a, bit0 b => by
    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
  | bit0 a, bit1 b => by
    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
  | bit1 a, bit0 b => by
    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)
  | bit1 a, bit1 b => by
    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)
#align pos_num.cmp_to_nat PosNum.cmp_to_nat

@[norm_cast]
theorem lt_to_nat {m n : PosNum} : (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 by decide
    | Ordering.gt, h => by simp [not_lt_of_gt h] <;> exact by decide
#align pos_num.lt_to_nat PosNum.lt_to_nat

@[norm_cast]
theorem le_to_nat {m n : PosNum} : (m : ℕ) ≤ n ↔ m ≤ n := by
  rw [← not_lt] <;> exact not_congr lt_to_nat
#align pos_num.le_to_nat PosNum.le_to_nat

end PosNum

namespace Num

variable {α : Type _}

open PosNum

theorem add_zero (n : Num) : n + 0 = n := by cases n <;> rfl
#align num.add_zero Num.add_zero

theorem zero_add (n : Num) : 0 + n = n := by cases n <;> rfl
#align num.zero_add Num.zero_add

theorem add_one : ∀ n : Num, n + 1 = succ n
  | 0 => rfl
  | Pos p => by cases p <;> rfl
#align num.add_one Num.add_one

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 [PosNum.add_one, add_zero] <;> rfl
  | Pos p, Pos q => congr_arg pos (PosNum.add_succ _ _)
#align num.add_succ Num.add_succ

theorem bit0_of_bit0 : ∀ n : Num, bit0 n = n.bit0
  | 0 => rfl
  | Pos p => congr_arg pos p.bit0_of_bit0
#align num.bit0_of_bit0 Num.bit0_of_bit0

theorem bit1_of_bit1 : ∀ n : Num, bit1 n = n.bit1
  | 0 => rfl
  | Pos p => congr_arg pos p.bit1_of_bit1
#align num.bit1_of_bit1 Num.bit1_of_bit1

@[simp]
theorem ofNat'_zero : Num.ofNat' 0 = 0 := by simp [Num.ofNat']
#align num.of_nat'_zero Num.ofNat'_zero

theorem ofNat'_bit (b n) : ofNat' (Nat.bit b n) = cond b Num.bit1 Num.bit0 (ofNat' n) :=
  Nat.binaryRec_eq rfl _ _
#align num.of_nat'_bit Num.ofNat'_bit

@[simp]
theorem ofNat'_one : Num.ofNat' 1 = 1 := by erw [of_nat'_bit tt 0, cond, of_nat'_zero] <;> rfl
#align num.of_nat'_one Num.ofNat'_one

theorem bit1_succ : ∀ n : Num, n.bit1.succ = n.succ.bit0
  | 0 => rfl
  | Pos n => rfl
#align num.bit1_succ Num.bit1_succ

theorem ofNat'_succ : ∀ {n}, ofNat' (n + 1) = ofNat' n + 1 :=
  Nat.binaryRec (by simp <;> rfl) fun b n ih =>
    by
    cases b
    · erw [of_nat'_bit tt n, of_nat'_bit]
      simp only [← bit1_of_bit1, ← bit0_of_bit0, cond, _root_.bit1]
    · erw [show n.bit tt + 1 = (n + 1).bit ff by simp [Nat.bit, _root_.bit1, _root_.bit0] <;> cc,
        of_nat'_bit, of_nat'_bit, ih]
      simp only [cond, add_one, bit1_succ]
#align num.of_nat'_succ Num.ofNat'_succ

@[simp]
theorem add_ofNat' (m n) : Num.ofNat' (m + n) = Num.ofNat' m + Num.ofNat' n := by
  induction n <;> simp [Nat.add_zero, of_nat'_succ, add_zero, Nat.add_succ, add_one, add_succ, *]
#align num.add_of_nat' Num.add_ofNat'

@[simp, norm_cast]
theorem cast_zero [Zero α] [One α] [Add α] : ((0 : Num) : α) = 0 :=
  rfl
#align num.cast_zero Num.cast_zero

@[simp]
theorem cast_zero' [Zero α] [One α] [Add α] : (Num.zero : α) = 0 :=
  rfl
#align num.cast_zero' Num.cast_zero'

@[simp, norm_cast]
theorem cast_one [Zero α] [One α] [Add α] : ((1 : Num) : α) = 1 :=
  rfl
#align num.cast_one Num.cast_one

@[simp]
theorem cast_pos [Zero α] [One α] [Add α] (n : PosNum) : (Num.pos n : α) = n :=
  rfl
#align num.cast_pos Num.cast_pos

theorem succ'_to_nat : ∀ n, (succ' n : ℕ) = n + 1
  | 0 => (zero_add _).symm
  | Pos p => PosNum.succ_to_nat _
#align num.succ'_to_nat Num.succ'_to_nat

theorem succ_to_nat (n) : (succ n : ℕ) = n + 1 :=
  succ'_to_nat n
#align num.succ_to_nat Num.succ_to_nat

@[simp, norm_cast]
theorem cast_to_nat [AddMonoidWithOne α] : ∀ n : Num, ((n : ℕ) : α) = n
  | 0 => Nat.cast_zero
  | Pos p => p.cast_to_nat
#align num.cast_to_nat Num.cast_to_nat

@[norm_cast]
theorem add_to_nat : ∀ m n, ((m + n : Num) : ℕ) = m + n
  | 0, 0 => rfl
  | 0, Pos q => (zero_add _).symm
  | Pos p, 0 => rfl
  | Pos p, Pos q => PosNum.add_to_nat _ _
#align num.add_to_nat Num.add_to_nat

@[norm_cast]
theorem mul_to_nat : ∀ m n, ((m * n : Num) : ℕ) = m * n
  | 0, 0 => rfl
  | 0, Pos q => (MulZeroClass.zero_mul _).symm
  | Pos p, 0 => rfl
  | Pos p, Pos q => PosNum.mul_to_nat _ _
#align num.mul_to_nat Num.mul_to_nat

theorem cmp_to_nat : ∀ m n, (Ordering.casesOn (cmp m n) ((m : ℕ) < n) (m = n) ((n : ℕ) < m) : Prop)
  | 0, 0 => rfl
  | 0, Pos b => to_nat_pos _
  | Pos a, 0 => to_nat_pos _
  | Pos a, Pos b =>
    by
    have := PosNum.cmp_to_nat a b <;> revert this <;> dsimp [cmp] <;> cases PosNum.cmp a b
    exacts[id, congr_arg Pos, id]
#align num.cmp_to_nat Num.cmp_to_nat

@[norm_cast]
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 by decide
    | Ordering.gt, h => by simp [not_lt_of_gt h] <;> exact by decide
#align num.lt_to_nat Num.lt_to_nat

@[norm_cast]
theorem le_to_nat {m n : Num} : (m : ℕ) ≤ n ↔ m ≤ n := by
  rw [← not_lt] <;> exact not_congr lt_to_nat
#align num.le_to_nat Num.le_to_nat

end Num

namespace PosNum

@[simp]
theorem of_to_nat' : ∀ n : PosNum, Num.ofNat' (n : ℕ) = Num.pos n
  | 1 => by erw [@Num.ofNat'_bit tt 0, Num.ofNat'_zero] <;> rfl
  | bit0 p => by erw [@Num.ofNat'_bit ff, of_to_nat'] <;> rfl
  | bit1 p => by erw [@Num.ofNat'_bit tt, of_to_nat'] <;> rfl
#align pos_num.of_to_nat' PosNum.of_to_nat'

end PosNum

namespace Num

@[simp, norm_cast]
theorem of_to_nat' : ∀ n : Num, Num.ofNat' (n : ℕ) = n
  | 0 => ofNat'_zero
  | Pos p => p.of_to_nat'
#align num.of_to_nat' Num.of_to_nat'

@[norm_cast]
theorem to_nat_inj {m n : Num} : (m : ℕ) = n ↔ m = n :=
  ⟨fun h => Function.LeftInverse.injective of_to_nat' h, congr_arg _⟩
#align num.to_nat_inj Num.to_nat_inj

/- ./././Mathport/Syntax/Translate/Expr.lean:330:4: warning: unsupported (TODO): `[tacs] -/
/-- This tactic tries to turn an (in)equality about `num`s to one about `nat`s by rewriting.
```lean
example (n : num) (m : num) : n ≤ n + m :=
begin
  num.transfer_rw,
  exact nat.le_add_right _ _
end
```
-/
unsafe def transfer_rw : tactic Unit :=
  sorry
#align num.transfer_rw num.transfer_rw

/- ./././Mathport/Syntax/Translate/Expr.lean:330:4: warning: unsupported (TODO): `[tacs] -/
/--
This tactic tries to prove (in)equalities about `num`s by transfering them to the `nat` world and
then trying to call `simp`.
```lean
example (n : num) (m : num) : n ≤ n + m := by num.transfer
```
-/
unsafe def transfer : tactic Unit :=
  sorry
#align num.transfer num.transfer

/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:69:18: unsupported non-interactive tactic num.transfer -/
instance : AddMonoid Num where
  add := (· + ·)
  zero := 0
  zero_add := zero_add
  add_zero := add_zero
  add_assoc := by
    run_tac
      transfer

instance : AddMonoidWithOne Num :=
  { Num.addMonoid with
    natCast := Num.ofNat'
    one := 1
    natCast_zero := ofNat'_zero
    natCast_succ := fun _ => ofNat'_succ }

/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:69:18: unsupported non-interactive tactic num.transfer -/
instance : CommSemiring Num := by
  refine_struct
          { Num.addMonoid,
            Num.addMonoidWithOne with
            mul := (· * ·)
            one := 1
            add := (· + ·)
            zero := 0
            npow := @npowRec Num ⟨1⟩ ⟨(· * ·)⟩ } <;>
        try intros ; rfl <;>
      try
        run_tac
          transfer <;>
    simp [add_comm, mul_add, add_mul, mul_assoc, mul_comm, mul_left_comm]

/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:69:18: unsupported non-interactive tactic num.transfer_rw -/
/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:69:18: unsupported non-interactive tactic num.transfer -/
/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:69:18: unsupported non-interactive tactic num.transfer_rw -/
/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:69:18: unsupported non-interactive tactic num.transfer_rw -/
/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:69:18: unsupported non-interactive tactic num.transfer_rw -/
/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:69:18: unsupported non-interactive tactic num.transfer_rw -/
instance : OrderedCancelAddCommMonoid Num :=
  { Num.commSemiring with
    lt := (· < ·)
    lt_iff_le_not_le := by
      intro a b
      run_tac
        transfer_rw
      apply lt_iff_le_not_le
    le := (· ≤ ·)
    le_refl := by
      run_tac
        transfer
    le_trans := by
      intro a b c
      run_tac
        transfer_rw
      apply le_trans
    le_antisymm := by
      intro a b
      run_tac
        transfer_rw
      apply le_antisymm
    add_le_add_left := by
      intro a b h c
      revert h
      run_tac
        transfer_rw
      exact fun h => add_le_add_left h c
    le_of_add_le_add_left := by
      intro a b c
      run_tac
        transfer_rw
      apply le_of_add_le_add_left }

/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:69:18: unsupported non-interactive tactic num.transfer_rw -/
/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:69:18: unsupported non-interactive tactic num.transfer_rw -/
/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:69:18: unsupported non-interactive tactic num.transfer_rw -/
instance : LinearOrderedSemiring Num :=
  { Num.commSemiring,
    Num.orderedCancelAddCommMonoid with
    le_total := by
      intro a b
      run_tac
        transfer_rw
      apply le_total
    zero_le_one := by decide
    mul_lt_mul_of_pos_left := by
      intro a b c
      run_tac
        transfer_rw
      apply mul_lt_mul_of_pos_left
    mul_lt_mul_of_pos_right := by
      intro a b c
      run_tac
        transfer_rw
      apply mul_lt_mul_of_pos_right
    decidableLt := Num.decidableLT
    decidableLe := Num.decidableLE
    DecidableEq := Num.decidableEq
    exists_pair_ne := ⟨0, 1, by decide⟩ }

@[simp, norm_cast]
theorem add_of_nat (m n) : ((m + n : ℕ) : Num) = m + n :=
  add_ofNat' _ _
#align num.add_of_nat Num.add_of_nat

@[simp, norm_cast]
theorem to_nat_to_int (n : Num) : ((n : ℕ) : ℤ) = n :=
  cast_to_nat _
#align num.to_nat_to_int Num.to_nat_to_int

@[simp, norm_cast]
theorem cast_to_int {α} [AddGroupWithOne α] (n : Num) : ((n : ℤ) : α) = n := by
  rw [← to_nat_to_int, Int.cast_ofNat, cast_to_nat]
#align num.cast_to_int Num.cast_to_int

theorem to_of_nat : ∀ n : ℕ, ((n : Num) : ℕ) = n
  | 0 => by rw [Nat.cast_zero, cast_zero]
  | n + 1 => by rw [Nat.cast_succ, add_one, succ_to_nat, to_of_nat]
#align num.to_of_nat Num.to_of_nat

@[simp, norm_cast]
theorem of_nat_cast {α} [AddMonoidWithOne α] (n : ℕ) : ((n : Num) : α) = n := by
  rw [← cast_to_nat, to_of_nat]
#align num.of_nat_cast Num.of_nat_cast

@[simp, norm_cast]
theorem of_nat_inj {m n : ℕ} : (m : Num) = n ↔ m = n :=
  ⟨fun h => Function.LeftInverse.injective to_of_nat h, congr_arg _⟩
#align num.of_nat_inj Num.of_nat_inj

@[simp, norm_cast]
theorem of_to_nat : ∀ n : Num, ((n : ℕ) : Num) = n :=
  of_to_nat'
#align num.of_to_nat Num.of_to_nat

@[norm_cast]
theorem dvd_to_nat (m n : Num) : (m : ℕ) ∣ n ↔ m ∣ n :=
  ⟨fun ⟨k, e⟩ => ⟨k, by rw [← of_to_nat n, e] <;> simp⟩, fun ⟨k, e⟩ => ⟨k, by simp [e, mul_to_nat]⟩⟩
#align num.dvd_to_nat Num.dvd_to_nat

end Num

namespace PosNum

variable {α : Type _}

open Num

@[simp, norm_cast]
theorem of_to_nat : ∀ n : PosNum, ((n : ℕ) : Num) = Num.pos n :=
  of_to_nat'
#align pos_num.of_to_nat PosNum.of_to_nat

@[norm_cast]
theorem to_nat_inj {m n : PosNum} : (m : ℕ) = n ↔ m = n :=
  ⟨fun h => Num.pos.inj <| by rw [← PosNum.of_to_nat, ← PosNum.of_to_nat, h], congr_arg _⟩
#align pos_num.to_nat_inj PosNum.to_nat_inj

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 (motive :=
      ∀ k : Num, Nat.succ ↑k = ↑n → ↑(Num.casesOn k 1 bit1 : PosNum) = Nat.pred (bit0 n)) pred' n,
      this with
    | 0, (h : ((1 : Num) : ℕ) = n) => by rw [← to_nat_inj.1 h] <;> rfl
    | Num.pos p, (h : Nat.succ ↑p = n) => by rw [← h] <;> exact (Nat.succ_add p p).symm
  | bit1 n => rfl
#align pos_num.pred'_to_nat PosNum.pred'_to_nat

@[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]
#align pos_num.pred'_succ' PosNum.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 _)]
#align pos_num.succ'_pred' PosNum.succ'_pred'

instance : Dvd PosNum :=
  ⟨fun m n => Pos m ∣ Pos n⟩

@[norm_cast]
theorem dvd_to_nat {m n : PosNum} : (m : ℕ) ∣ n ↔ m ∣ n :=
  Num.dvd_to_nat (Pos m) (Pos n)
#align pos_num.dvd_to_nat PosNum.dvd_to_nat

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]
#align pos_num.size_to_nat PosNum.size_to_nat

theorem size_eq_natSize : ∀ n, (size n : ℕ) = natSize 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]
#align pos_num.size_eq_nat_size PosNum.size_eq_natSize

theorem natSize_to_nat (n) : natSize n = Nat.size n := by rw [← size_eq_nat_size, size_to_nat]
#align pos_num.nat_size_to_nat PosNum.natSize_to_nat

theorem natSize_pos (n) : 0 < natSize n := by cases n <;> apply Nat.succ_pos
#align pos_num.nat_size_pos PosNum.natSize_pos

/- ./././Mathport/Syntax/Translate/Expr.lean:330:4: warning: unsupported (TODO): `[tacs] -/
/-- This tactic tries to turn an (in)equality about `pos_num`s to one about `nat`s by rewriting.
```lean
example (n : pos_num) (m : pos_num) : n ≤ n + m :=
begin
  pos_num.transfer_rw,
  exact nat.le_add_right _ _
end
```
-/
unsafe def transfer_rw : tactic Unit :=
  sorry
#align pos_num.transfer_rw pos_num.transfer_rw

/- ./././Mathport/Syntax/Translate/Expr.lean:330:4: warning: unsupported (TODO): `[tacs] -/
/--
This tactic tries to prove (in)equalities about `pos_num`s by transferring them to the `nat` world
and then trying to call `simp`.
```lean
example (n : pos_num) (m : pos_num) : n ≤ n + m := by pos_num.transfer
```
-/
unsafe def transfer : tactic Unit :=
  sorry
#align pos_num.transfer pos_num.transfer

/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:69:18: unsupported non-interactive tactic pos_num.transfer -/
instance : AddCommSemigroup PosNum := by
  refine' { add := (· + ·).. } <;>
    run_tac
      transfer

/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:69:18: unsupported non-interactive tactic pos_num.transfer -/
instance : CommMonoid PosNum := by
  refine_struct
        { mul := (· * ·)
          one := (1 : PosNum)
          npow := @npowRec PosNum ⟨1⟩ ⟨(· * ·)⟩ } <;>
      try intros ; rfl <;>
    run_tac
      transfer

/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:69:18: unsupported non-interactive tactic pos_num.transfer -/
instance : Distrib PosNum := by
  refine'
      { add := (· + ·)
        mul := (· * ·).. } <;>
    · run_tac
        transfer
      simp [mul_add, mul_comm]

/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:69:18: unsupported non-interactive tactic pos_num.transfer_rw -/
/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:69:18: unsupported non-interactive tactic pos_num.transfer -/
/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:69:18: unsupported non-interactive tactic pos_num.transfer_rw -/
/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:69:18: unsupported non-interactive tactic pos_num.transfer_rw -/
/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:69:18: unsupported non-interactive tactic pos_num.transfer_rw -/
instance : LinearOrder PosNum where
  lt := (· < ·)
  lt_iff_le_not_le := by
    intro a b
    run_tac
      transfer_rw
    apply lt_iff_le_not_le
  le := (· ≤ ·)
  le_refl := by
    run_tac
      transfer
  le_trans := by
    intro a b c
    run_tac
      transfer_rw
    apply le_trans
  le_antisymm := by
    intro a b
    run_tac
      transfer_rw
    apply le_antisymm
  le_total := by
    intro a b
    run_tac
      transfer_rw
    apply le_total
  decidableLt := by infer_instance
  decidableLe := by infer_instance
  DecidableEq := by infer_instance

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

@[simp, norm_cast]
theorem bit_to_nat (b n) : (bit b n : ℕ) = Nat.bit b n := by cases b <;> rfl
#align pos_num.bit_to_nat PosNum.bit_to_nat

@[simp, norm_cast]
theorem cast_add [AddMonoidWithOne α] (m n) : ((m + n : PosNum) : α) = m + n := by
  rw [← cast_to_nat, add_to_nat, Nat.cast_add, cast_to_nat, cast_to_nat]
#align pos_num.cast_add PosNum.cast_add

@[simp, norm_cast]
theorem cast_succ [AddMonoidWithOne α] (n : PosNum) : (succ n : α) = n + 1 := by
  rw [← add_one, cast_add, cast_one]
#align pos_num.cast_succ PosNum.cast_succ

@[simp, norm_cast]
theorem cast_inj [AddMonoidWithOne α] [CharZero α] {m n : PosNum} : (m : α) = n ↔ m = n := by
  rw [← cast_to_nat m, ← cast_to_nat n, Nat.cast_inj, to_nat_inj]
#align pos_num.cast_inj PosNum.cast_inj

@[simp]
theorem one_le_cast [LinearOrderedSemiring α] (n : PosNum) : (1 : α) ≤ n := by
  rw [← cast_to_nat, ← Nat.cast_one, Nat.cast_le] <;> apply to_nat_pos
#align pos_num.one_le_cast PosNum.one_le_cast

@[simp]
theorem cast_pos [LinearOrderedSemiring α] (n : PosNum) : 0 < (n : α) :=
  lt_of_lt_of_le zero_lt_one (one_le_cast n)
#align pos_num.cast_pos PosNum.cast_pos

@[simp, norm_cast]
theorem cast_mul [Semiring α] (m n) : ((m * n : PosNum) : α) = m * n := by
  rw [← cast_to_nat, mul_to_nat, Nat.cast_mul, cast_to_nat, cast_to_nat]
#align pos_num.cast_mul PosNum.cast_mul

@[simp]
theorem cmp_eq (m n) : cmp m n = Ordering.eq ↔ m = n :=
  by
  have := cmp_to_nat m n
  cases cmp m n <;> simp at this⊢ <;> try exact this <;>
    · simp [show m ≠ n from fun e => by rw [e] at this <;> exact lt_irrefl _ this]
#align pos_num.cmp_eq PosNum.cmp_eq

@[simp, norm_cast]
theorem cast_lt [LinearOrderedSemiring α] {m n : PosNum} : (m : α) < n ↔ m < n := by
  rw [← cast_to_nat m, ← cast_to_nat n, Nat.cast_lt, lt_to_nat]
#align pos_num.cast_lt PosNum.cast_lt

@[simp, norm_cast]
theorem cast_le [LinearOrderedSemiring α] {m n : PosNum} : (m : α) ≤ n ↔ m ≤ n := by
  rw [← not_lt] <;> exact not_congr cast_lt
#align pos_num.cast_le PosNum.cast_le

end PosNum

namespace Num

variable {α : Type _}

open PosNum

theorem bit_to_nat (b n) : (bit b n : ℕ) = Nat.bit b n := by cases b <;> cases n <;> rfl
#align num.bit_to_nat Num.bit_to_nat

theorem cast_succ' [AddMonoidWithOne α] (n) : (succ' n : α) = n + 1 := by
  rw [← PosNum.cast_to_nat, succ'_to_nat, Nat.cast_add_one, cast_to_nat]
#align num.cast_succ' Num.cast_succ'

theorem cast_succ [AddMonoidWithOne α] (n) : (succ n : α) = n + 1 :=
  cast_succ' n
#align num.cast_succ Num.cast_succ

@[simp, norm_cast]
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]
#align num.cast_add Num.cast_add

@[simp, norm_cast]
theorem cast_bit0 [Semiring α] (n : Num) : (n.bit0 : α) = bit0 n := by
  rw [← bit0_of_bit0, _root_.bit0, cast_add] <;> rfl
#align num.cast_bit0 Num.cast_bit0

@[simp, norm_cast]
theorem cast_bit1 [Semiring α] (n : Num) : (n.bit1 : α) = bit1 n := by
  rw [← bit1_of_bit1, _root_.bit1, bit0_of_bit0, cast_add, cast_bit0] <;> rfl
#align num.cast_bit1 Num.cast_bit1

@[simp, norm_cast]
theorem cast_mul [Semiring α] : ∀ m n, ((m * n : Num) : α) = m * n
  | 0, 0 => (MulZeroClass.zero_mul _).symm
  | 0, Pos q => (MulZeroClass.zero_mul _).symm
  | Pos p, 0 => (MulZeroClass.mul_zero _).symm
  | Pos p, Pos q => PosNum.cast_mul _ _
#align num.cast_mul Num.cast_mul

theorem size_to_nat : ∀ n, (size n : ℕ) = Nat.size n
  | 0 => Nat.size_zero.symm
  | Pos p => p.size_to_nat
#align num.size_to_nat Num.size_to_nat

theorem size_eq_natSize : ∀ n, (size n : ℕ) = natSize n
  | 0 => rfl
  | Pos p => p.size_eq_natSize
#align num.size_eq_nat_size Num.size_eq_natSize

theorem natSize_to_nat (n) : natSize n = Nat.size n := by rw [← size_eq_nat_size, size_to_nat]
#align num.nat_size_to_nat Num.natSize_to_nat

@[simp]
theorem ofNat'_eq : ∀ n, Num.ofNat' n = n :=
  Nat.binaryRec (by simp) fun b n IH => by
    rw [of_nat'] at IH⊢
    rw [Nat.binaryRec_eq, IH]
    · cases b <;> simp [Nat.bit, bit0_of_bit0, bit1_of_bit1]
    · rfl
#align num.of_nat'_eq Num.ofNat'_eq

theorem zneg_toZNum (n : Num) : -n.toZNum = n.toZNumNeg := by cases n <;> rfl
#align num.zneg_to_znum Num.zneg_toZNum

theorem zneg_toZNumNeg (n : Num) : -n.toZNumNeg = n.toZNum := by cases n <;> rfl
#align num.zneg_to_znum_neg Num.zneg_toZNumNeg

theorem toZNum_inj {m n : Num} : m.toZNum = n.toZNum ↔ m = n :=
  ⟨fun h => by cases m <;> cases n <;> cases h <;> rfl, congr_arg _⟩
#align num.to_znum_inj Num.toZNum_inj

@[simp, norm_cast squash]
theorem cast_toZNum [Zero α] [One α] [Add α] [Neg α] : ∀ n : Num, (n.toZNum : α) = n
  | 0 => rfl
  | Num.pos p => rfl
#align num.cast_to_znum Num.cast_toZNum

@[simp]
theorem cast_toZNumNeg [AddGroup α] [One α] : ∀ n : Num, (n.toZNumNeg : α) = -n
  | 0 => neg_zero.symm
  | Num.pos p => rfl
#align num.cast_to_znum_neg Num.cast_toZNumNeg

@[simp]
theorem add_toZNum (m n : Num) : Num.toZNum (m + n) = m.toZNum + n.toZNum := by
  cases m <;> cases n <;> rfl
#align num.add_to_znum Num.add_toZNum

end Num

namespace PosNum

open Num

theorem pred_to_nat {n : PosNum} (h : 1 < n) : (pred n : ℕ) = Nat.pred n :=
  by
  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 (by decide)
  · rw [← pred'_to_nat, e]
    rfl
#align pos_num.pred_to_nat PosNum.pred_to_nat

theorem sub'_one (a : PosNum) : sub' a 1 = (pred' a).toZNum := by cases a <;> rfl
#align pos_num.sub'_one PosNum.sub'_one

theorem one_sub' (a : PosNum) : sub' 1 a = (pred' a).toZNumNeg := by cases a <;> rfl
#align pos_num.one_sub' PosNum.one_sub'

theorem lt_iff_cmp {m n} : m < n ↔ cmp m n = Ordering.lt :=
  Iff.rfl
#align pos_num.lt_iff_cmp PosNum.lt_iff_cmp

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 by decide
#align pos_num.le_iff_cmp PosNum.le_iff_cmp

end PosNum

namespace Num

variable {α : Type _}

open PosNum

theorem pred_to_nat : ∀ n : Num, (pred n : ℕ) = Nat.pred n
  | 0 => rfl
  | Pos p => by rw [pred, PosNum.pred'_to_nat] <;> rfl
#align num.pred_to_nat Num.pred_to_nat

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 [PosNum.pred'_to_nat, Nat.succ_pred_eq_of_pos (PosNum.to_nat_pos _)] <;>
      rfl
#align num.ppred_to_nat Num.ppred_to_nat

theorem cmp_swap (m n) : (cmp m n).symm = cmp n m := by
  cases m <;> cases n <;> try unfold cmp <;> try rfl <;> apply PosNum.cmp_swap
#align num.cmp_swap Num.cmp_swap

theorem cmp_eq (m n) : cmp m n = Ordering.eq ↔ m = n :=
  by
  have := cmp_to_nat m n
  cases cmp m n <;> simp at this⊢ <;> try exact this <;>
    · simp [show m ≠ n from fun e => by rw [e] at this <;> exact lt_irrefl _ this]
#align num.cmp_eq Num.cmp_eq

@[simp, norm_cast]
theorem cast_lt [LinearOrderedSemiring α] {m n : Num} : (m : α) < n ↔ m < n := by
  rw [← cast_to_nat m, ← cast_to_nat n, Nat.cast_lt, lt_to_nat]
#align num.cast_lt Num.cast_lt

@[simp, norm_cast]
theorem cast_le [LinearOrderedSemiring α] {m n : Num} : (m : α) ≤ n ↔ m ≤ n := by
  rw [← not_lt] <;> exact not_congr cast_lt
#align num.cast_le Num.cast_le

@[simp, norm_cast]
theorem cast_inj [LinearOrderedSemiring α] {m n : Num} : (m : α) = n ↔ m = n := by
  rw [← cast_to_nat m, ← cast_to_nat n, Nat.cast_inj, to_nat_inj]
#align num.cast_inj Num.cast_inj

theorem lt_iff_cmp {m n} : m < n ↔ cmp m n = Ordering.lt :=
  Iff.rfl
#align num.lt_iff_cmp Num.lt_iff_cmp

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 by decide
#align num.le_iff_cmp Num.le_iff_cmp

theorem bitwise'_to_nat {f : Num → Num → Num} {g : Bool → Bool → Bool} (p : PosNum → PosNum → Num)
    (gff : g false false = false) (f00 : f 0 0 = 0)
    (f0n : ∀ n, f 0 (pos n) = cond (g false true) (pos n) 0)
    (fn0 : ∀ n, f (pos n) 0 = cond (g true false) (pos n) 0)
    (fnn : ∀ m n, f (pos m) (pos n) = p m n) (p11 : p 1 1 = cond (g true true) 1 0)
    (p1b : ∀ b n, p 1 (PosNum.bit b n) = bit (g true b) (cond (g false true) (pos n) 0))
    (pb1 : ∀ a m, p (PosNum.bit a m) 1 = bit (g a true) (cond (g true false) (pos m) 0))
    (pbb : ∀ a b m n, p (PosNum.bit a m) (PosNum.bit b n) = bit (g a b) (p m n)) :
    ∀ m n : Num, (f m n : ℕ) = Nat.bitwise' g m n :=
  by
  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] <;> rfl
  · unfold Nat.bitwise'
    rw [f0n, Nat.binaryRec_zero]
    cases g ff tt <;> rfl
  · unfold Nat.bitwise'
    generalize h : (Pos m : ℕ) = m'
    revert h
    apply Nat.bitCasesOn m' _
    intro b m' h
    rw [fn0, Nat.binaryRec_eq, Nat.binaryRec_zero, ← h]
    cases g tt ff <;> rfl
    apply Nat.bitwise'_bit_aux gff
  · rw [fnn]
    have : ∀ (b) (n : PosNum), (cond b (↑n) 0 : ℕ) = ↑(cond b (Pos n) 0 : Num) := by
      intros <;> cases b <;> rfl
    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 PosNum.bit0 with PosNum.bit ff
    any_goals change PosNum.bit1 with PosNum.bit tt
    any_goals change ((1 : Num) : ℕ) with Nat.bit tt 0
    all_goals
      repeat'
        rw [show ∀ b n, (Pos (PosNum.bit b n) : ℕ) = Nat.bit b ↑n by intros <;> cases b <;> rfl]
      rw [Nat.bitwise'_bit]
    any_goals assumption
    any_goals rw [Nat.bitwise'_zero, p11]; cases g tt tt <;> rfl
    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]
#align num.bitwise_to_nat Num.bitwise'_to_nat

@[simp, norm_cast]
theorem lor_to_nat : ∀ m n, (lor m n : ℕ) = Nat.lor' m n := by
  apply bitwise_to_nat fun x y => Pos (PosNum.lor x y) <;> intros <;> try cases a <;>
      try cases b <;>
    rfl
#align num.lor_to_nat Num.lor_to_nat

@[simp, norm_cast]
theorem land_to_nat : ∀ m n, (land m n : ℕ) = Nat.land' m n := by
  apply bitwise_to_nat PosNum.land <;> intros <;> try cases a <;> try cases b <;> rfl
#align num.land_to_nat Num.land_to_nat

@[simp, norm_cast]
theorem ldiff_to_nat : ∀ m n, (ldiff m n : ℕ) = Nat.ldiff' m n := by
  apply bitwise_to_nat PosNum.ldiff <;> intros <;> try cases a <;> try cases b <;> rfl
#align num.ldiff_to_nat Num.ldiff_to_nat

@[simp, norm_cast]
theorem lxor_to_nat : ∀ m n, (lxor m n : ℕ) = Nat.lxor' m n := by
  apply bitwise_to_nat PosNum.lxor <;> intros <;> try cases a <;> try cases b <;> rfl
#align num.lxor_to_nat Num.lxor_to_nat

@[simp, norm_cast]
theorem shiftl_to_nat (m n) : (shiftl m n : ℕ) = Nat.shiftl m n :=
  by
  cases m <;> dsimp only [shiftl];
  · symm
    apply Nat.zero_shiftl
  simp; induction' n with n IH; · rfl
  simp [PosNum.shiftl, Nat.shiftl_succ]; rw [← IH]
#align num.shiftl_to_nat Num.shiftl_to_nat

@[simp, norm_cast]
theorem shiftr_to_nat (m n) : (shiftr m n : ℕ) = Nat.shiftr m n :=
  by
  cases' m with m <;> dsimp only [shiftr];
  · symm
    apply Nat.zero_shiftr
  induction' n with n IH generalizing m; · cases m <;> rfl
  cases' m with m m <;> dsimp only [PosNum.shiftr]
  · rw [Nat.shiftr_eq_div_pow]
    symm
    apply Nat.div_eq_of_lt
    exact @Nat.pow_lt_pow_of_lt_right 2 (by decide) 0 (n + 1) (Nat.succ_pos _)
  · trans
    apply IH
    change Nat.shiftr m n = Nat.shiftr (bit1 m) (n + 1)
    rw [add_comm n 1, Nat.shiftr_add]
    apply congr_arg fun x => Nat.shiftr x n
    unfold Nat.shiftr
    change (bit1 ↑m : ℕ) with Nat.bit tt m
    rw [Nat.div2_bit]
  · trans
    apply IH
    change Nat.shiftr m n = Nat.shiftr (bit0 m) (n + 1)
    rw [add_comm n 1, Nat.shiftr_add]
    apply congr_arg fun x => Nat.shiftr x n
    unfold Nat.shiftr
    change (bit0 ↑m : ℕ) with Nat.bit ff m
    rw [Nat.div2_bit]
#align num.shiftr_to_nat Num.shiftr_to_nat

@[simp]
theorem testBit_to_nat (m n) : testBit m n = Nat.testBit m n :=
  by
  cases' m with m <;> unfold test_bit Nat.testBit
  · change (zero : Nat) with 0
    rw [Nat.zero_shiftr]
    rfl
  induction' n with n IH generalizing m <;> cases m <;> dsimp only [PosNum.testBit]
  · rfl
  · 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] <;> rfl
  · change PosNum.testBit 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 PosNum.testBit 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
#align num.test_bit_to_nat Num.testBit_to_nat

end Num

namespace ZNum

variable {α : Type _}

open PosNum

@[simp, norm_cast]
theorem cast_zero [Zero α] [One α] [Add α] [Neg α] : ((0 : ZNum) : α) = 0 :=
  rfl
#align znum.cast_zero ZNum.cast_zero

@[simp]
theorem cast_zero' [Zero α] [One α] [Add α] [Neg α] : (ZNum.zero : α) = 0 :=
  rfl
#align znum.cast_zero' ZNum.cast_zero'

@[simp, norm_cast]
theorem cast_one [Zero α] [One α] [Add α] [Neg α] : ((1 : ZNum) : α) = 1 :=
  rfl
#align znum.cast_one ZNum.cast_one

@[simp]
theorem cast_pos [Zero α] [One α] [Add α] [Neg α] (n : PosNum) : (pos n : α) = n :=
  rfl
#align znum.cast_pos ZNum.cast_pos

@[simp]
theorem cast_neg [Zero α] [One α] [Add α] [Neg α] (n : PosNum) : (neg n : α) = -n :=
  rfl
#align znum.cast_neg ZNum.cast_neg

@[simp, norm_cast]
theorem cast_zneg [AddGroup α] [One α] : ∀ n, ((-n : ZNum) : α) = -n
  | 0 => neg_zero.symm
  | Pos p => rfl
  | neg p => (neg_neg _).symm
#align znum.cast_zneg ZNum.cast_zneg

theorem neg_zero : (-0 : ZNum) = 0 :=
  rfl
#align znum.neg_zero ZNum.neg_zero

theorem zneg_pos (n : PosNum) : -pos n = neg n :=
  rfl
#align znum.zneg_pos ZNum.zneg_pos

theorem zneg_neg (n : PosNum) : -neg n = pos n :=
  rfl
#align znum.zneg_neg ZNum.zneg_neg

theorem zneg_zneg (n : ZNum) : - -n = n := by cases n <;> rfl
#align znum.zneg_zneg ZNum.zneg_zneg

theorem zneg_bit1 (n : ZNum) : -n.bit1 = (-n).bitm1 := by cases n <;> rfl
#align znum.zneg_bit1 ZNum.zneg_bit1

theorem zneg_bitm1 (n : ZNum) : -n.bitm1 = (-n).bit1 := by cases n <;> rfl
#align znum.zneg_bitm1 ZNum.zneg_bitm1

theorem zneg_succ (n : ZNum) : -n.succ = (-n).pred := by
  cases n <;> try rfl <;> rw [succ, Num.zneg_toZNumNeg] <;> rfl
#align znum.zneg_succ ZNum.zneg_succ

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

@[simp]
theorem abs_to_nat : ∀ n, (abs n : ℕ) = Int.natAbs n
  | 0 => rfl
  | Pos p => congr_arg Int.natAbs p.to_nat_to_int
  | neg p => show Int.natAbs ((p : ℕ) : ℤ) = Int.natAbs (-p) by rw [p.to_nat_to_int, Int.natAbs_neg]
#align znum.abs_to_nat ZNum.abs_to_nat

@[simp]
theorem abs_toZNum : ∀ n : Num, abs n.toZNum = n
  | 0 => rfl
  | Num.pos p => rfl
#align znum.abs_to_znum ZNum.abs_toZNum

@[simp, norm_cast]
theorem cast_to_int [AddGroupWithOne α] : ∀ n : ZNum, ((n : ℤ) : α) = n
  | 0 => by rw [cast_zero, cast_zero, Int.cast_zero]
  | Pos p => by rw [cast_pos, cast_pos, PosNum.cast_to_int]
  | neg p => by rw [cast_neg, cast_neg, Int.cast_neg, PosNum.cast_to_int]
#align znum.cast_to_int ZNum.cast_to_int

theorem bit0_of_bit0 : ∀ n : ZNum, bit0 n = n.bit0
  | 0 => rfl
  | Pos a => congr_arg pos a.bit0_of_bit0
  | neg a => congr_arg neg a.bit0_of_bit0
#align znum.bit0_of_bit0 ZNum.bit0_of_bit0

theorem bit1_of_bit1 : ∀ n : ZNum, bit1 n = n.bit1
  | 0 => rfl
  | Pos a => congr_arg pos a.bit1_of_bit1
  | neg a => show PosNum.sub' 1 (bit0 a) = _ by rw [PosNum.one_sub', a.bit0_of_bit0] <;> rfl
#align znum.bit1_of_bit1 ZNum.bit1_of_bit1

@[simp, norm_cast]
theorem cast_bit0 [AddGroupWithOne α] : ∀ n : ZNum, (n.bit0 : α) = bit0 n
  | 0 => (add_zero _).symm
  | Pos p => by rw [ZNum.bit0, cast_pos, cast_pos] <;> rfl
  | neg p => by
    rw [ZNum.bit0, cast_neg, cast_neg, PosNum.cast_bit0, _root_.bit0, _root_.bit0, neg_add_rev]
#align znum.cast_bit0 ZNum.cast_bit0

@[simp, norm_cast]
theorem cast_bit1 [AddGroupWithOne α] : ∀ 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] <;> rfl
  | neg p => by
    rw [ZNum.bit1, cast_neg, cast_neg]
    cases' e : pred' p with a <;> 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 : ℤ) := by simp [add_comm]
      simpa [_root_.bit1, _root_.bit0, -add_comm]
#align znum.cast_bit1 ZNum.cast_bit1

@[simp]
theorem cast_bitm1 [AddGroupWithOne α] (n : ZNum) : (n.bitm1 : α) = bit0 n - 1 :=
  by
  conv =>
    lhs
    rw [← zneg_zneg n]
  rw [← zneg_bit1, cast_zneg, cast_bit1]
  have : ((-1 + n + n : ℤ) : α) = (n + n + -1 : ℤ) := by simp [add_comm, add_left_comm]
  simpa [_root_.bit1, _root_.bit0, sub_eq_add_neg, -Int.add_neg_one]
#align znum.cast_bitm1 ZNum.cast_bitm1

theorem add_zero (n : ZNum) : n + 0 = n := by cases n <;> rfl
#align znum.add_zero ZNum.add_zero

theorem zero_add (n : ZNum) : 0 + n = n := by cases n <;> rfl
#align znum.zero_add ZNum.zero_add

theorem add_one : ∀ n : ZNum, n + 1 = succ n
  | 0 => rfl
  | Pos p => congr_arg pos p.add_one
  | neg p => by cases p <;> rfl
#align znum.add_one ZNum.add_one

end ZNum

namespace PosNum

variable {α : Type _}

theorem cast_to_zNum : ∀ n : PosNum, (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)
#align pos_num.cast_to_znum PosNum.cast_to_zNum

attribute [-simp] Int.add_neg_one

theorem cast_sub' [AddGroupWithOne α] : ∀ m n : PosNum, (sub' m n : α) = m - n
  | a, 1 => by
    rw [sub'_one, Num.cast_toZNum, ← Num.cast_to_nat, pred'_to_nat, ← Nat.sub_one] <;>
      simp [PosNum.cast_pos]
  | 1, b => by
    rw [one_sub', Num.cast_toZNumNeg, ← neg_sub, neg_inj, ← Num.cast_to_nat, pred'_to_nat, ←
        Nat.sub_one] <;>
      simp [PosNum.cast_pos]
  | bit0 a, bit0 b => by
    rw [sub', ZNum.cast_bit0, cast_sub']
    have : ((a + -b + (a + -b) : ℤ) : α) = a + a + (-b + -b) := by simp [add_left_comm]
    simpa [_root_.bit0, sub_eq_add_neg]
  | bit0 a, bit1 b => by
    rw [sub', ZNum.cast_bitm1, cast_sub']
    have : ((-b + (a + (-b + -1)) : ℤ) : α) = (a + -1 + (-b + -b) : ℤ) := by
      simp [add_comm, add_left_comm]
    simpa [_root_.bit1, _root_.bit0, sub_eq_add_neg]
  | bit1 a, bit0 b => by
    rw [sub', ZNum.cast_bit1, cast_sub']
    have : ((-b + (a + (-b + 1)) : ℤ) : α) = (a + 1 + (-b + -b) : ℤ) := by
      simp [add_comm, add_left_comm]
    simpa [_root_.bit1, _root_.bit0, sub_eq_add_neg]
  | bit1 a, bit1 b => by
    rw [sub', ZNum.cast_bit0, cast_sub']
    have : ((-b + (a + -b) : ℤ) : α) = a + (-b + -b) := by simp [add_left_comm]
    simpa [_root_.bit1, _root_.bit0, sub_eq_add_neg]
#align pos_num.cast_sub' PosNum.cast_sub'

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

theorem to_int_eq_succ_pred (n : PosNum) : (n : ℤ) = (n.pred' : ℕ) + 1 := by
  rw [← n.to_nat_to_int, to_nat_eq_succ_pred] <;> rfl
#align pos_num.to_int_eq_succ_pred PosNum.to_int_eq_succ_pred

end PosNum

namespace Num

variable {α : Type _}

@[simp]
theorem cast_sub' [AddGroupWithOne α] : ∀ 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 => PosNum.cast_sub' _ _
#align num.cast_sub' Num.cast_sub'

theorem toZNum_succ : ∀ n : Num, n.succ.toZNum = n.toZNum.succ
  | 0 => rfl
  | Pos n => rfl
#align num.to_znum_succ Num.toZNum_succ

theorem toZNumNeg_succ : ∀ n : Num, n.succ.toZNumNeg = n.toZNumNeg.pred
  | 0 => rfl
  | Pos n => rfl
#align num.to_znum_neg_succ Num.toZNumNeg_succ

@[simp]
theorem pred_succ : ∀ n : ZNum, n.pred.succ = n
  | 0 => rfl
  | ZNum.neg p => show toZNumNeg (pos p).succ'.pred' = _ by rw [PosNum.pred'_succ'] <;> rfl
  | ZNum.pos p => by rw [ZNum.pred, ← to_znum_succ, Num.succ, PosNum.succ'_pred', to_znum]
#align num.pred_succ Num.pred_succ

theorem succ_ofInt' : ∀ n, ZNum.ofInt' (n + 1) = ZNum.ofInt' n + 1
  | (n : ℕ) => by
    erw [ZNum.ofInt', ZNum.ofInt', Num.ofNat'_succ, Num.add_one, to_znum_succ, ZNum.add_one]
  | -[0+1] => by erw [ZNum.ofInt', ZNum.ofInt', of_nat'_succ, of_nat'_zero] <;> rfl
  | -[n + 1+1] => by
    erw [ZNum.ofInt', ZNum.ofInt', @Num.ofNat'_succ (n + 1), Num.add_one, to_znum_neg_succ,
      @of_nat'_succ n, Num.add_one, ZNum.add_one, pred_succ]
#align num.succ_of_int' Num.succ_ofInt'

theorem ofInt'_toZNum : ∀ n : ℕ, toZNum n = ZNum.ofInt' n
  | 0 => rfl
  | n + 1 => by
    rw [Nat.cast_succ, Num.add_one, to_znum_succ, of_int'_to_znum, Nat.cast_succ, succ_of_int',
      ZNum.add_one]
#align num.of_int'_to_znum Num.ofInt'_toZNum

theorem mem_ofZNum' : ∀ {m : Num} {n : ZNum}, m ∈ ofZNum' n ↔ n = toZNum m
  | 0, 0 => ⟨fun _ => rfl, fun _ => rfl⟩
  | Pos m, 0 => ⟨fun h => by cases h, fun h => by cases h⟩
  | m, ZNum.pos p =>
    Option.some_inj.trans <| by cases m <;> constructor <;> intro h <;> try cases h <;> rfl
  | m, ZNum.neg p => ⟨fun h => by cases h, fun h => by cases m <;> cases h⟩
#align num.mem_of_znum' Num.mem_ofZNum'

theorem ofZNum'_to_nat : ∀ n : ZNum, coe <$> ofZNum' n = Int.toNat' n
  | 0 => rfl
  | ZNum.pos p => show _ = Int.toNat' p by rw [← PosNum.to_nat_to_int p] <;> rfl
  | ZNum.neg p =>
    (congr_arg fun x => Int.toNat' (-x)) <|
      show ((p.pred' + 1 : ℕ) : ℤ) = p by rw [← succ'_to_nat] <;> simp
#align num.of_znum'_to_nat Num.ofZNum'_to_nat

@[simp]
theorem ofZNum_toNat : ∀ n : ZNum, (ofZNum n : ℕ) = Int.toNat n
  | 0 => rfl
  | ZNum.pos p => show _ = Int.toNat p by rw [← PosNum.to_nat_to_int p] <;> rfl
  | ZNum.neg p =>
    (congr_arg fun x => Int.toNat (-x)) <|
      show ((p.pred' + 1 : ℕ) : ℤ) = p by rw [← succ'_to_nat] <;> simp
#align num.of_znum_to_nat Num.ofZNum_toNat

@[simp]
theorem cast_ofZNum [AddGroupWithOne α] (n : ZNum) : (ofZNum n : α) = Int.toNat n := by
  rw [← cast_to_nat, of_znum_to_nat]
#align num.cast_of_znum Num.cast_ofZNum

@[simp, norm_cast]
theorem sub_to_nat (m n) : ((m - n : Num) : ℕ) = m - n :=
  show (ofZNum _ : ℕ) = _ by
    rw [of_znum_to_nat, cast_sub', ← to_nat_to_int, ← to_nat_to_int, Int.toNat_sub]
#align num.sub_to_nat Num.sub_to_nat

end Num

namespace ZNum

variable {α : Type _}

@[simp, norm_cast]
theorem cast_add [AddGroupWithOne α] : ∀ 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 => PosNum.cast_add _ _
  | Pos a, neg b => by simpa only [sub_eq_add_neg] using PosNum.cast_sub' _ _
  | neg a, Pos b =>
    have : (↑b + -↑a : α) = -↑a + ↑b := by
      rw [← PosNum.cast_to_int a, ← PosNum.cast_to_int b, ← Int.cast_neg, ← Int.cast_add (-a)] <;>
        simp [add_comm]
    (PosNum.cast_sub' _ _).trans <| (sub_eq_add_neg _ _).trans this
  | neg a, neg b =>
    show -(↑(a + b) : α) = -a + -b by
      rw [PosNum.cast_add, neg_eq_iff_eq_neg, neg_add_rev, neg_neg, neg_neg, ← PosNum.cast_to_int a,
        ← PosNum.cast_to_int b, ← Int.cast_add, ← Int.cast_add, add_comm]
#align znum.cast_add ZNum.cast_add

@[simp]
theorem cast_succ [AddGroupWithOne α] (n) : ((succ n : ZNum) : α) = n + 1 := by
  rw [← add_one, cast_add, cast_one]
#align znum.cast_succ ZNum.cast_succ

@[simp, norm_cast]
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 => PosNum.cast_mul a b
  | Pos a, neg b => show -↑(a * b) = ↑a * -↑b by rw [PosNum.cast_mul, neg_mul_eq_mul_neg]
  | neg a, Pos b => show -↑(a * b) = -↑a * ↑b by rw [PosNum.cast_mul, neg_mul_eq_neg_mul]
  | neg a, neg b => show ↑(a * b) = -↑a * -↑b by rw [PosNum.cast_mul, neg_mul_neg]
#align znum.mul_to_int ZNum.mul_to_int

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]
#align znum.cast_mul ZNum.cast_mul

theorem ofInt'_neg : ∀ n : ℤ, ofInt' (-n) = -ofInt' n
  | -[n+1] => show ofInt' (n + 1 : ℕ) = _ by simp only [of_int', Num.zneg_toZNumNeg]
  | 0 => show Num.toZNum _ = -Num.toZNum _ by rw [Num.ofNat'_zero] <;> rfl
  | (n + 1 : ℕ) => show Num.toZNumNeg _ = -Num.toZNum _ by rw [Num.zneg_toZNum] <;> rfl
#align znum.of_int'_neg ZNum.ofInt'_neg

theorem of_to_int' : ∀ n : ZNum, ZNum.ofInt' n = n
  | 0 => by erw [of_int', Num.ofNat'_zero, Num.toZNum]
  | Pos a => by rw [cast_pos, ← PosNum.cast_to_nat, ← Num.ofInt'_toZNum, PosNum.of_to_nat] <;> rfl
  | neg a => by
    rw [cast_neg, of_int'_neg, ← PosNum.cast_to_nat, ← Num.ofInt'_toZNum, PosNum.of_to_nat] <;> rfl
#align znum.of_to_int' ZNum.of_to_int'

theorem to_int_inj {m n : ZNum} : (m : ℤ) = n ↔ m = n :=
  ⟨fun h => Function.LeftInverse.injective of_to_int' h, congr_arg _⟩
#align znum.to_int_inj ZNum.to_int_inj

theorem cmp_to_int : ∀ m n, (Ordering.casesOn (cmp m n) ((m : ℤ) < n) (m = n) ((n : ℤ) < m) : Prop)
  | 0, 0 => rfl
  | Pos a, Pos b => by
    have := PosNum.cmp_to_nat a b <;> revert this <;> dsimp [cmp] <;> cases PosNum.cmp a b <;>
        dsimp <;>
      [simp, exact congr_arg Pos, simp [GT.gt]]
  | neg a, neg b => by
    have := PosNum.cmp_to_nat b a <;> revert this <;> dsimp [cmp] <;> cases PosNum.cmp b a <;>
        dsimp <;>
      [simp, simp (config := { contextual := true }), simp [GT.gt]]
  | Pos a, 0 => PosNum.cast_pos _
  | Pos a, neg b => lt_trans (neg_lt_zero.2 <| PosNum.cast_pos _) (PosNum.cast_pos _)
  | 0, neg b => neg_lt_zero.2 <| PosNum.cast_pos _
  | neg a, 0 => neg_lt_zero.2 <| PosNum.cast_pos _
  | neg a, Pos b => lt_trans (neg_lt_zero.2 <| PosNum.cast_pos _) (PosNum.cast_pos _)
  | 0, Pos b => PosNum.cast_pos _
#align znum.cmp_to_int ZNum.cmp_to_int

@[norm_cast]
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 by decide
    | Ordering.gt, h => by simp [not_lt_of_gt h] <;> exact by decide
#align znum.lt_to_int ZNum.lt_to_int

theorem le_to_int {m n : ZNum} : (m : ℤ) ≤ n ↔ m ≤ n := by
  rw [← not_lt] <;> exact not_congr lt_to_int
#align znum.le_to_int ZNum.le_to_int

@[simp, norm_cast]
theorem cast_lt [LinearOrderedRing α] {m n : ZNum} : (m : α) < n ↔ m < n := by
  rw [← cast_to_int m, ← cast_to_int n, Int.cast_lt, lt_to_int]
#align znum.cast_lt ZNum.cast_lt

@[simp, norm_cast]
theorem cast_le [LinearOrderedRing α] {m n : ZNum} : (m : α) ≤ n ↔ m ≤ n := by
  rw [← not_lt] <;> exact not_congr cast_lt
#align znum.cast_le ZNum.cast_le

@[simp, norm_cast]
theorem cast_inj [LinearOrderedRing α] {m n : ZNum} : (m : α) = n ↔ m = n := by
  rw [← cast_to_int m, ← cast_to_int n, Int.cast_inj, to_int_inj]
#align znum.cast_inj ZNum.cast_inj

/- ./././Mathport/Syntax/Translate/Expr.lean:330:4: warning: unsupported (TODO): `[tacs] -/
/-- This tactic tries to turn an (in)equality about `znum`s to one about `int`s by rewriting.
```lean
example (n : znum) (m : znum) : n ≤ n + m * m :=
begin
  znum.transfer_rw,
  exact le_add_of_nonneg_right (mul_self_nonneg _)
end
```
-/
unsafe def transfer_rw : tactic Unit :=
  sorry
#align znum.transfer_rw znum.transfer_rw

/- ./././Mathport/Syntax/Translate/Expr.lean:330:4: warning: unsupported (TODO): `[tacs] -/
/--
This tactic tries to prove (in)equalities about `znum`s by transfering them to the `int` world and
then trying to call `simp`.
```lean
example (n : znum) (m : znum) : n ≤ n + m * m :=
begin
  znum.transfer,
  exact mul_self_nonneg _
end
```
-/
unsafe def transfer : tactic Unit :=
  sorry
#align znum.transfer znum.transfer

/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:69:18: unsupported non-interactive tactic znum.transfer_rw -/
/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:69:18: unsupported non-interactive tactic znum.transfer -/
/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:69:18: unsupported non-interactive tactic znum.transfer_rw -/
/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:69:18: unsupported non-interactive tactic znum.transfer_rw -/
/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:69:18: unsupported non-interactive tactic znum.transfer_rw -/
instance : LinearOrder ZNum where
  lt := (· < ·)
  lt_iff_le_not_le := by
    intro a b
    run_tac
      transfer_rw
    apply lt_iff_le_not_le
  le := (· ≤ ·)
  le_refl := by
    run_tac
      transfer
  le_trans := by
    intro a b c
    run_tac
      transfer_rw
    apply le_trans
  le_antisymm := by
    intro a b
    run_tac
      transfer_rw
    apply le_antisymm
  le_total := by
    intro a b
    run_tac
      transfer_rw
    apply le_total
  DecidableEq := ZNum.decidableEq
  decidableLe := ZNum.decidableLE
  decidableLt := ZNum.decidableLT

/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:69:18: unsupported non-interactive tactic znum.transfer -/
/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:69:18: unsupported non-interactive tactic znum.transfer -/
/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:69:18: unsupported non-interactive tactic znum.transfer -/
instance : AddCommGroup ZNum where
  add := (· + ·)
  add_assoc := by
    run_tac
      transfer
  zero := 0
  zero_add := zero_add
  add_zero := add_zero
  add_comm := by
    run_tac
      transfer
  neg := Neg.neg
  add_left_neg := by
    run_tac
      transfer

instance : AddMonoidWithOne ZNum :=
  { ZNum.addCommGroup with
    one := 1
    natCast := fun n => ZNum.ofInt' n
    natCast_zero := show (Num.ofNat' 0).toZNum = 0 by rw [Num.ofNat'_zero] <;> rfl
    natCast_succ := fun n =>
      show (Num.ofNat' (n + 1)).toZNum = (Num.ofNat' n).toZNum + 1 by
        rw [Num.ofNat'_succ, Num.add_one, Num.toZNum_succ, ZNum.add_one] }

/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:69:18: unsupported non-interactive tactic znum.transfer -/
/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:69:18: unsupported non-interactive tactic znum.transfer -/
/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:69:18: unsupported non-interactive tactic znum.transfer -/
/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:69:18: unsupported non-interactive tactic znum.transfer -/
/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:69:18: unsupported non-interactive tactic znum.transfer -/
/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:69:18: unsupported non-interactive tactic znum.transfer -/
/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:69:18: unsupported non-interactive tactic znum.transfer_rw -/
/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:69:18: unsupported non-interactive tactic znum.transfer_rw -/
instance : LinearOrderedCommRing ZNum :=
  { ZNum.linearOrder, ZNum.addCommGroup,
    ZNum.addMonoidWithOne with
    mul := (· * ·)
    mul_assoc := by
      run_tac
        transfer
    one := 1
    one_mul := by
      run_tac
        transfer
    mul_one := by
      run_tac
        transfer
    left_distrib :=
      by
      run_tac
        transfer
      simp [mul_add]
    right_distrib :=
      by
      run_tac
        transfer
      simp [mul_add, mul_comm]
    mul_comm := by
      run_tac
        transfer
    exists_pair_ne := ⟨0, 1, by decide⟩
    add_le_add_left := by
      intro a b h c
      revert h
      run_tac
        transfer_rw
      exact fun h => add_le_add_left h c
    mul_pos := fun a b =>
      show 0 < a → 0 < b → 0 < a * b
        by
        run_tac
          transfer_rw
        apply mul_pos
    zero_le_one := by decide }

@[simp, norm_cast]
theorem cast_sub [Ring α] (m n) : ((m - n : ZNum) : α) = m - n := by simp [sub_eq_neg_add]
#align znum.cast_sub ZNum.cast_sub

@[simp, norm_cast]
theorem neg_of_int : ∀ n, ((-n : ℤ) : ZNum) = -n
  | (n + 1 : ℕ) => rfl
  | 0 => by rw [Int.cast_neg, Int.cast_zero]
  | -[n+1] => (zneg_zneg _).symm
#align znum.neg_of_int ZNum.neg_of_int

@[simp]
theorem ofInt'_eq : ∀ n : ℤ, ZNum.ofInt' n = n
  | (n : ℕ) => rfl
  | -[n+1] => by
    show Num.toZNumNeg (n + 1 : ℕ) = -(n + 1 : ℕ)
    rw [← neg_inj, neg_neg, Nat.cast_succ, Num.add_one, Num.zneg_toZNumNeg, Num.toZNum_succ,
      Nat.cast_succ, ZNum.add_one]
    rfl
#align znum.of_int'_eq ZNum.ofInt'_eq

@[simp]
theorem of_nat_toZNum (n : ℕ) : Num.toZNum n = n :=
  rfl
#align znum.of_nat_to_znum ZNum.of_nat_toZNum

@[simp, norm_cast]
theorem of_to_int (n : ZNum) : ((n : ℤ) : ZNum) = n := by rw [← of_int'_eq, of_to_int']
#align znum.of_to_int ZNum.of_to_int

theorem to_of_int (n : ℤ) : ((n : ZNum) : ℤ) = n :=
  Int.inductionOn' n 0 (by simp) (by simp) (by simp)
#align znum.to_of_int ZNum.to_of_int

@[simp]
theorem of_nat_toZNumNeg (n : ℕ) : Num.toZNumNeg n = -n := by rw [← of_nat_to_znum, Num.zneg_toZNum]
#align znum.of_nat_to_znum_neg ZNum.of_nat_toZNumNeg

@[simp, norm_cast]
theorem of_int_cast [AddGroupWithOne α] (n : ℤ) : ((n : ZNum) : α) = n := by
  rw [← cast_to_int, to_of_int]
#align znum.of_int_cast ZNum.of_int_cast

@[simp, norm_cast]
theorem of_nat_cast [AddGroupWithOne α] (n : ℕ) : ((n : ZNum) : α) = n := by
  rw [← Int.cast_ofNat, of_int_cast, Int.cast_ofNat]
#align znum.of_nat_cast ZNum.of_nat_cast

@[simp, norm_cast]
theorem dvd_to_int (m n : ZNum) : (m : ℤ) ∣ n ↔ m ∣ n :=
  ⟨fun ⟨k, e⟩ => ⟨k, by rw [← of_to_int n, e] <;> simp⟩, fun ⟨k, e⟩ => ⟨k, by simp [e]⟩⟩
#align znum.dvd_to_int ZNum.dvd_to_int

end ZNum

namespace PosNum

theorem divmod_to_nat_aux {n d : PosNum} {q r : Num} (h₁ : (r : ℕ) + d * bit0 q = n)
    (h₂ : (r : ℕ) < 2 * d) :
    ((divModAux d q r).2 + d * (divModAux d q r).1 : ℕ) = ↑n ∧ ((divModAux d q r).2 : ℕ) < d :=
  by
  unfold divmod_aux
  have : ∀ {r₂}, Num.ofZNum' (Num.sub' r (Num.pos d)) = some r₂ ↔ (r : ℕ) = r₂ + d :=
    by
    intro r₂
    apply num.mem_of_znum'.trans
    rw [← ZNum.to_int_inj, Num.cast_toZNum, Num.cast_sub', sub_eq_iff_eq_add, ← Int.coe_nat_inj']
    simp
  cases' e : Num.ofZNum' (Num.sub' r (Num.pos d)) with r₂ <;> simp [divmod_aux]
  · refine' ⟨h₁, lt_of_not_ge fun 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
    constructor
    · rwa [_root_.bit1, add_comm _ 1, mul_add, mul_one, ← add_assoc, ← this]
    · rwa [this, two_mul, add_lt_add_iff_right] at h₂
#align pos_num.divmod_to_nat_aux PosNum.divmod_to_nat_aux

theorem divMod_to_nat (d n : PosNum) :
    (n / d : ℕ) = (divMod d n).1 ∧ (n % d : ℕ) = (divMod d n).2 :=
  by
  rw [Nat.div_mod_unique (PosNum.cast_pos _)]
  induction' n with n IH n IH
  ·
    exact
      divmod_to_nat_aux (by simp <;> rfl) (Nat.mul_le_mul_left 2 (PosNum.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
#align pos_num.divmod_to_nat PosNum.divMod_to_nat

@[simp]
theorem div'_to_nat (n d) : (div' n d : ℕ) = n / d :=
  (divMod_to_nat _ _).1.symm
#align pos_num.div'_to_nat PosNum.div'_to_nat

@[simp]
theorem mod'_to_nat (n d) : (mod' n d : ℕ) = n % d :=
  (divMod_to_nat _ _).2.symm
#align pos_num.mod'_to_nat PosNum.mod'_to_nat

end PosNum

namespace Num

@[simp]
protected theorem div_zero (n : Num) : n / 0 = 0 :=
  show n.div 0 = 0 by
    cases n
    rfl
    simp [Num.div]
#align num.div_zero Num.div_zero

@[simp, norm_cast]
theorem div_to_nat : ∀ n d, ((n / d : Num) : ℕ) = n / d
  | 0, 0 => by simp
  | 0, Pos d => (Nat.zero_div _).symm
  | Pos n, 0 => (Nat.div_zero _).symm
  | Pos n, Pos d => PosNum.div'_to_nat _ _
#align num.div_to_nat Num.div_to_nat

@[simp]
protected theorem mod_zero (n : Num) : n % 0 = n :=
  show n.mod 0 = n by
    cases n
    rfl
    simp [Num.mod]
#align num.mod_zero Num.mod_zero

@[simp, norm_cast]
theorem mod_to_nat : ∀ n d, ((n % d : Num) : ℕ) = n % d
  | 0, 0 => by simp
  | 0, Pos d => (Nat.zero_mod _).symm
  | Pos n, 0 => (Nat.mod_zero _).symm
  | Pos n, Pos d => PosNum.mod'_to_nat _ _
#align num.mod_to_nat Num.mod_to_nat

theorem gcd_to_nat_aux :
    ∀ {n} {a b : Num}, a ≤ b → (a * b).natSize ≤ n → (gcdAux 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 <| PosNum.natSize_pos _
  | Nat.succ n, 0, b, ab, h => (Nat.gcd_zero_left _).symm
  | Nat.succ n, Pos a, b, ab, h => by
    simp [gcd_aux]
    rw [Nat.gcd_rec, gcd_to_nat_aux, mod_to_nat]; · rfl
    · rw [← le_to_nat, mod_to_nat]
      exact le_of_lt (Nat.mod_lt _ (PosNum.cast_pos _))
    rw [nat_size_to_nat, mul_to_nat, Nat.size_le] at h⊢
    rw [mod_to_nat, mul_comm]
    rw [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 _ (PosNum.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
#align num.gcd_to_nat_aux Num.gcd_to_nat_aux

@[simp]
theorem gcd_to_nat : ∀ a b, (gcd a b : ℕ) = Nat.gcd a b :=
  by
  have : ∀ a b : Num, (a * b).natSize ≤ a.natSize + b.natSize :=
    by
    intros
    simp [nat_size_to_nat]
    rw [Nat.size_le, pow_add]
    exact mul_lt_mul'' (Nat.lt_size_self _) (Nat.lt_size_self _) (Nat.zero_le _) (Nat.zero_le _)
  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 _ _)
#align num.gcd_to_nat Num.gcd_to_nat

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] <;> rfl
#align num.dvd_iff_mod_eq_zero Num.dvd_iff_mod_eq_zero

instance decidableDvd : DecidableRel ((· ∣ ·) : Num → Num → Prop)
  | a, b => decidable_of_iff' _ dvd_iff_mod_eq_zero
#align num.decidable_dvd Num.decidableDvd

end Num

instance PosNum.decidableDvd : DecidableRel ((· ∣ ·) : PosNum → PosNum → Prop)
  | a, b => Num.decidableDvd _ _
#align pos_num.decidable_dvd PosNum.decidableDvd

namespace ZNum

@[simp]
protected theorem div_zero (n : ZNum) : n / 0 = 0 :=
  show n.div 0 = 0 by cases n <;> first |rfl|simp [ZNum.div]
#align znum.div_zero ZNum.div_zero

@[simp, norm_cast]
theorem div_to_int : ∀ n d, ((n / d : ZNum) : ℤ) = n / d
  | 0, 0 => by simp [Int.div_zero]
  | 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_toZNum _).trans <| by rw [← Num.to_nat_to_int] <;> simp
  | Pos n, neg d => (Num.cast_toZNumNeg _).trans <| by rw [← Num.to_nat_to_int] <;> simp
  | neg n, Pos d =>
    show -_ = -_ / ↑d
      by
      rw [n.to_int_eq_succ_pred, d.to_int_eq_succ_pred, ← PosNum.to_nat_to_int, Num.succ'_to_nat,
        Num.div_to_nat]
      change -[n.pred' / ↑d+1] = -[n.pred' / (d.pred' + 1)+1]
      rw [d.to_nat_eq_succ_pred]
  | neg n, neg d =>
    show ↑(PosNum.pred' n / Num.pos d).succ' = -_ / -↑d
      by
      rw [n.to_int_eq_succ_pred, d.to_int_eq_succ_pred, ← PosNum.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]
#align znum.div_to_int ZNum.div_to_int

@[simp, norm_cast]
theorem mod_to_int : ∀ n d, ((n % d : ZNum) : ℤ) = n % d
  | 0, d => (Int.zero_mod _).symm
  | Pos n, d =>
    (Num.cast_toZNum _).trans <| by
      rw [← Num.to_nat_to_int, cast_pos, Num.mod_to_nat, ← PosNum.to_nat_to_int, abs_to_nat] <;> rfl
  | 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.subNatNat_eq_coe, n.to_int_eq_succ_pred] <;>
        rfl
#align znum.mod_to_int ZNum.mod_to_int

@[simp]
theorem gcd_to_nat (a b) : (gcd a b : ℕ) = Int.gcd a b :=
  (Num.gcd_to_nat _ _).trans <| by simpa
#align znum.gcd_to_nat ZNum.gcd_to_nat

theorem dvd_iff_mod_eq_zero {m n : ZNum} : m ∣ n ↔ n % m = 0 := by
  rw [← dvd_to_int, Int.dvd_iff_emod_eq_zero, ← to_int_inj, mod_to_int] <;> rfl
#align znum.dvd_iff_mod_eq_zero ZNum.dvd_iff_mod_eq_zero

instance : DecidableRel ((· ∣ ·) : ZNum → ZNum → Prop)
  | a, b => decidable_of_iff' _ dvd_iff_mod_eq_zero

end ZNum

namespace Int

/-- Cast a `snum` to the corresponding integer. -/
def ofSnum : SNum → ℤ :=
  SNum.rec' (fun a => cond a (-1) 0) fun a p IH => cond a (bit1 IH) (bit0 IH)
#align int.of_snum Int.ofSnum

instance snumCoe : Coe SNum ℤ :=
  ⟨ofSnum⟩
#align int.snum_coe Int.snumCoe

end Int

instance : LT SNum :=
  ⟨fun a b => (a : ℤ) < b⟩

instance : LE SNum :=
  ⟨fun a b => (a : ℤ) ≤ b⟩


import tactic.polya.field

open polya.field tactic tactic.polya.field

def i : num := 0
def j : num := 1
def t1 : nterm ℚ := i * (1 / 2 : ℚ) + i * (1 / 2 : ℚ) - i * (1 : ℚ)
def t2 : nterm ℚ := i * 1 - i

--def t : nterm ℚ := ( i * (1 / 2) + i * (1 / 2) ) * i⁻¹
def t : nterm ℚ := i * i⁻¹
run_cmd ( trace t.norm )

theorem slow : t1.norm = 0 := rfl
theorem fast : t2.norm = 0 := rfl

meta def test_on (e : expr) : tactic unit :=
do
  (t, s) ← (term_of_expr e).run ∅,
  let nt := norm γ t,
  trace nt,
  nterm_to_expr s nt >>= trace

constants x y z : ℚ

run_cmd test_on `(x - y + z)

--theorem test : x * (1 / 10) + x * (1 / 10) - x * (1 / 5) = 0 :=
--by field1

example : x * (1 / 4) + x * (1 / 4) = x * (1 / 2):=
by field1

example (h : x = y) : x * (1 / 4) + x * (1 / 4) = y * (1 / 2) :=
begin
  field1,
  rw h,
end

run_cmd ( do
  e ← to_expr ``( (x * (1 / 2) + x * (1 / 2)) * x ⁻¹ ),
  (new_e, pr, mv, l, s) ← norm_expr e ∅,
  trace new_e
)

example : true :=
by do
  e ← to_expr ``(x * x⁻¹),
  --this should create no new goals
  (new_e, pr, mv, mvs, s) ← norm_expr e ∅,
  exact `(trivial)


import topology.basic
import topology.instances.basic
import topology.properties
import misc.set

namespace topology

universe u

open set

theorem finite_union_of_compact_sets_compact {X : Type u} [topology X]
  : ∀ {l : list (set X)}, is_cover (list_to_set l) → (∀ {S}, S ∈ l → compact_set S) → compact_space X := sorry

end topology


import .lovelib

/- # LoVe Homework 7: Metaprogramming

Homework must be done individually. -/

set_option pp.beta true
set_option pp.generalized_field_notation false

namespace LoVe

/- ## Question 1 (9 points): A `mindless` Tactic

We develop a tactic that automates some of the mindless `intro`/`apply`
proofs that formed the core of lecture 2.

We proceed in three steps.

1.1 (6 points). Develop a `mindless_safe` tactic that applies the
introduction rules for `true`, `∧`, and `↔`, that applies `tactic.intro` (or
`tactic.intro1`) for `→`/`∀`, and that applies `tactic.assumption`, repeatedly
on all goals. The tactic generalizes `intro_ands` from the exercise.

Hint: You can use the `<|>` operator between the rules/tactics used for
different symbols. -/

meta def mindless_safe : tactic unit :=
sorry

lemma abcd (a b c d : Prop) (hc : c) :
  a → ¬ b ∧ (c ↔ d) :=
begin
  mindless_safe,
  /- The proof state should be roughly as follows:

  a b c d : Prop,
  hc : c,
  a_1 : a,
  a_2 : b
  ⊢ false

  a b c d : Prop,
  hc : c,
  a_1 : a,
  a_2 : c
  ⊢ d -/
  repeat { sorry }
end

/- 1.2 (3 points). Develop a `mindless_unsafe` tactic that works on the current
goal. It first tries to apply each hypothesis in turn using `tactic.apply`, then
invokes some `continue` tactic, which is passed as argument, and finally checks
that this succeeded by invoking `tactic.done` (which succeeds only if there are
no goals left). -/

meta def mindless_unsafe (continue : tactic unit) : tactic unit :=
sorry

lemma modus_ponens (a b : Prop) (ha : a) (hab : a → b) :
  b :=
by mindless_unsafe tactic.assumption

/- Finally, everything comes together with the `mindless` tactic below. The
tactic performs a depth-bounded search for a proof, applying `mindless_safe`
and `mindless_unsafe` on all goals in alternation. The bound is necessary to
eventually backtrack. -/

meta def mindless : ℕ → tactic unit
| 0           := mindless_safe
| (depth + 1) :=
  do
    mindless_safe,
    tactic.all_goals (tactic.try (mindless_unsafe (mindless depth))),
    pure ()

/- Some tests are provided below. All should succeed. -/

lemma I (a : Prop) :
  a → a :=
by mindless 0

lemma K (a b : Prop) :
  a → b → b :=
by mindless 0

lemma C (a b c : Prop) :
  (a → b → c) → b → a → c :=
by mindless 1

lemma proj_1st (a : Prop) :
  a → a → a :=
by mindless 0

lemma some_nonsense (a b c : Prop) :
  (a → b → c) → a → (a → c) → b → c :=
by mindless 1

lemma contrapositive (a b : Prop) :
  (a → b) → ¬ b → ¬ a :=
by mindless 2

lemma B (a b c : Prop) :
  (a → b) → (c → a) → c → b :=
by mindless 2

lemma S (a b c : Prop) :
  (a → b → c) → (a → b) → a → c :=
by mindless 2

lemma more_nonsense (a b c : Prop) :
  (c → (a → b) → a) → c → b → a :=
by mindless 1

lemma even_more_nonsense (a b c : Prop) :
  (a → a → b) → (b → c) → a → b → c :=
by mindless 1

lemma weak_peirce (a b : Prop) :
  ((((a → b) → a) → a) → b) → b :=
by mindless 3

lemma one_for_the_road (a c : Prop) (ha : a) (hccc : c → c → c) (haac : a → c) :
  c :=
by mindless 1

/- ## Question 2 (2 bonus points): An `auto` Tactic

2.1 (1 bonus point). Develop an Isabelle-style `auto` tactic.

This tactic would apply all harmless introduction and elimination rules. In
addition, it would try potentially harmful rules (such as `or.intro_left` and
`false.elim`) but backtrack at some point (or try several possibilities in
parallel). Iterative deepening may be a valid approach, or best-first search, or
breadth-first search. The tactic should also attempt to apply assumptions whose
conclusion matches the goal, but backtrack if necessary.

See also "Automatic Proof and Disproof in Isabelle/HOL"
(https://www.cs.vu.nl/~jbe248/frocos2011-dis-proof.pdf) by Blanchette, Bulwahn,
and Nipkow, and the references they give.

2.2 (1 bonus point). Test your tactic on some benchmarks.

You can try your tactic on logic puzzles of the kinds we proved in exercise 2
and homework 2. Please include these below. -/

end LoVe