/-
Copyright (c) 2018 Mario Carneiro. 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.list.dedup
! leanprover-community/mathlib commit f694c7dead66f5d4c80f446c796a5aad14707f0e
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.List.Nodup

/-!
# Erasure of duplicates in a list

> THIS FILE IS SYNCHRONIZED WITH MATHLIB4.
> Any changes to this file require a corresponding PR to mathlib4.

This file proves basic results about `list.dedup` (definition in `data.list.defs`).
`dedup l` returns `l` without its duplicates. It keeps the earliest (that is, rightmost)
occurrence of each.

## Tags

duplicate, multiplicity, nodup, `nub`
-/

universe u

namespace List

variable {α : Type u} [DecidableEq α]

#print List.dedup_nil /-
@[simp]
theorem dedup_nil : dedup [] = ([] : List α) :=
  rfl
#align list.dedup_nil List.dedup_nil
-/

#print List.dedup_cons_of_mem' /-
theorem dedup_cons_of_mem' {a : α} {l : List α} (h : a ∈ dedup l) : dedup (a :: l) = dedup l :=
  pwFilter_cons_of_neg <| by simpa only [forall_mem_ne] using h
#align list.dedup_cons_of_mem' List.dedup_cons_of_mem'
-/

#print List.dedup_cons_of_not_mem' /-
theorem dedup_cons_of_not_mem' {a : α} {l : List α} (h : a ∉ dedup l) :
    dedup (a :: l) = a :: dedup l :=
  pwFilter_cons_of_pos <| by simpa only [forall_mem_ne] using h
#align list.dedup_cons_of_not_mem' List.dedup_cons_of_not_mem'
-/

#print List.mem_dedup /-
@[simp]
theorem mem_dedup {a : α} {l : List α} : a ∈ dedup l ↔ a ∈ l := by
  simpa only [dedup, forall_mem_ne, Classical.not_not] using
    not_congr
      (@forall_mem_pw_filter α (· ≠ ·) _
        (fun x y z xz => not_and_or.1 <| mt (fun h => Eq.trans h.1 h.2) xz) a l)
#align list.mem_dedup List.mem_dedup
-/

#print List.dedup_cons_of_mem /-
@[simp]
theorem dedup_cons_of_mem {a : α} {l : List α} (h : a ∈ l) : dedup (a :: l) = dedup l :=
  dedup_cons_of_mem' <| mem_dedup.2 h
#align list.dedup_cons_of_mem List.dedup_cons_of_mem
-/

#print List.dedup_cons_of_not_mem /-
@[simp]
theorem dedup_cons_of_not_mem {a : α} {l : List α} (h : a ∉ l) : dedup (a :: l) = a :: dedup l :=
  dedup_cons_of_not_mem' <| mt mem_dedup.1 h
#align list.dedup_cons_of_not_mem List.dedup_cons_of_not_mem
-/

#print List.dedup_sublist /-
theorem dedup_sublist : ∀ l : List α, dedup l <+ l :=
  pwFilter_sublist
#align list.dedup_sublist List.dedup_sublist
-/

#print List.dedup_subset /-
theorem dedup_subset : ∀ l : List α, dedup l ⊆ l :=
  pwFilter_subset
#align list.dedup_subset List.dedup_subset
-/

#print List.subset_dedup /-
theorem subset_dedup (l : List α) : l ⊆ dedup l := fun a => mem_dedup.2
#align list.subset_dedup List.subset_dedup
-/

#print List.nodup_dedup /-
theorem nodup_dedup : ∀ l : List α, Nodup (dedup l) :=
  pairwise_pwFilter
#align list.nodup_dedup List.nodup_dedup
-/

#print List.dedup_eq_self /-
theorem dedup_eq_self {l : List α} : dedup l = l ↔ Nodup l :=
  pwFilter_eq_self
#align list.dedup_eq_self List.dedup_eq_self
-/

#print List.Nodup.dedup /-
protected theorem Nodup.dedup {l : List α} (h : l.Nodup) : l.dedup = l :=
  List.dedup_eq_self.2 h
#align list.nodup.dedup List.Nodup.dedup
-/

#print List.dedup_idempotent /-
@[simp]
theorem dedup_idempotent {l : List α} : dedup (dedup l) = dedup l :=
  pwFilter_idempotent
#align list.dedup_idempotent List.dedup_idempotent
-/

#print List.dedup_append /-
theorem dedup_append (l₁ l₂ : List α) : dedup (l₁ ++ l₂) = l₁ ∪ dedup l₂ :=
  by
  induction' l₁ with a l₁ IH; · rfl; rw [cons_union, ← IH]
  show dedup (a :: (l₁ ++ l₂)) = insert a (dedup (l₁ ++ l₂))
  by_cases a ∈ dedup (l₁ ++ l₂) <;> [rw [dedup_cons_of_mem' h, insert_of_mem h],
    rw [dedup_cons_of_not_mem' h, insert_of_not_mem h]]
#align list.dedup_append List.dedup_append
-/

#print List.replicate_dedup /-
theorem replicate_dedup {x : α} : ∀ {k}, k ≠ 0 → (replicate k x).dedup = [x]
  | 0, h => (h rfl).elim
  | 1, _ => rfl
  | n + 2, _ => by
    rw [replicate_succ, dedup_cons_of_mem (mem_replicate.2 ⟨n.succ_ne_zero, rfl⟩),
      replicate_dedup n.succ_ne_zero]
#align list.replicate_dedup List.replicate_dedup
-/

#print List.count_dedup /-
theorem count_dedup (l : List α) (a : α) : l.dedup.count a = if a ∈ l then 1 else 0 := by
  simp_rw [count_eq_of_nodup <| nodup_dedup l, mem_dedup]
#align list.count_dedup List.count_dedup
-/

/- warning: list.sum_map_count_dedup_filter_eq_countp -> List.sum_map_count_dedup_filter_eq_countp is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u1} α] (p : α -> Prop) [_inst_2 : DecidablePred.{succ u1} α p] (l : List.{u1} α), Eq.{1} Nat (List.sum.{0} Nat Nat.hasAdd Nat.hasZero (List.map.{u1, 0} α Nat (fun (x : α) => List.count.{u1} α (fun (a : α) (b : α) => _inst_1 a b) x l) (List.filterₓ.{u1} α p (fun (a : α) => _inst_2 a) (List.dedup.{u1} α (fun (a : α) (b : α) => _inst_1 a b) l)))) (List.countp.{u1} α p (fun (a : α) => _inst_2 a) l)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u1} α] (p : α -> Bool) (_inst_2 : List.{u1} α), Eq.{1} Nat (List.sum.{0} Nat instAddNat (LinearOrderedCommMonoidWithZero.toZero.{0} Nat Nat.linearOrderedCommMonoidWithZero) (List.map.{u1, 0} α Nat (fun (x : α) => List.count.{u1} α (instBEq.{u1} α (fun (a : α) (b : α) => _inst_1 a b)) x _inst_2) (List.filter.{u1} α p (List.dedup.{u1} α (fun (a : α) (b : α) => _inst_1 a b) _inst_2)))) (List.countp.{u1} α p _inst_2)
Case conversion may be inaccurate. Consider using '#align list.sum_map_count_dedup_filter_eq_countp List.sum_map_count_dedup_filter_eq_countpₓ'. -/
/-- Summing the count of `x` over a list filtered by some `p` is just `countp` applied to `p` -/
theorem sum_map_count_dedup_filter_eq_countp (p : α → Prop) [DecidablePred p] (l : List α) :
    ((l.dedup.filterₓ p).map fun x => l.count x).Sum = l.countp p :=
  by
  induction' l with a as h
  · simp
  · simp_rw [List.countp_cons, List.count_cons', List.sum_map_add]
    congr 1
    · refine' trans _ h
      by_cases ha : a ∈ as
      · simp [dedup_cons_of_mem ha]
      · simp only [dedup_cons_of_not_mem ha, List.filter]
        split_ifs with hp <;> simp [List.map_cons, List.sum_cons, List.count_eq_zero.2 ha, zero_add]
    · by_cases hp : p a
      · refine' trans (sum_map_eq_nsmul_single a _ fun _ h _ => by simp [h]) _
        simp [hp, count_dedup]
      · refine' trans (List.sum_eq_zero fun n hn => _) (by simp [hp])
        obtain ⟨a', ha'⟩ := List.mem_map.1 hn
        simp only [(fun h => hp (h ▸ (List.mem_filter.1 ha'.1).2) : a' ≠ a), if_false] at ha'
        exact ha'.2.symm
#align list.sum_map_count_dedup_filter_eq_countp List.sum_map_count_dedup_filter_eq_countp

#print List.sum_map_count_dedup_eq_length /-
theorem sum_map_count_dedup_eq_length (l : List α) :
    (l.dedup.map fun x => l.count x).Sum = l.length := by
  simpa using sum_map_count_dedup_filter_eq_countp (fun _ => True) l
#align list.sum_map_count_dedup_eq_length List.sum_map_count_dedup_eq_length
-/

end List


import Lib.Algebra.Monoid
import Lib.Data.MonoFoldable
import Lib.Data.MonoFunctor
import Lib.Data.Traversable
import Lib.Function

class MonoTraversable (T : Type u) (α : outParam (Type u))
extends MonoFunctor T α, MonoFoldable T α where
  traverse {F : Type u → Type u} [Applicative F] (f : α → F α) : T → F T
  mapM {F : Type u → Type u} [Monad F] (f : α → F α) : T → F T

instance {T} [Traversable T] : MonoTraversable (T α) α where
  traverse := Traversable.traverse
  mapM := Traversable.mapM

namespace MonoTraversable

open MonoTraversable
-- open Traversable (StateT.mk)
-- def StateT.mk {σ α} (f : σ → m (α × σ)) : StateT σ m α := f

section Accum

variable {T : Type u} [MonoTraversable T α]
variable (f : α → σ → α × σ)

def accuml (x₀ : σ) (x : T) : T × σ :=
StateT.run (m := Id) (mapM (StateT.mk.{u,u} ∘ f) x) x₀

def scanl (x₀ : σ) (x : T) : T :=
StateT.run' (m := Id) (mapM (StateT.mk.{u,u} ∘ f) x) x₀

def accumr (x₀ : σ) (x : T) : T × σ :=
StateT.run (m := Id) (Op1.run (traverse (Op1.mk.{u,u} ∘ StateT.mk.{u,u} ∘ f) x)) x₀

def scanr (x₀ : σ) (x : T) : T :=
accumr f x₀ x |>.1

end Accum

section AccumIdx

variable {T : Type u} [MonoTraversable.{u} T α]
variable {β : Type u} (f : Nat → α → σ → α × σ)

def accumlIdx (x₀ : σ) (x : T) : T × σ :=
accuml (λ a (x, i) => f i a x |>.map id (., i+1)) (x₀, 0) x |>.map id Prod.fst

def scanlIdx (x₀ : σ) (x : T) : T :=
accumlIdx f x₀ x |>.fst

end AccumIdx
end MonoTraversable

open MonoTraversable
class LawfulMonoTraversable (T : Type u) (α : Type u)
      [MonoTraversable T α]
extends LawfulMonoFunctor T α, LawfulMonoFoldable T α where
  traverse_eq_mapM {M} [Monad M] [LawfulMonad M] (f : α → M α) (x : T) :
    traverse f x = mapM f x
  map_eq_traverse (f : α → α) (x : T) :
    MonoFunctor.map f x = Id.run (traverse (Id.mk ∘ f) x)
  -- id_traverse {α} (x : T α) : traverse Id.mk x = Id.mk x
  comp_traverse {F G} [Applicative F] [LawfulApplicative F]
    [Applicative G] [LawfulApplicative G]
    (x : T) (f : α → F α) (g : α → G α) :
    Comp.run (traverse (Comp.mk ∘ Functor.map f ∘ g) x) =
    traverse f <$> traverse g x
  foldl_eq_traverse {β} (f : β → α → β) (x : T) (x₀ : β) :
    MonoFoldable.foldl f x₀ x =
    Endo.run (Op.run (Const.run (traverse
      (Const.mk (α := α) ∘ Op.mk ∘ Endo.mk ∘ flip f) x))) x₀
  traverse_sim {F G}
               [Applicative F] [LawfulApplicative F]
               [Applicative G] [LawfulApplicative G]
               (x : T) (R : ApplicativeRel F G)
               (f : α → F α) (g : α → G α) :
      (∀ a, R (f a) (g a)) →
      R (traverse f x) (traverse g x)

instance {T} [Traversable T] [LawfulTraversable T] :
         LawfulMonoTraversable (T α) α where
  traverse_eq_mapM := LawfulTraversable.traverse_eq_mapM
  map_eq_traverse := LawfulTraversable.map_eq_traverse
  comp_traverse := LawfulTraversable.comp_traverse
  foldl_eq_traverse := LawfulTraversable.foldl_eq_traverse
  traverse_sim := LawfulTraversable.traverse_sim

namespace LawfulMonoTraversable
open MonoTraversable

variable {T} [MonoTraversable T α] [LawfulMonoTraversable T α]

theorem id_traverse (x : T) : traverse Id.mk x = Id.mk x := by
have := (map_eq_traverse id x).symm
simp [LawfulMonoFunctor.id_map] at this
assumption

section nat
variable {T : Type u} [MonoTraversable T α] [LawfulMonoTraversable T α]
variable {F : Type u → Type u} [Applicative F] [LawfulApplicative F]
variable {G : Type u → Type u} [Applicative G] [LawfulApplicative G]

theorem traverse_nat (x : T) (f : ApplicativeHom F G)
        (g : α → F α) :
  f (traverse g x) = traverse (f.fn ∘ g) x := by
let R := f.toApplicativeRel
apply LawfulMonoTraversable.traverse_sim _ R; intro a
simp [ApplicativeHom.toApplicativeRel]

end nat

end LawfulMonoTraversable


/-
Copyright (c) 2017 Simon Hudon All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Simon Hudon, Mario Carneiro

! This file was ported from Lean 3 source module tactic.norm_num
! leanprover-community/mathlib commit ec322deb9ba5aad978f862669053069b7957c31d
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Rat.Cast
import Mathbin.Data.Rat.MetaDefs
import Mathbin.Data.Int.Lemmas

/-!
# `norm_num`

Evaluating arithmetic expressions including `*`, `+`, `-`, `^`, `≤`.
-/

universe u v w

namespace Tactic

namespace InstanceCache

/-- Faster version of `mk_app ``bit0 [e]`. -/
unsafe def mk_bit0 (c : instance_cache) (e : expr) : tactic (instance_cache × expr) := do
  let (c, ai) ← c.get `` Add
  return (c, (expr.const `` bit0 [c]).mk_app [c, ai, e])
#align tactic.instance_cache.mk_bit0 tactic.instance_cache.mk_bit0

/-- Faster version of `mk_app ``bit1 [e]`. -/
unsafe def mk_bit1 (c : instance_cache) (e : expr) : tactic (instance_cache × expr) := do
  let (c, ai) ← c.get `` Add
  let (c, oi) ← c.get `` One
  return (c, (expr.const `` bit1 [c]).mk_app [c, oi, ai, e])
#align tactic.instance_cache.mk_bit1 tactic.instance_cache.mk_bit1

end InstanceCache

end Tactic

open Tactic

/-!
Each lemma in this file is written the way it is to exactly match (with no defeq reduction allowed)
the conclusion of some lemma generated by the proof procedure that uses it. That proof procedure
should describe the shape of the generated lemma in its docstring.
-/

namespace NormNum

variable {α : Type u}

theorem subst_into_add {α} [Add α] (l r tl tr t) (prl : (l : α) = tl) (prr : r = tr)
    (prt : tl + tr = t) : l + r = t := by rw [prl, prr, prt]
#align norm_num.subst_into_add NormNum.subst_into_add

theorem subst_into_mul {α} [Mul α] (l r tl tr t) (prl : (l : α) = tl) (prr : r = tr)
    (prt : tl * tr = t) : l * r = t := by rw [prl, prr, prt]
#align norm_num.subst_into_mul NormNum.subst_into_mul

theorem subst_into_neg {α} [Neg α] (a ta t : α) (pra : a = ta) (prt : -ta = t) : -a = t := by
  simp [pra, prt]
#align norm_num.subst_into_neg NormNum.subst_into_neg

/-- The result type of `match_numeral`, either `0`, `1`, or a top level
decomposition of `bit0 e` or `bit1 e`. The `other` case means it is not a numeral. -/
unsafe inductive match_numeral_result
  | zero
  | one
  | bit0 (e : expr)
  | bit1 (e : expr)
  | other
#align norm_num.match_numeral_result norm_num.match_numeral_result

/-- Unfold the top level constructor of the numeral expression. -/
unsafe def match_numeral : expr → match_numeral_result
  | q(bit0 $(e)) => match_numeral_result.bit0 e
  | q(bit1 $(e)) => match_numeral_result.bit1 e
  | q(@Zero.zero _ _) => match_numeral_result.zero
  | q(@One.one _ _) => match_numeral_result.one
  | _ => match_numeral_result.other
#align norm_num.match_numeral norm_num.match_numeral

theorem zero_succ {α} [Semiring α] : (0 + 1 : α) = 1 :=
  zero_add _
#align norm_num.zero_succ NormNum.zero_succ

theorem one_succ {α} [Semiring α] : (1 + 1 : α) = 2 :=
  rfl
#align norm_num.one_succ NormNum.one_succ

theorem bit0_succ {α} [Semiring α] (a : α) : bit0 a + 1 = bit1 a :=
  rfl
#align norm_num.bit0_succ NormNum.bit0_succ

theorem bit1_succ {α} [Semiring α] (a b : α) (h : a + 1 = b) : bit1 a + 1 = bit0 b :=
  h ▸ by simp [bit1, bit0, add_left_comm, add_assoc]
#align norm_num.bit1_succ NormNum.bit1_succ

section

open MatchNumeralResult

/-- Given `a`, `b` natural numerals, proves `⊢ a + 1 = b`, assuming that this is provable.
(It may prove garbage instead of failing if `a + 1 = b` is false.) -/
unsafe def prove_succ : instance_cache → expr → expr → tactic (instance_cache × expr)
  | c, e, r =>
    match match_numeral e with
    | zero => c.mk_app `` zero_succ []
    | one => c.mk_app `` one_succ []
    | bit0 e => c.mk_app `` bit0_succ [e]
    | bit1 e => do
      let r := r.app_arg
      let (c, p) ← prove_succ c e r
      c `` bit1_succ [e, r, p]
    | _ => failed
#align norm_num.prove_succ norm_num.prove_succ

end

/-- Given `a` natural numeral, returns `(b, ⊢ a + 1 = b)`. -/
unsafe def prove_succ' (c : instance_cache) (a : expr) : tactic (instance_cache × expr × expr) := do
  let na ← a.toNat
  let (c, b) ← c.ofNat (na + 1)
  let (c, p) ← prove_succ c a b
  return (c, b, p)
#align norm_num.prove_succ' norm_num.prove_succ'

theorem zero_adc {α} [Semiring α] (a b : α) (h : a + 1 = b) : 0 + a + 1 = b := by rwa [zero_add]
#align norm_num.zero_adc NormNum.zero_adc

theorem adc_zero {α} [Semiring α] (a b : α) (h : a + 1 = b) : a + 0 + 1 = b := by rwa [add_zero]
#align norm_num.adc_zero NormNum.adc_zero

theorem one_add {α} [Semiring α] (a b : α) (h : a + 1 = b) : 1 + a = b := by rwa [add_comm]
#align norm_num.one_add NormNum.one_add

theorem add_bit0_bit0 {α} [Semiring α] (a b c : α) (h : a + b = c) : bit0 a + bit0 b = bit0 c :=
  h ▸ by simp [bit0, add_left_comm, add_assoc]
#align norm_num.add_bit0_bit0 NormNum.add_bit0_bit0

theorem add_bit0_bit1 {α} [Semiring α] (a b c : α) (h : a + b = c) : bit0 a + bit1 b = bit1 c :=
  h ▸ by simp [bit0, bit1, add_left_comm, add_assoc]
#align norm_num.add_bit0_bit1 NormNum.add_bit0_bit1

theorem add_bit1_bit0 {α} [Semiring α] (a b c : α) (h : a + b = c) : bit1 a + bit0 b = bit1 c :=
  h ▸ by simp [bit0, bit1, add_left_comm, add_comm, add_assoc]
#align norm_num.add_bit1_bit0 NormNum.add_bit1_bit0

theorem add_bit1_bit1 {α} [Semiring α] (a b c : α) (h : a + b + 1 = c) : bit1 a + bit1 b = bit0 c :=
  h ▸ by simp [bit0, bit1, add_left_comm, add_comm, add_assoc]
#align norm_num.add_bit1_bit1 NormNum.add_bit1_bit1

theorem adc_one_one {α} [Semiring α] : (1 + 1 + 1 : α) = 3 :=
  rfl
#align norm_num.adc_one_one NormNum.adc_one_one

theorem adc_bit0_one {α} [Semiring α] (a b : α) (h : a + 1 = b) : bit0 a + 1 + 1 = bit0 b :=
  h ▸ by simp [bit0, add_left_comm, add_assoc]
#align norm_num.adc_bit0_one NormNum.adc_bit0_one

theorem adc_one_bit0 {α} [Semiring α] (a b : α) (h : a + 1 = b) : 1 + bit0 a + 1 = bit0 b :=
  h ▸ by simp [bit0, add_left_comm, add_assoc]
#align norm_num.adc_one_bit0 NormNum.adc_one_bit0

theorem adc_bit1_one {α} [Semiring α] (a b : α) (h : a + 1 = b) : bit1 a + 1 + 1 = bit1 b :=
  h ▸ by simp [bit1, bit0, add_left_comm, add_assoc]
#align norm_num.adc_bit1_one NormNum.adc_bit1_one

theorem adc_one_bit1 {α} [Semiring α] (a b : α) (h : a + 1 = b) : 1 + bit1 a + 1 = bit1 b :=
  h ▸ by simp [bit1, bit0, add_left_comm, add_assoc]
#align norm_num.adc_one_bit1 NormNum.adc_one_bit1

theorem adc_bit0_bit0 {α} [Semiring α] (a b c : α) (h : a + b = c) : bit0 a + bit0 b + 1 = bit1 c :=
  h ▸ by simp [bit1, bit0, add_left_comm, add_assoc]
#align norm_num.adc_bit0_bit0 NormNum.adc_bit0_bit0

theorem adc_bit1_bit0 {α} [Semiring α] (a b c : α) (h : a + b + 1 = c) :
    bit1 a + bit0 b + 1 = bit0 c :=
  h ▸ by simp [bit1, bit0, add_left_comm, add_assoc]
#align norm_num.adc_bit1_bit0 NormNum.adc_bit1_bit0

theorem adc_bit0_bit1 {α} [Semiring α] (a b c : α) (h : a + b + 1 = c) :
    bit0 a + bit1 b + 1 = bit0 c :=
  h ▸ by simp [bit1, bit0, add_left_comm, add_assoc]
#align norm_num.adc_bit0_bit1 NormNum.adc_bit0_bit1

theorem adc_bit1_bit1 {α} [Semiring α] (a b c : α) (h : a + b + 1 = c) :
    bit1 a + bit1 b + 1 = bit1 c :=
  h ▸ by simp [bit1, bit0, add_left_comm, add_assoc]
#align norm_num.adc_bit1_bit1 NormNum.adc_bit1_bit1

section

open MatchNumeralResult

mutual
  unsafe def prove_add_nat : instance_cache → expr → expr → expr → tactic (instance_cache × expr)
    | c, a, b, r => do
      match match_numeral a, match_numeral b with
        | zero, _ => c `` zero_add [b]
        | _, zero => c `` add_zero [a]
        | _, one => prove_succ c a r
        | one, _ => do
          let (c, p) ← prove_succ c b r
          c `` one_add [b, r, p]
        | bit0 a, bit0 b => do
          let r := r
          let (c, p) ← prove_add_nat c a b r
          c `` add_bit0_bit0 [a, b, r, p]
        | bit0 a, bit1 b => do
          let r := r
          let (c, p) ← prove_add_nat c a b r
          c `` add_bit0_bit1 [a, b, r, p]
        | bit1 a, bit0 b => do
          let r := r
          let (c, p) ← prove_add_nat c a b r
          c `` add_bit1_bit0 [a, b, r, p]
        | bit1 a, bit1 b => do
          let r := r
          let (c, p) ← prove_adc_nat c a b r
          c `` add_bit1_bit1 [a, b, r, p]
        | _, _ => failed
  unsafe def prove_adc_nat : instance_cache → expr → expr → expr → tactic (instance_cache × expr)
    | c, a, b, r => do
      match match_numeral a, match_numeral b with
        | zero, _ => do
          let (c, p) ← prove_succ c b r
          c `` zero_adc [b, r, p]
        | _, zero => do
          let (c, p) ← prove_succ c b r
          c `` adc_zero [b, r, p]
        | one, one => c `` adc_one_one []
        | bit0 a, one => do
          let r := r
          let (c, p) ← prove_succ c a r
          c `` adc_bit0_one [a, r, p]
        | one, bit0 b => do
          let r := r
          let (c, p) ← prove_succ c b r
          c `` adc_one_bit0 [b, r, p]
        | bit1 a, one => do
          let r := r
          let (c, p) ← prove_succ c a r
          c `` adc_bit1_one [a, r, p]
        | one, bit1 b => do
          let r := r
          let (c, p) ← prove_succ c b r
          c `` adc_one_bit1 [b, r, p]
        | bit0 a, bit0 b => do
          let r := r
          let (c, p) ← prove_add_nat c a b r
          c `` adc_bit0_bit0 [a, b, r, p]
        | bit0 a, bit1 b => do
          let r := r
          let (c, p) ← prove_adc_nat c a b r
          c `` adc_bit0_bit1 [a, b, r, p]
        | bit1 a, bit0 b => do
          let r := r
          let (c, p) ← prove_adc_nat c a b r
          c `` adc_bit1_bit0 [a, b, r, p]
        | bit1 a, bit1 b => do
          let r := r
          let (c, p) ← prove_adc_nat c a b r
          c `` adc_bit1_bit1 [a, b, r, p]
        | _, _ => failed
end
#align norm_num.prove_add_nat norm_num.prove_add_nat
#align norm_num.prove_adc_nat norm_num.prove_adc_nat

/-- Given `a`,`b`,`r` natural numerals, proves `⊢ a + b = r`. -/
add_decl_doc prove_add_nat

/-- Given `a`,`b`,`r` natural numerals, proves `⊢ a + b + 1 = r`. -/
add_decl_doc prove_adc_nat

/-- Given `a`,`b` natural numerals, returns `(r, ⊢ a + b = r)`. -/
unsafe def prove_add_nat' (c : instance_cache) (a b : expr) :
    tactic (instance_cache × expr × expr) := do
  let na ← a.toNat
  let nb ← b.toNat
  let (c, r) ← c.ofNat (na + nb)
  let (c, p) ← prove_add_nat c a b r
  return (c, r, p)
#align norm_num.prove_add_nat' norm_num.prove_add_nat'

end

theorem bit0_mul {α} [Semiring α] (a b c : α) (h : a * b = c) : bit0 a * b = bit0 c :=
  h ▸ by simp [bit0, add_mul]
#align norm_num.bit0_mul NormNum.bit0_mul

theorem mul_bit0' {α} [Semiring α] (a b c : α) (h : a * b = c) : a * bit0 b = bit0 c :=
  h ▸ by simp [bit0, mul_add]
#align norm_num.mul_bit0' NormNum.mul_bit0'

theorem mul_bit0_bit0 {α} [Semiring α] (a b c : α) (h : a * b = c) :
    bit0 a * bit0 b = bit0 (bit0 c) :=
  bit0_mul _ _ _ (mul_bit0' _ _ _ h)
#align norm_num.mul_bit0_bit0 NormNum.mul_bit0_bit0

theorem mul_bit1_bit1 {α} [Semiring α] (a b c d e : α) (hc : a * b = c) (hd : a + b = d)
    (he : bit0 c + d = e) : bit1 a * bit1 b = bit1 e := by
  rw [← he, ← hd, ← hc] <;> simp [bit1, bit0, mul_add, add_mul, add_left_comm, add_assoc]
#align norm_num.mul_bit1_bit1 NormNum.mul_bit1_bit1

section

open MatchNumeralResult

/-- Given `a`,`b` natural numerals, returns `(r, ⊢ a * b = r)`. -/
unsafe def prove_mul_nat : instance_cache → expr → expr → tactic (instance_cache × expr × expr)
  | ic, a, b =>
    match match_numeral a, match_numeral b with
    | zero, _ => do
      let (ic, z) ← ic.mk_app `` Zero.zero []
      let (ic, p) ← ic.mk_app `` MulZeroClass.zero_mul [b]
      return (ic, z, p)
    | _, zero => do
      let (ic, z) ← ic.mk_app `` Zero.zero []
      let (ic, p) ← ic.mk_app `` MulZeroClass.mul_zero [a]
      return (ic, z, p)
    | one, _ => do
      let (ic, p) ← ic.mk_app `` one_mul [b]
      return (ic, b, p)
    | _, one => do
      let (ic, p) ← ic.mk_app `` mul_one [a]
      return (ic, a, p)
    | bit0 a, bit0 b => do
      let (ic, c, p) ← prove_mul_nat ic a b
      let (ic, p) ← ic.mk_app `` mul_bit0_bit0 [a, b, c, p]
      let (ic, c') ← ic.mk_bit0 c
      let (ic, c') ← ic.mk_bit0 c'
      return (ic, c', p)
    | bit0 a, _ => do
      let (ic, c, p) ← prove_mul_nat ic a b
      let (ic, p) ← ic.mk_app `` bit0_mul [a, b, c, p]
      let (ic, c') ← ic.mk_bit0 c
      return (ic, c', p)
    | _, bit0 b => do
      let (ic, c, p) ← prove_mul_nat ic a b
      let (ic, p) ← ic.mk_app `` mul_bit0' [a, b, c, p]
      let (ic, c') ← ic.mk_bit0 c
      return (ic, c', p)
    | bit1 a, bit1 b => do
      let (ic, c, pc) ← prove_mul_nat ic a b
      let (ic, d, pd) ← prove_add_nat' ic a b
      let (ic, c') ← ic.mk_bit0 c
      let (ic, e, pe) ← prove_add_nat' ic c' d
      let (ic, p) ← ic.mk_app `` mul_bit1_bit1 [a, b, c, d, e, pc, pd, pe]
      let (ic, e') ← ic.mk_bit1 e
      return (ic, e', p)
    | _, _ => failed
#align norm_num.prove_mul_nat norm_num.prove_mul_nat

end

theorem zero_lt_one [LinearOrderedSemiring α] : (0 : α) < 1 :=
  zero_lt_one
#align norm_num.zero_lt_one NormNum.zero_lt_one

section

open MatchNumeralResult

/-- Given `a` a positive natural numeral, returns `⊢ 0 < a`. -/
unsafe def prove_pos_nat (c : instance_cache) : expr → tactic (instance_cache × expr)
  | e =>
    match match_numeral e with
    | one => c.mk_app `` zero_lt_one []
    | bit0 e => do
      let (c, p) ← prove_pos_nat e
      c `` bit0_pos [e, p]
    | bit1 e => do
      let (c, p) ← prove_pos_nat e
      c `` bit1_pos' [e, p]
    | _ => failed
#align norm_num.prove_pos_nat norm_num.prove_pos_nat

end

-- failed to format: unknown constant 'term.pseudo.antiquot'
/-- Given `a` a rational numeral, returns `⊢ 0 < a`. -/ unsafe
  def
    prove_pos
    ( c : instance_cache ) : expr → tactic ( instance_cache × expr )
    |
        q( $ ( e₁ ) / $ ( e₂ ) )
        =>
        do
          let ( c , p₁ ) ← prove_pos_nat c e₁
            let ( c , p₂ ) ← prove_pos_nat c e₂
            c ` ` div_pos [ e₁ , e₂ , p₁ , p₂ ]
      | e => prove_pos_nat c e
#align norm_num.prove_pos norm_num.prove_pos

/-- `match_neg (- e) = some e`, otherwise `none` -/
unsafe def match_neg : expr → Option expr
  | q(-$(e)) => some e
  | _ => none
#align norm_num.match_neg norm_num.match_neg

/-- `match_sign (- e) = inl e`, `match_sign 0 = inr ff`, otherwise `inr tt` -/
unsafe def match_sign : expr → Sum expr Bool
  | q(-$(e)) => Sum.inl e
  | q(Zero.zero) => Sum.inr false
  | _ => Sum.inr true
#align norm_num.match_sign norm_num.match_sign

theorem ne_zero_of_pos {α} [OrderedAddCommGroup α] (a : α) : 0 < a → a ≠ 0 :=
  ne_of_gt
#align norm_num.ne_zero_of_pos NormNum.ne_zero_of_pos

theorem ne_zero_neg {α} [AddGroup α] (a : α) : a ≠ 0 → -a ≠ 0 :=
  mt neg_eq_zero.1
#align norm_num.ne_zero_neg NormNum.ne_zero_neg

/-- Given `a` a rational numeral, returns `⊢ a ≠ 0`. -/
unsafe def prove_ne_zero' (c : instance_cache) : expr → tactic (instance_cache × expr)
  | a =>
    match match_neg a with
    | some a => do
      let (c, p) ← prove_ne_zero' a
      c `` ne_zero_neg [a, p]
    | none => do
      let (c, p) ← prove_pos c a
      c `` ne_zero_of_pos [a, p]
#align norm_num.prove_ne_zero' norm_num.prove_ne_zero'

theorem clear_denom_div {α} [DivisionRing α] (a b b' c d : α) (h₀ : b ≠ 0) (h₁ : b * b' = d)
    (h₂ : a * b' = c) : a / b * d = c := by rwa [← h₁, ← mul_assoc, div_mul_cancel _ h₀]
#align norm_num.clear_denom_div NormNum.clear_denom_div

/-- Given `a` nonnegative rational and `d` a natural number, returns `(b, ⊢ a * d = b)`.
(`d` should be a multiple of the denominator of `a`, so that `b` is a natural number.) -/
unsafe def prove_clear_denom'
    (prove_ne_zero : instance_cache → expr → ℚ → tactic (instance_cache × expr))
    (c : instance_cache) (a d : expr) (na : ℚ) (nd : ℕ) : tactic (instance_cache × expr × expr) :=
  if na.den = 1 then prove_mul_nat c a d
  else do
    let [_, _, a, b] ← return a.get_app_args
    let (c, b') ← c.ofNat (nd / na.den)
    let (c, p₀) ← prove_ne_zero c b na.den
    let (c, _, p₁) ← prove_mul_nat c b b'
    let (c, r, p₂) ← prove_mul_nat c a b'
    let (c, p) ← c.mk_app `` clear_denom_div [a, b, b', r, d, p₀, p₁, p₂]
    return (c, r, p)
#align norm_num.prove_clear_denom' norm_num.prove_clear_denom'

theorem nonneg_pos {α} [OrderedCancelAddCommMonoid α] (a : α) : 0 < a → 0 ≤ a :=
  le_of_lt
#align norm_num.nonneg_pos NormNum.nonneg_pos

theorem lt_one_bit0 {α} [LinearOrderedSemiring α] (a : α) (h : 1 ≤ a) : 1 < bit0 a :=
  lt_of_lt_of_le one_lt_two (bit0_le_bit0.2 h)
#align norm_num.lt_one_bit0 NormNum.lt_one_bit0

theorem lt_one_bit1 {α} [LinearOrderedSemiring α] (a : α) (h : 0 < a) : 1 < bit1 a :=
  one_lt_bit1.2 h
#align norm_num.lt_one_bit1 NormNum.lt_one_bit1

theorem lt_bit0_bit0 {α} [LinearOrderedSemiring α] (a b : α) : a < b → bit0 a < bit0 b :=
  bit0_lt_bit0.2
#align norm_num.lt_bit0_bit0 NormNum.lt_bit0_bit0

theorem lt_bit0_bit1 {α} [LinearOrderedSemiring α] (a b : α) (h : a ≤ b) : bit0 a < bit1 b :=
  lt_of_le_of_lt (bit0_le_bit0.2 h) (lt_add_one _)
#align norm_num.lt_bit0_bit1 NormNum.lt_bit0_bit1

theorem lt_bit1_bit0 {α} [LinearOrderedSemiring α] (a b : α) (h : a + 1 ≤ b) : bit1 a < bit0 b :=
  lt_of_lt_of_le (by simp [bit0, bit1, zero_lt_one, add_assoc]) (bit0_le_bit0.2 h)
#align norm_num.lt_bit1_bit0 NormNum.lt_bit1_bit0

theorem lt_bit1_bit1 {α} [LinearOrderedSemiring α] (a b : α) : a < b → bit1 a < bit1 b :=
  bit1_lt_bit1.2
#align norm_num.lt_bit1_bit1 NormNum.lt_bit1_bit1

theorem le_one_bit0 {α} [LinearOrderedSemiring α] (a : α) (h : 1 ≤ a) : 1 ≤ bit0 a :=
  le_of_lt (lt_one_bit0 _ h)
#align norm_num.le_one_bit0 NormNum.le_one_bit0

-- deliberately strong hypothesis because bit1 0 is not a numeral
theorem le_one_bit1 {α} [LinearOrderedSemiring α] (a : α) (h : 0 < a) : 1 ≤ bit1 a :=
  le_of_lt (lt_one_bit1 _ h)
#align norm_num.le_one_bit1 NormNum.le_one_bit1

theorem le_bit0_bit0 {α} [LinearOrderedSemiring α] (a b : α) : a ≤ b → bit0 a ≤ bit0 b :=
  bit0_le_bit0.2
#align norm_num.le_bit0_bit0 NormNum.le_bit0_bit0

theorem le_bit0_bit1 {α} [LinearOrderedSemiring α] (a b : α) (h : a ≤ b) : bit0 a ≤ bit1 b :=
  le_of_lt (lt_bit0_bit1 _ _ h)
#align norm_num.le_bit0_bit1 NormNum.le_bit0_bit1

theorem le_bit1_bit0 {α} [LinearOrderedSemiring α] (a b : α) (h : a + 1 ≤ b) : bit1 a ≤ bit0 b :=
  le_of_lt (lt_bit1_bit0 _ _ h)
#align norm_num.le_bit1_bit0 NormNum.le_bit1_bit0

theorem le_bit1_bit1 {α} [LinearOrderedSemiring α] (a b : α) : a ≤ b → bit1 a ≤ bit1 b :=
  bit1_le_bit1.2
#align norm_num.le_bit1_bit1 NormNum.le_bit1_bit1

theorem sle_one_bit0 {α} [LinearOrderedSemiring α] (a : α) : 1 ≤ a → 1 + 1 ≤ bit0 a :=
  bit0_le_bit0.2
#align norm_num.sle_one_bit0 NormNum.sle_one_bit0

theorem sle_one_bit1 {α} [LinearOrderedSemiring α] (a : α) : 1 ≤ a → 1 + 1 ≤ bit1 a :=
  le_bit0_bit1 _ _
#align norm_num.sle_one_bit1 NormNum.sle_one_bit1

theorem sle_bit0_bit0 {α} [LinearOrderedSemiring α] (a b : α) : a + 1 ≤ b → bit0 a + 1 ≤ bit0 b :=
  le_bit1_bit0 _ _
#align norm_num.sle_bit0_bit0 NormNum.sle_bit0_bit0

theorem sle_bit0_bit1 {α} [LinearOrderedSemiring α] (a b : α) (h : a ≤ b) : bit0 a + 1 ≤ bit1 b :=
  bit1_le_bit1.2 h
#align norm_num.sle_bit0_bit1 NormNum.sle_bit0_bit1

theorem sle_bit1_bit0 {α} [LinearOrderedSemiring α] (a b : α) (h : a + 1 ≤ b) :
    bit1 a + 1 ≤ bit0 b :=
  (bit1_succ a _ rfl).symm ▸ bit0_le_bit0.2 h
#align norm_num.sle_bit1_bit0 NormNum.sle_bit1_bit0

theorem sle_bit1_bit1 {α} [LinearOrderedSemiring α] (a b : α) (h : a + 1 ≤ b) :
    bit1 a + 1 ≤ bit1 b :=
  (bit1_succ a _ rfl).symm ▸ le_bit0_bit1 _ _ h
#align norm_num.sle_bit1_bit1 NormNum.sle_bit1_bit1

/-- Given `a` a rational numeral, returns `⊢ 0 ≤ a`. -/
unsafe def prove_nonneg (ic : instance_cache) : expr → tactic (instance_cache × expr)
  | e@q(Zero.zero) => ic.mk_app `` le_refl [e]
  | e =>
    if ic.α = q(ℕ) then return (ic, q(Nat.zero_le).mk_app [e])
    else do
      let (ic, p) ← prove_pos ic e
      ic `` nonneg_pos [e, p]
#align norm_num.prove_nonneg norm_num.prove_nonneg

section

open MatchNumeralResult

/-- Given `a` a rational numeral, returns `⊢ 1 ≤ a`. -/
unsafe def prove_one_le_nat (ic : instance_cache) : expr → tactic (instance_cache × expr)
  | a =>
    match match_numeral a with
    | one => ic.mk_app `` le_refl [a]
    | bit0 a => do
      let (ic, p) ← prove_one_le_nat a
      ic `` le_one_bit0 [a, p]
    | bit1 a => do
      let (ic, p) ← prove_pos_nat ic a
      ic `` le_one_bit1 [a, p]
    | _ => failed
#align norm_num.prove_one_le_nat norm_num.prove_one_le_nat

mutual
  unsafe def prove_le_nat (ic : instance_cache) : expr → expr → tactic (instance_cache × expr)
    | a, b =>
      if a = b then ic.mk_app `` le_refl [a]
      else
        match match_numeral a, match_numeral b with
        | zero, _ => prove_nonneg ic b
        | one, bit0 b => do
          let (ic, p) ← prove_one_le_nat ic b
          ic `` le_one_bit0 [b, p]
        | one, bit1 b => do
          let (ic, p) ← prove_pos_nat ic b
          ic `` le_one_bit1 [b, p]
        | bit0 a, bit0 b => do
          let (ic, p) ← prove_le_nat a b
          ic `` le_bit0_bit0 [a, b, p]
        | bit0 a, bit1 b => do
          let (ic, p) ← prove_le_nat a b
          ic `` le_bit0_bit1 [a, b, p]
        | bit1 a, bit0 b => do
          let (ic, p) ← prove_sle_nat a b
          ic `` le_bit1_bit0 [a, b, p]
        | bit1 a, bit1 b => do
          let (ic, p) ← prove_le_nat a b
          ic `` le_bit1_bit1 [a, b, p]
        | _, _ => failed
  unsafe def prove_sle_nat (ic : instance_cache) : expr → expr → tactic (instance_cache × expr)
    | a, b =>
      match match_numeral a, match_numeral b with
      | zero, _ => prove_nonneg ic b
      | one, bit0 b => do
        let (ic, p) ← prove_one_le_nat ic b
        ic `` sle_one_bit0 [b, p]
      | one, bit1 b => do
        let (ic, p) ← prove_one_le_nat ic b
        ic `` sle_one_bit1 [b, p]
      | bit0 a, bit0 b => do
        let (ic, p) ← prove_sle_nat a b
        ic `` sle_bit0_bit0 [a, b, p]
      | bit0 a, bit1 b => do
        let (ic, p) ← prove_le_nat a b
        ic `` sle_bit0_bit1 [a, b, p]
      | bit1 a, bit0 b => do
        let (ic, p) ← prove_sle_nat a b
        ic `` sle_bit1_bit0 [a, b, p]
      | bit1 a, bit1 b => do
        let (ic, p) ← prove_sle_nat a b
        ic `` sle_bit1_bit1 [a, b, p]
      | _, _ => failed
end
#align norm_num.prove_le_nat norm_num.prove_le_nat
#align norm_num.prove_sle_nat norm_num.prove_sle_nat

/-- Given `a`,`b` natural numerals, proves `⊢ a ≤ b`. -/
add_decl_doc prove_le_nat

/-- Given `a`,`b` natural numerals, proves `⊢ a + 1 ≤ b`. -/
add_decl_doc prove_sle_nat

/-- Given `a`,`b` natural numerals, proves `⊢ a < b`. -/
unsafe def prove_lt_nat (ic : instance_cache) : expr → expr → tactic (instance_cache × expr)
  | a, b =>
    match match_numeral a, match_numeral b with
    | zero, _ => prove_pos ic b
    | one, bit0 b => do
      let (ic, p) ← prove_one_le_nat ic b
      ic `` lt_one_bit0 [b, p]
    | one, bit1 b => do
      let (ic, p) ← prove_pos_nat ic b
      ic `` lt_one_bit1 [b, p]
    | bit0 a, bit0 b => do
      let (ic, p) ← prove_lt_nat a b
      ic `` lt_bit0_bit0 [a, b, p]
    | bit0 a, bit1 b => do
      let (ic, p) ← prove_le_nat ic a b
      ic `` lt_bit0_bit1 [a, b, p]
    | bit1 a, bit0 b => do
      let (ic, p) ← prove_sle_nat ic a b
      ic `` lt_bit1_bit0 [a, b, p]
    | bit1 a, bit1 b => do
      let (ic, p) ← prove_lt_nat a b
      ic `` lt_bit1_bit1 [a, b, p]
    | _, _ => failed
#align norm_num.prove_lt_nat norm_num.prove_lt_nat

end

theorem clear_denom_lt {α} [LinearOrderedSemiring α] (a a' b b' d : α) (h₀ : 0 < d)
    (ha : a * d = a') (hb : b * d = b') (h : a' < b') : a < b :=
  lt_of_mul_lt_mul_right (by rwa [ha, hb]) (le_of_lt h₀)
#align norm_num.clear_denom_lt NormNum.clear_denom_lt

/-- Given `a`,`b` nonnegative rational numerals, proves `⊢ a < b`. -/
unsafe def prove_lt_nonneg_rat (ic : instance_cache) (a b : expr) (na nb : ℚ) :
    tactic (instance_cache × expr) :=
  if na.den = 1 ∧ nb.den = 1 then prove_lt_nat ic a b
  else do
    let nd := na.den.lcm nb.den
    let (ic, d) ← ic.ofNat nd
    let (ic, p₀) ← prove_pos ic d
    let (ic, a', pa) ← prove_clear_denom' (fun ic e _ => prove_ne_zero' ic e) ic a d na nd
    let (ic, b', pb) ← prove_clear_denom' (fun ic e _ => prove_ne_zero' ic e) ic b d nb nd
    let (ic, p) ← prove_lt_nat ic a' b'
    ic `` clear_denom_lt [a, a', b, b', d, p₀, pa, pb, p]
#align norm_num.prove_lt_nonneg_rat norm_num.prove_lt_nonneg_rat

theorem lt_neg_pos {α} [OrderedAddCommGroup α] (a b : α) (ha : 0 < a) (hb : 0 < b) : -a < b :=
  lt_trans (neg_neg_of_pos ha) hb
#align norm_num.lt_neg_pos NormNum.lt_neg_pos

/-- Given `a`,`b` rational numerals, proves `⊢ a < b`. -/
unsafe def prove_lt_rat (ic : instance_cache) (a b : expr) (na nb : ℚ) :
    tactic (instance_cache × expr) :=
  match match_sign a, match_sign b with
  | Sum.inl a, Sum.inl b => do
    let-- we have to switch the order of `a` and `b` because `a < b ↔ -b < -a`
      (ic, p)
      ← prove_lt_nonneg_rat ic b a (-nb) (-na)
    ic `` neg_lt_neg [b, a, p]
  | Sum.inl a, Sum.inr ff => do
    let (ic, p) ← prove_pos ic a
    ic `` neg_neg_of_pos [a, p]
  | Sum.inl a, Sum.inr tt => do
    let (ic, pa) ← prove_pos ic a
    let (ic, pb) ← prove_pos ic b
    ic `` lt_neg_pos [a, b, pa, pb]
  | Sum.inr ff, _ => prove_pos ic b
  | Sum.inr tt, _ => prove_lt_nonneg_rat ic a b na nb
#align norm_num.prove_lt_rat norm_num.prove_lt_rat

theorem clear_denom_le {α} [LinearOrderedSemiring α] (a a' b b' d : α) (h₀ : 0 < d)
    (ha : a * d = a') (hb : b * d = b') (h : a' ≤ b') : a ≤ b :=
  le_of_mul_le_mul_right (by rwa [ha, hb]) h₀
#align norm_num.clear_denom_le NormNum.clear_denom_le

/-- Given `a`,`b` nonnegative rational numerals, proves `⊢ a ≤ b`. -/
unsafe def prove_le_nonneg_rat (ic : instance_cache) (a b : expr) (na nb : ℚ) :
    tactic (instance_cache × expr) :=
  if na.den = 1 ∧ nb.den = 1 then prove_le_nat ic a b
  else do
    let nd := na.den.lcm nb.den
    let (ic, d) ← ic.ofNat nd
    let (ic, p₀) ← prove_pos ic d
    let (ic, a', pa) ← prove_clear_denom' (fun ic e _ => prove_ne_zero' ic e) ic a d na nd
    let (ic, b', pb) ← prove_clear_denom' (fun ic e _ => prove_ne_zero' ic e) ic b d nb nd
    let (ic, p) ← prove_le_nat ic a' b'
    ic `` clear_denom_le [a, a', b, b', d, p₀, pa, pb, p]
#align norm_num.prove_le_nonneg_rat norm_num.prove_le_nonneg_rat

theorem le_neg_pos {α} [OrderedAddCommGroup α] (a b : α) (ha : 0 ≤ a) (hb : 0 ≤ b) : -a ≤ b :=
  le_trans (neg_nonpos_of_nonneg ha) hb
#align norm_num.le_neg_pos NormNum.le_neg_pos

/-- Given `a`,`b` rational numerals, proves `⊢ a ≤ b`. -/
unsafe def prove_le_rat (ic : instance_cache) (a b : expr) (na nb : ℚ) :
    tactic (instance_cache × expr) :=
  match match_sign a, match_sign b with
  | Sum.inl a, Sum.inl b => do
    let (ic, p) ← prove_le_nonneg_rat ic a b (-na) (-nb)
    ic `` neg_le_neg [a, b, p]
  | Sum.inl a, Sum.inr ff => do
    let (ic, p) ← prove_nonneg ic a
    ic `` neg_nonpos_of_nonneg [a, p]
  | Sum.inl a, Sum.inr tt => do
    let (ic, pa) ← prove_nonneg ic a
    let (ic, pb) ← prove_nonneg ic b
    ic `` le_neg_pos [a, b, pa, pb]
  | Sum.inr ff, _ => prove_nonneg ic b
  | Sum.inr tt, _ => prove_le_nonneg_rat ic a b na nb
#align norm_num.prove_le_rat norm_num.prove_le_rat

/-- Given `a`,`b` rational numerals, proves `⊢ a ≠ b`. This version tries to prove
`⊢ a < b` or `⊢ b < a`, and so is not appropriate for types without an order relation. -/
unsafe def prove_ne_rat (ic : instance_cache) (a b : expr) (na nb : ℚ) :
    tactic (instance_cache × expr) :=
  if na < nb then do
    let (ic, p) ← prove_lt_rat ic a b na nb
    ic `` ne_of_lt [a, b, p]
  else do
    let (ic, p) ← prove_lt_rat ic b a nb na
    ic `` ne_of_gt [a, b, p]
#align norm_num.prove_ne_rat norm_num.prove_ne_rat

theorem nat_cast_zero {α} [Semiring α] : ↑(0 : ℕ) = (0 : α) :=
  Nat.cast_zero
#align norm_num.nat_cast_zero NormNum.nat_cast_zero

theorem nat_cast_one {α} [Semiring α] : ↑(1 : ℕ) = (1 : α) :=
  Nat.cast_one
#align norm_num.nat_cast_one NormNum.nat_cast_one

theorem nat_cast_bit0 {α} [Semiring α] (a : ℕ) (a' : α) (h : ↑a = a') : ↑(bit0 a) = bit0 a' :=
  h ▸ Nat.cast_bit0 _
#align norm_num.nat_cast_bit0 NormNum.nat_cast_bit0

theorem nat_cast_bit1 {α} [Semiring α] (a : ℕ) (a' : α) (h : ↑a = a') : ↑(bit1 a) = bit1 a' :=
  h ▸ Nat.cast_bit1 _
#align norm_num.nat_cast_bit1 NormNum.nat_cast_bit1

theorem int_cast_zero {α} [Ring α] : ↑(0 : ℤ) = (0 : α) :=
  Int.cast_zero
#align norm_num.int_cast_zero NormNum.int_cast_zero

theorem int_cast_one {α} [Ring α] : ↑(1 : ℤ) = (1 : α) :=
  Int.cast_one
#align norm_num.int_cast_one NormNum.int_cast_one

theorem int_cast_bit0 {α} [Ring α] (a : ℤ) (a' : α) (h : ↑a = a') : ↑(bit0 a) = bit0 a' :=
  h ▸ Int.cast_bit0 _
#align norm_num.int_cast_bit0 NormNum.int_cast_bit0

theorem int_cast_bit1 {α} [Ring α] (a : ℤ) (a' : α) (h : ↑a = a') : ↑(bit1 a) = bit1 a' :=
  h ▸ Int.cast_bit1 _
#align norm_num.int_cast_bit1 NormNum.int_cast_bit1

theorem rat_cast_bit0 {α} [DivisionRing α] [CharZero α] (a : ℚ) (a' : α) (h : ↑a = a') :
    ↑(bit0 a) = bit0 a' :=
  h ▸ Rat.cast_bit0 _
#align norm_num.rat_cast_bit0 NormNum.rat_cast_bit0

theorem rat_cast_bit1 {α} [DivisionRing α] [CharZero α] (a : ℚ) (a' : α) (h : ↑a = a') :
    ↑(bit1 a) = bit1 a' :=
  h ▸ Rat.cast_bit1 _
#align norm_num.rat_cast_bit1 NormNum.rat_cast_bit1

/-- Given `a' : α` a natural numeral, returns `(a : ℕ, ⊢ ↑a = a')`.
(Note that the returned value is on the left of the equality.) -/
unsafe def prove_nat_uncast (ic nc : instance_cache) :
    ∀ a' : expr, tactic (instance_cache × instance_cache × expr × expr)
  | a' =>
    match match_numeral a' with
    | match_numeral_result.zero => do
      let (nc, e) ← nc.mk_app `` Zero.zero []
      let (ic, p) ← ic.mk_app `` nat_cast_zero []
      return (ic, nc, e, p)
    | match_numeral_result.one => do
      let (nc, e) ← nc.mk_app `` One.one []
      let (ic, p) ← ic.mk_app `` nat_cast_one []
      return (ic, nc, e, p)
    | match_numeral_result.bit0 a' => do
      let (ic, nc, a, p) ← prove_nat_uncast a'
      let (nc, a0) ← nc.mk_bit0 a
      let (ic, p) ← ic.mk_app `` nat_cast_bit0 [a, a', p]
      return (ic, nc, a0, p)
    | match_numeral_result.bit1 a' => do
      let (ic, nc, a, p) ← prove_nat_uncast a'
      let (nc, a1) ← nc.mk_bit1 a
      let (ic, p) ← ic.mk_app `` nat_cast_bit1 [a, a', p]
      return (ic, nc, a1, p)
    | _ => failed
#align norm_num.prove_nat_uncast norm_num.prove_nat_uncast

/-- Given `a' : α` a natural numeral, returns `(a : ℤ, ⊢ ↑a = a')`.
(Note that the returned value is on the left of the equality.) -/
unsafe def prove_int_uncast_nat (ic zc : instance_cache) :
    ∀ a' : expr, tactic (instance_cache × instance_cache × expr × expr)
  | a' =>
    match match_numeral a' with
    | match_numeral_result.zero => do
      let (zc, e) ← zc.mk_app `` Zero.zero []
      let (ic, p) ← ic.mk_app `` int_cast_zero []
      return (ic, zc, e, p)
    | match_numeral_result.one => do
      let (zc, e) ← zc.mk_app `` One.one []
      let (ic, p) ← ic.mk_app `` int_cast_one []
      return (ic, zc, e, p)
    | match_numeral_result.bit0 a' => do
      let (ic, zc, a, p) ← prove_int_uncast_nat a'
      let (zc, a0) ← zc.mk_bit0 a
      let (ic, p) ← ic.mk_app `` int_cast_bit0 [a, a', p]
      return (ic, zc, a0, p)
    | match_numeral_result.bit1 a' => do
      let (ic, zc, a, p) ← prove_int_uncast_nat a'
      let (zc, a1) ← zc.mk_bit1 a
      let (ic, p) ← ic.mk_app `` int_cast_bit1 [a, a', p]
      return (ic, zc, a1, p)
    | _ => failed
#align norm_num.prove_int_uncast_nat norm_num.prove_int_uncast_nat

/-- Given `a' : α` a natural numeral, returns `(a : ℚ, ⊢ ↑a = a')`.
(Note that the returned value is on the left of the equality.) -/
unsafe def prove_rat_uncast_nat (ic qc : instance_cache) (cz_inst : expr) :
    ∀ a' : expr, tactic (instance_cache × instance_cache × expr × expr)
  | a' =>
    match match_numeral a' with
    | match_numeral_result.zero => do
      let (qc, e) ← qc.mk_app `` Zero.zero []
      let (ic, p) ← ic.mk_app `` Rat.cast_zero []
      return (ic, qc, e, p)
    | match_numeral_result.one => do
      let (qc, e) ← qc.mk_app `` One.one []
      let (ic, p) ← ic.mk_app `` Rat.cast_one []
      return (ic, qc, e, p)
    | match_numeral_result.bit0 a' => do
      let (ic, qc, a, p) ← prove_rat_uncast_nat a'
      let (qc, a0) ← qc.mk_bit0 a
      let (ic, p) ← ic.mk_app `` rat_cast_bit0 [cz_inst, a, a', p]
      return (ic, qc, a0, p)
    | match_numeral_result.bit1 a' => do
      let (ic, qc, a, p) ← prove_rat_uncast_nat a'
      let (qc, a1) ← qc.mk_bit1 a
      let (ic, p) ← ic.mk_app `` rat_cast_bit1 [cz_inst, a, a', p]
      return (ic, qc, a1, p)
    | _ => failed
#align norm_num.prove_rat_uncast_nat norm_num.prove_rat_uncast_nat

theorem rat_cast_div {α} [DivisionRing α] [CharZero α] (a b : ℚ) (a' b' : α) (ha : ↑a = a')
    (hb : ↑b = b') : ↑(a / b) = a' / b' :=
  ha ▸ hb ▸ Rat.cast_div _ _
#align norm_num.rat_cast_div NormNum.rat_cast_div

/-- Given `a' : α` a nonnegative rational numeral, returns `(a : ℚ, ⊢ ↑a = a')`.
(Note that the returned value is on the left of the equality.) -/
unsafe def prove_rat_uncast_nonneg (ic qc : instance_cache) (cz_inst a' : expr) (na' : ℚ) :
    tactic (instance_cache × instance_cache × expr × expr) :=
  if na'.den = 1 then prove_rat_uncast_nat ic qc cz_inst a'
  else do
    let [_, _, a', b'] ← return a'.get_app_args
    let (ic, qc, a, pa) ← prove_rat_uncast_nat ic qc cz_inst a'
    let (ic, qc, b, pb) ← prove_rat_uncast_nat ic qc cz_inst b'
    let (qc, e) ← qc.mk_app `` Div.div [a, b]
    let (ic, p) ← ic.mk_app `` rat_cast_div [cz_inst, a, b, a', b', pa, pb]
    return (ic, qc, e, p)
#align norm_num.prove_rat_uncast_nonneg norm_num.prove_rat_uncast_nonneg

theorem int_cast_neg {α} [Ring α] (a : ℤ) (a' : α) (h : ↑a = a') : ↑(-a) = -a' :=
  h ▸ Int.cast_neg _
#align norm_num.int_cast_neg NormNum.int_cast_neg

theorem rat_cast_neg {α} [DivisionRing α] (a : ℚ) (a' : α) (h : ↑a = a') : ↑(-a) = -a' :=
  h ▸ Rat.cast_neg _
#align norm_num.rat_cast_neg NormNum.rat_cast_neg

/-- Given `a' : α` an integer numeral, returns `(a : ℤ, ⊢ ↑a = a')`.
(Note that the returned value is on the left of the equality.) -/
unsafe def prove_int_uncast (ic zc : instance_cache) (a' : expr) :
    tactic (instance_cache × instance_cache × expr × expr) :=
  match match_neg a' with
  | some a' => do
    let (ic, zc, a, p) ← prove_int_uncast_nat ic zc a'
    let (zc, e) ← zc.mk_app `` Neg.neg [a]
    let (ic, p) ← ic.mk_app `` int_cast_neg [a, a', p]
    return (ic, zc, e, p)
  | none => prove_int_uncast_nat ic zc a'
#align norm_num.prove_int_uncast norm_num.prove_int_uncast

/-- Given `a' : α` a rational numeral, returns `(a : ℚ, ⊢ ↑a = a')`.
(Note that the returned value is on the left of the equality.) -/
unsafe def prove_rat_uncast (ic qc : instance_cache) (cz_inst a' : expr) (na' : ℚ) :
    tactic (instance_cache × instance_cache × expr × expr) :=
  match match_neg a' with
  | some a' => do
    let (ic, qc, a, p) ← prove_rat_uncast_nonneg ic qc cz_inst a' (-na')
    let (qc, e) ← qc.mk_app `` Neg.neg [a]
    let (ic, p) ← ic.mk_app `` rat_cast_neg [a, a', p]
    return (ic, qc, e, p)
  | none => prove_rat_uncast_nonneg ic qc cz_inst a' na'
#align norm_num.prove_rat_uncast norm_num.prove_rat_uncast

theorem nat_cast_ne {α} [Semiring α] [CharZero α] (a b : ℕ) (a' b' : α) (ha : ↑a = a')
    (hb : ↑b = b') (h : a ≠ b) : a' ≠ b' :=
  ha ▸ hb ▸ mt Nat.cast_inj.1 h
#align norm_num.nat_cast_ne NormNum.nat_cast_ne

theorem int_cast_ne {α} [Ring α] [CharZero α] (a b : ℤ) (a' b' : α) (ha : ↑a = a') (hb : ↑b = b')
    (h : a ≠ b) : a' ≠ b' :=
  ha ▸ hb ▸ mt Int.cast_inj.1 h
#align norm_num.int_cast_ne NormNum.int_cast_ne

theorem rat_cast_ne {α} [DivisionRing α] [CharZero α] (a b : ℚ) (a' b' : α) (ha : ↑a = a')
    (hb : ↑b = b') (h : a ≠ b) : a' ≠ b' :=
  ha ▸ hb ▸ mt Rat.cast_inj.1 h
#align norm_num.rat_cast_ne NormNum.rat_cast_ne

/-- Given `a`,`b` rational numerals, proves `⊢ a ≠ b`. Currently it tries two methods:

  * Prove `⊢ a < b` or `⊢ b < a`, if the base type has an order
  * Embed `↑(a':ℚ) = a` and `↑(b':ℚ) = b`, and then prove `a' ≠ b'`.
    This requires that the base type be `char_zero`, and also that it be a `division_ring`
    so that the coercion from `ℚ` is well defined.

We may also add coercions to `ℤ` and `ℕ` as well in order to support `char_zero`
rings and semirings. -/
unsafe def prove_ne : instance_cache → expr → expr → ℚ → ℚ → tactic (instance_cache × expr)
  | ic, a, b, na, nb =>
    prove_ne_rat ic a b na nb <|> do
      let cz_inst ← mk_mapp `` CharZero [ic.α, none] >>= mk_instance
      if na = 1 ∧ nb = 1 then
          if na ≥ 0 ∧ nb ≥ 0 then do
            guard (ic ≠ q(ℕ))
            let nc ← mk_instance_cache q(ℕ)
            let (ic, nc, a', pa) ← prove_nat_uncast ic nc a
            let (ic, nc, b', pb) ← prove_nat_uncast ic nc b
            let (nc, p) ← prove_ne_rat nc a' b' na nb
            ic `` nat_cast_ne [cz_inst, a', b', a, b, pa, pb, p]
          else do
            guard (ic ≠ q(ℤ))
            let zc ← mk_instance_cache q(ℤ)
            let (ic, zc, a', pa) ← prove_int_uncast ic zc a
            let (ic, zc, b', pb) ← prove_int_uncast ic zc b
            let (zc, p) ← prove_ne_rat zc a' b' na nb
            ic `` int_cast_ne [cz_inst, a', b', a, b, pa, pb, p]
        else do
          guard (ic ≠ q(ℚ))
          let qc ← mk_instance_cache q(ℚ)
          let (ic, qc, a', pa) ← prove_rat_uncast ic qc cz_inst a na
          let (ic, qc, b', pb) ← prove_rat_uncast ic qc cz_inst b nb
          let (qc, p) ← prove_ne_rat qc a' b' na nb
          ic `` rat_cast_ne [cz_inst, a', b', a, b, pa, pb, p]
#align norm_num.prove_ne norm_num.prove_ne

/-- Given `a` a rational numeral, returns `⊢ a ≠ 0`. -/
unsafe def prove_ne_zero (ic : instance_cache) : expr → ℚ → tactic (instance_cache × expr)
  | a, na => do
    let (ic, z) ← ic.mk_app `` Zero.zero []
    prove_ne ic a z na 0
#align norm_num.prove_ne_zero norm_num.prove_ne_zero

/-- Given `a` nonnegative rational and `d` a natural number, returns `(b, ⊢ a * d = b)`.
(`d` should be a multiple of the denominator of `a`, so that `b` is a natural number.) -/
unsafe def prove_clear_denom :
    instance_cache → expr → expr → ℚ → ℕ → tactic (instance_cache × expr × expr) :=
  prove_clear_denom' prove_ne_zero
#align norm_num.prove_clear_denom norm_num.prove_clear_denom

theorem clear_denom_add {α} [DivisionRing α] (a a' b b' c c' d : α) (h₀ : d ≠ 0) (ha : a * d = a')
    (hb : b * d = b') (hc : c * d = c') (h : a' + b' = c') : a + b = c :=
  mul_right_cancel₀ h₀ <| by rwa [add_mul, ha, hb, hc]
#align norm_num.clear_denom_add NormNum.clear_denom_add

/-- Given `a`,`b`,`c` nonnegative rational numerals, returns `⊢ a + b = c`. -/
unsafe def prove_add_nonneg_rat (ic : instance_cache) (a b c : expr) (na nb nc : ℚ) :
    tactic (instance_cache × expr) :=
  if na.den = 1 ∧ nb.den = 1 then prove_add_nat ic a b c
  else do
    let nd := na.den.lcm nb.den
    let (ic, d) ← ic.ofNat nd
    let (ic, p₀) ← prove_ne_zero ic d nd
    let (ic, a', pa) ← prove_clear_denom ic a d na nd
    let (ic, b', pb) ← prove_clear_denom ic b d nb nd
    let (ic, c', pc) ← prove_clear_denom ic c d nc nd
    let (ic, p) ← prove_add_nat ic a' b' c'
    ic `` clear_denom_add [a, a', b, b', c, c', d, p₀, pa, pb, pc, p]
#align norm_num.prove_add_nonneg_rat norm_num.prove_add_nonneg_rat

theorem add_pos_neg_pos {α} [AddGroup α] (a b c : α) (h : c + b = a) : a + -b = c :=
  h ▸ by simp
#align norm_num.add_pos_neg_pos NormNum.add_pos_neg_pos

theorem add_pos_neg_neg {α} [AddGroup α] (a b c : α) (h : c + a = b) : a + -b = -c :=
  h ▸ by simp
#align norm_num.add_pos_neg_neg NormNum.add_pos_neg_neg

theorem add_neg_pos_pos {α} [AddGroup α] (a b c : α) (h : a + c = b) : -a + b = c :=
  h ▸ by simp
#align norm_num.add_neg_pos_pos NormNum.add_neg_pos_pos

theorem add_neg_pos_neg {α} [AddGroup α] (a b c : α) (h : b + c = a) : -a + b = -c :=
  h ▸ by simp
#align norm_num.add_neg_pos_neg NormNum.add_neg_pos_neg

theorem add_neg_neg {α} [AddGroup α] (a b c : α) (h : b + a = c) : -a + -b = -c :=
  h ▸ by simp
#align norm_num.add_neg_neg NormNum.add_neg_neg

/-- Given `a`,`b`,`c` rational numerals, returns `⊢ a + b = c`. -/
unsafe def prove_add_rat (ic : instance_cache) (ea eb ec : expr) (a b c : ℚ) :
    tactic (instance_cache × expr) :=
  match match_neg ea, match_neg eb, match_neg ec with
  | some ea, some eb, some ec => do
    let (ic, p) ← prove_add_nonneg_rat ic eb ea ec (-b) (-a) (-c)
    ic `` add_neg_neg [ea, eb, ec, p]
  | some ea, none, some ec => do
    let (ic, p) ← prove_add_nonneg_rat ic eb ec ea b (-c) (-a)
    ic `` add_neg_pos_neg [ea, eb, ec, p]
  | some ea, none, none => do
    let (ic, p) ← prove_add_nonneg_rat ic ea ec eb (-a) c b
    ic `` add_neg_pos_pos [ea, eb, ec, p]
  | none, some eb, some ec => do
    let (ic, p) ← prove_add_nonneg_rat ic ec ea eb (-c) a (-b)
    ic `` add_pos_neg_neg [ea, eb, ec, p]
  | none, some eb, none => do
    let (ic, p) ← prove_add_nonneg_rat ic ec eb ea c (-b) a
    ic `` add_pos_neg_pos [ea, eb, ec, p]
  | _, _, _ => prove_add_nonneg_rat ic ea eb ec a b c
#align norm_num.prove_add_rat norm_num.prove_add_rat

/-- Given `a`,`b` rational numerals, returns `(c, ⊢ a + b = c)`. -/
unsafe def prove_add_rat' (ic : instance_cache) (a b : expr) :
    tactic (instance_cache × expr × expr) := do
  let na ← a.to_rat
  let nb ← b.to_rat
  let nc := na + nb
  let (ic, c) ← ic.of_rat nc
  let (ic, p) ← prove_add_rat ic a b c na nb nc
  return (ic, c, p)
#align norm_num.prove_add_rat' norm_num.prove_add_rat'

theorem clear_denom_simple_nat {α} [DivisionRing α] (a : α) : (1 : α) ≠ 0 ∧ a * 1 = a :=
  ⟨one_ne_zero, mul_one _⟩
#align norm_num.clear_denom_simple_nat NormNum.clear_denom_simple_nat

theorem clear_denom_simple_div {α} [DivisionRing α] (a b : α) (h : b ≠ 0) : b ≠ 0 ∧ a / b * b = a :=
  ⟨h, div_mul_cancel _ h⟩
#align norm_num.clear_denom_simple_div NormNum.clear_denom_simple_div

/-- Given `a` a nonnegative rational numeral, returns `(b, c, ⊢ a * b = c)`
where `b` and `c` are natural numerals. (`b` will be the denominator of `a`.) -/
unsafe def prove_clear_denom_simple (c : instance_cache) (a : expr) (na : ℚ) :
    tactic (instance_cache × expr × expr × expr) :=
  if na.den = 1 then do
    let (c, d) ← c.mk_app `` One.one []
    let (c, p) ← c.mk_app `` clear_denom_simple_nat [a]
    return (c, d, a, p)
  else do
    let [α, _, a, b] ← return a.get_app_args
    let (c, p₀) ← prove_ne_zero c b na.den
    let (c, p) ← c.mk_app `` clear_denom_simple_div [a, b, p₀]
    return (c, b, a, p)
#align norm_num.prove_clear_denom_simple norm_num.prove_clear_denom_simple

theorem clear_denom_mul {α} [Field α] (a a' b b' c c' d₁ d₂ d : α) (ha : d₁ ≠ 0 ∧ a * d₁ = a')
    (hb : d₂ ≠ 0 ∧ b * d₂ = b') (hc : c * d = c') (hd : d₁ * d₂ = d) (h : a' * b' = c') :
    a * b = c :=
  mul_right_cancel₀ ha.1 <|
    mul_right_cancel₀ hb.1 <| by
      rw [mul_assoc c, hd, hc, ← h, ← ha.2, ← hb.2, ← mul_assoc, mul_right_comm a]
#align norm_num.clear_denom_mul NormNum.clear_denom_mul

/-- Given `a`,`b` nonnegative rational numerals, returns `(c, ⊢ a * b = c)`. -/
unsafe def prove_mul_nonneg_rat (ic : instance_cache) (a b : expr) (na nb : ℚ) :
    tactic (instance_cache × expr × expr) :=
  if na.den = 1 ∧ nb.den = 1 then prove_mul_nat ic a b
  else do
    let nc := na * nb
    let (ic, c) ← ic.of_rat nc
    let (ic, d₁, a', pa) ← prove_clear_denom_simple ic a na
    let (ic, d₂, b', pb) ← prove_clear_denom_simple ic b nb
    let (ic, d, pd) ← prove_mul_nat ic d₁ d₂
    let nd ← d.toNat
    let (ic, c', pc) ← prove_clear_denom ic c d nc nd
    let (ic, _, p) ← prove_mul_nat ic a' b'
    let (ic, p) ← ic.mk_app `` clear_denom_mul [a, a', b, b', c, c', d₁, d₂, d, pa, pb, pc, pd, p]
    return (ic, c, p)
#align norm_num.prove_mul_nonneg_rat norm_num.prove_mul_nonneg_rat

theorem mul_neg_pos {α} [Ring α] (a b c : α) (h : a * b = c) : -a * b = -c :=
  h ▸ by simp
#align norm_num.mul_neg_pos NormNum.mul_neg_pos

theorem mul_pos_neg {α} [Ring α] (a b c : α) (h : a * b = c) : a * -b = -c :=
  h ▸ by simp
#align norm_num.mul_pos_neg NormNum.mul_pos_neg

theorem mul_neg_neg {α} [Ring α] (a b c : α) (h : a * b = c) : -a * -b = c :=
  h ▸ by simp
#align norm_num.mul_neg_neg NormNum.mul_neg_neg

/-- Given `a`,`b` rational numerals, returns `(c, ⊢ a * b = c)`. -/
unsafe def prove_mul_rat (ic : instance_cache) (a b : expr) (na nb : ℚ) :
    tactic (instance_cache × expr × expr) :=
  match match_sign a, match_sign b with
  | Sum.inl a, Sum.inl b => do
    let (ic, c, p) ← prove_mul_nonneg_rat ic a b (-na) (-nb)
    let (ic, p) ← ic.mk_app `` mul_neg_neg [a, b, c, p]
    return (ic, c, p)
  | Sum.inr ff, _ => do
    let (ic, z) ← ic.mk_app `` Zero.zero []
    let (ic, p) ← ic.mk_app `` MulZeroClass.zero_mul [b]
    return (ic, z, p)
  | _, Sum.inr ff => do
    let (ic, z) ← ic.mk_app `` Zero.zero []
    let (ic, p) ← ic.mk_app `` MulZeroClass.mul_zero [a]
    return (ic, z, p)
  | Sum.inl a, Sum.inr tt => do
    let (ic, c, p) ← prove_mul_nonneg_rat ic a b (-na) nb
    let (ic, p) ← ic.mk_app `` mul_neg_pos [a, b, c, p]
    let (ic, c') ← ic.mk_app `` Neg.neg [c]
    return (ic, c', p)
  | Sum.inr tt, Sum.inl b => do
    let (ic, c, p) ← prove_mul_nonneg_rat ic a b na (-nb)
    let (ic, p) ← ic.mk_app `` mul_pos_neg [a, b, c, p]
    let (ic, c') ← ic.mk_app `` Neg.neg [c]
    return (ic, c', p)
  | Sum.inr tt, Sum.inr tt => prove_mul_nonneg_rat ic a b na nb
#align norm_num.prove_mul_rat norm_num.prove_mul_rat

theorem inv_neg {α} [DivisionRing α] (a b : α) (h : a⁻¹ = b) : (-a)⁻¹ = -b :=
  h ▸ by simp only [inv_eq_one_div, one_div_neg_eq_neg_one_div]
#align norm_num.inv_neg NormNum.inv_neg

theorem inv_one {α} [DivisionRing α] : (1 : α)⁻¹ = 1 :=
  inv_one
#align norm_num.inv_one NormNum.inv_one

theorem inv_one_div {α} [DivisionRing α] (a : α) : (1 / a)⁻¹ = a := by rw [one_div, inv_inv]
#align norm_num.inv_one_div NormNum.inv_one_div

theorem inv_div_one {α} [DivisionRing α] (a : α) : a⁻¹ = 1 / a :=
  inv_eq_one_div _
#align norm_num.inv_div_one NormNum.inv_div_one

theorem inv_div {α} [DivisionRing α] (a b : α) : (a / b)⁻¹ = b / a := by
  simp only [inv_eq_one_div, one_div_div]
#align norm_num.inv_div NormNum.inv_div

/-- Given `a` a rational numeral, returns `(b, ⊢ a⁻¹ = b)`. -/
unsafe def prove_inv : instance_cache → expr → ℚ → tactic (instance_cache × expr × expr)
  | ic, e, n =>
    match match_sign e with
    | Sum.inl e => do
      let (ic, e', p) ← prove_inv ic e (-n)
      let (ic, r) ← ic.mk_app `` Neg.neg [e']
      let (ic, p) ← ic.mk_app `` inv_neg [e, e', p]
      return (ic, r, p)
    | Sum.inr ff => do
      let (ic, p) ← ic.mk_app `` inv_zero []
      return (ic, e, p)
    | Sum.inr tt =>
      if n.num = 1 then
        if n.den = 1 then do
          let (ic, p) ← ic.mk_app `` inv_one []
          return (ic, e, p)
        else do
          let e := e.app_arg
          let (ic, p) ← ic.mk_app `` inv_one_div [e]
          return (ic, e, p)
      else
        if n.den = 1 then do
          let (ic, p) ← ic.mk_app `` inv_div_one [e]
          let e ← infer_type p
          return (ic, e, p)
        else do
          let [_, _, a, b] ← return e.get_app_args
          let (ic, e') ← ic.mk_app `` Div.div [b, a]
          let (ic, p) ← ic.mk_app `` inv_div [a, b]
          return (ic, e', p)
#align norm_num.prove_inv norm_num.prove_inv

theorem div_eq {α} [DivisionRing α] (a b b' c : α) (hb : b⁻¹ = b') (h : a * b' = c) : a / b = c :=
  by rwa [← hb, ← div_eq_mul_inv] at h
#align norm_num.div_eq NormNum.div_eq

/-- Given `a`,`b` rational numerals, returns `(c, ⊢ a / b = c)`. -/
unsafe def prove_div (ic : instance_cache) (a b : expr) (na nb : ℚ) :
    tactic (instance_cache × expr × expr) := do
  let (ic, b', pb) ← prove_inv ic b nb
  let (ic, c, p) ← prove_mul_rat ic a b' na nb⁻¹
  let (ic, p) ← ic.mk_app `` div_eq [a, b, b', c, pb, p]
  return (ic, c, p)
#align norm_num.prove_div norm_num.prove_div

/-- Given `a` a rational numeral, returns `(b, ⊢ -a = b)`. -/
unsafe def prove_neg (ic : instance_cache) (a : expr) : tactic (instance_cache × expr × expr) :=
  match match_sign a with
  | Sum.inl a => do
    let (ic, p) ← ic.mk_app `` neg_neg [a]
    return (ic, a, p)
  | Sum.inr ff => do
    let (ic, p) ← ic.mk_app `` neg_zero []
    return (ic, a, p)
  | Sum.inr tt => do
    let (ic, a') ← ic.mk_app `` Neg.neg [a]
    let p ← mk_eq_refl a'
    return (ic, a', p)
#align norm_num.prove_neg norm_num.prove_neg

theorem sub_pos {α} [AddGroup α] (a b b' c : α) (hb : -b = b') (h : a + b' = c) : a - b = c := by
  rwa [← hb, ← sub_eq_add_neg] at h
#align norm_num.sub_pos NormNum.sub_pos

theorem sub_neg {α} [AddGroup α] (a b c : α) (h : a + b = c) : a - -b = c := by rwa [sub_neg_eq_add]
#align norm_num.sub_neg NormNum.sub_neg

/-- Given `a`,`b` rational numerals, returns `(c, ⊢ a - b = c)`. -/
unsafe def prove_sub (ic : instance_cache) (a b : expr) : tactic (instance_cache × expr × expr) :=
  match match_sign b with
  | Sum.inl b => do
    let (ic, c, p) ← prove_add_rat' ic a b
    let (ic, p) ← ic.mk_app `` sub_neg [a, b, c, p]
    return (ic, c, p)
  | Sum.inr ff => do
    let (ic, p) ← ic.mk_app `` sub_zero [a]
    return (ic, a, p)
  | Sum.inr tt => do
    let (ic, b', pb) ← prove_neg ic b
    let (ic, c, p) ← prove_add_rat' ic a b'
    let (ic, p) ← ic.mk_app `` sub_pos [a, b, b', c, pb, p]
    return (ic, c, p)
#align norm_num.prove_sub norm_num.prove_sub

theorem sub_nat_pos (a b c : ℕ) (h : b + c = a) : a - b = c :=
  h ▸ add_tsub_cancel_left _ _
#align norm_num.sub_nat_pos NormNum.sub_nat_pos

theorem sub_nat_neg (a b c : ℕ) (h : a + c = b) : a - b = 0 :=
  tsub_eq_zero_iff_le.mpr <| h ▸ Nat.le_add_right _ _
#align norm_num.sub_nat_neg NormNum.sub_nat_neg

/-- Given `a : nat`,`b : nat` natural numerals, returns `(c, ⊢ a - b = c)`. -/
unsafe def prove_sub_nat (ic : instance_cache) (a b : expr) : tactic (expr × expr) := do
  let na ← a.toNat
  let nb ← b.toNat
  if nb ≤ na then do
      let (ic, c) ← ic (na - nb)
      let (ic, p) ← prove_add_nat ic b c a
      return (c, q(sub_nat_pos).mk_app [a, b, c, p])
    else do
      let (ic, c) ← ic (nb - na)
      let (ic, p) ← prove_add_nat ic a c b
      return (q((0 : ℕ)), q(sub_nat_neg).mk_app [a, b, c, p])
#align norm_num.prove_sub_nat norm_num.prove_sub_nat

-- failed to format: unknown constant 'term.pseudo.antiquot'
/--
      Evaluates the basic field operations `+`,`neg`,`-`,`*`,`inv`,`/` on numerals.
      Also handles nat subtraction. Does not do recursive simplification; that is,
      `1 + 1 + 1` will not simplify but `2 + 1` will. This is handled by the top level
      `simp` call in `norm_num.derive`. -/
    unsafe
  def
    eval_field
    : expr → tactic ( expr × expr )
    |
        q( $ ( e₁ ) + $ ( e₂ ) )
        =>
        do
          let n₁ ← e₁ . to_rat
            let n₂ ← e₂ . to_rat
            let c ← infer_type e₁ >>= mk_instance_cache
            let n₃ := n₁ + n₂
            let ( c , e₃ ) ← c . of_rat n₃
            let ( _ , p ) ← prove_add_rat c e₁ e₂ e₃ n₁ n₂ n₃
            return ( e₃ , p )
      |
        q( $ ( e₁ ) * $ ( e₂ ) )
        =>
        do
          let n₁ ← e₁ . to_rat
            let n₂ ← e₂ . to_rat
            let c ← infer_type e₁ >>= mk_instance_cache
            Prod.snd <$> prove_mul_rat c e₁ e₂ n₁ n₂
      | q( - $ ( e ) ) => do let c ← infer_type e >>= mk_instance_cache Prod.snd <$> prove_neg c e
      |
        q( @ Sub.sub $ ( α ) $ ( inst ) $ ( a ) $ ( b ) )
        =>
        do
          let c ← mk_instance_cache α
            if α = q( Nat ) then prove_sub_nat c a b else Prod.snd <$> prove_sub c a b
      |
        q( Inv.inv $ ( e ) )
        =>
        do
          let n ← e . to_rat let c ← infer_type e >>= mk_instance_cache Prod.snd <$> prove_inv c e n
      |
        q( $ ( e₁ ) / $ ( e₂ ) )
        =>
        do
          let n₁ ← e₁ . to_rat
            let n₂ ← e₂ . to_rat
            let c ← infer_type e₁ >>= mk_instance_cache
            Prod.snd <$> prove_div c e₁ e₂ n₁ n₂
      | _ => failed
#align norm_num.eval_field norm_num.eval_field

theorem pow_bit0 [Monoid α] (a c' c : α) (b : ℕ) (h : a ^ b = c') (h₂ : c' * c' = c) :
    a ^ bit0 b = c :=
  h₂ ▸ by simp [pow_bit0, h]
#align norm_num.pow_bit0 NormNum.pow_bit0

theorem pow_bit1 [Monoid α] (a c₁ c₂ c : α) (b : ℕ) (h : a ^ b = c₁) (h₂ : c₁ * c₁ = c₂)
    (h₃ : c₂ * a = c) : a ^ bit1 b = c := by rw [← h₃, ← h₂] <;> simp [pow_bit1, h]
#align norm_num.pow_bit1 NormNum.pow_bit1

section

open MatchNumeralResult

/-- Given `a` a rational numeral and `b : nat`, returns `(c, ⊢ a ^ b = c)`. -/
unsafe def prove_pow (a : expr) (na : ℚ) :
    instance_cache → expr → tactic (instance_cache × expr × expr)
  | ic, b =>
    match match_numeral b with
    | zero => do
      let (ic, p) ← ic.mk_app `` pow_zero [a]
      let (ic, o) ← ic.mk_app `` One.one []
      return (ic, o, p)
    | one => do
      let (ic, p) ← ic.mk_app `` pow_one [a]
      return (ic, a, p)
    | bit0 b => do
      let (ic, c', p) ← prove_pow ic b
      let nc' ← expr.to_rat c'
      let (ic, c, p₂) ← prove_mul_rat ic c' c' nc' nc'
      let (ic, p) ← ic.mk_app `` pow_bit0 [a, c', c, b, p, p₂]
      return (ic, c, p)
    | bit1 b => do
      let (ic, c₁, p) ← prove_pow ic b
      let nc₁ ← expr.to_rat c₁
      let (ic, c₂, p₂) ← prove_mul_rat ic c₁ c₁ nc₁ nc₁
      let (ic, c, p₃) ← prove_mul_rat ic c₂ a (nc₁ * nc₁) na
      let (ic, p) ← ic.mk_app `` pow_bit1 [a, c₁, c₂, c, b, p, p₂, p₃]
      return (ic, c, p)
    | _ => failed
#align norm_num.prove_pow norm_num.prove_pow

end

theorem zpow_pos {α} [DivInvMonoid α] (a : α) (b : ℤ) (b' : ℕ) (c : α) (hb : b = b')
    (h : a ^ b' = c) : a ^ b = c := by rw [← h, hb, zpow_ofNat]
#align norm_num.zpow_pos NormNum.zpow_pos

theorem zpow_neg {α} [DivInvMonoid α] (a : α) (b : ℤ) (b' : ℕ) (c c' : α) (b0 : 0 < b')
    (hb : b = b') (h : a ^ b' = c) (hc : c⁻¹ = c') : a ^ (-b) = c' := by
  rw [← hc, ← h, hb, zpow_neg_coe_of_pos _ b0]
#align norm_num.zpow_neg NormNum.zpow_neg

/-- Given `a` a rational numeral and `b : ℤ`, returns `(c, ⊢ a ^ b = c)`. -/
unsafe def prove_zpow (ic zc nc : instance_cache) (a : expr) (na : ℚ) (b : expr) :
    tactic (instance_cache × instance_cache × instance_cache × expr × expr) :=
  match match_sign b with
  | Sum.inl b => do
    let (zc, nc, b', hb) ← prove_nat_uncast zc nc b
    let (nc, b0) ← prove_pos nc b'
    let (ic, c, h) ← prove_pow a na ic b'
    let (ic, c', hc) ← c.to_rat >>= prove_inv ic c
    let (ic, p) ← ic.mk_app `` zpow_neg [a, b, b', c, c', b0, hb, h, hc]
    pure (ic, zc, nc, c', p)
  | Sum.inr ff => do
    let (ic, o) ← ic.mk_app `` One.one []
    let (ic, p) ← ic.mk_app `` zpow_zero [a]
    pure (ic, zc, nc, o, p)
  | Sum.inr tt => do
    let (zc, nc, b', hb) ← prove_nat_uncast zc nc b
    let (ic, c, h) ← prove_pow a na ic b'
    let (ic, p) ← ic.mk_app `` zpow_pos [a, b, b', c, hb, h]
    pure (ic, zc, nc, c, p)
#align norm_num.prove_zpow norm_num.prove_zpow

/-- Evaluates expressions of the form `a ^ b`, `monoid.npow a b` or `nat.pow a b`. -/
unsafe def eval_pow : expr → tactic (expr × expr)
  | q(@Pow.pow $(α) $(β) $(m) $(e₁) $(e₂)) => do
    let n₁ ← e₁.to_rat
    let c ← mk_instance_cache α
    match β with
      | q(ℕ) => do
        let (c, m') ← c `` Monoid.Pow []
        is_def_eq m m'
        Prod.snd <$> prove_pow e₁ n₁ c e₂
      | q(ℤ) => do
        let (c, m') ← c `` DivInvMonoid.Pow []
        is_def_eq m m'
        let zc ← mk_instance_cache q(ℤ)
        let nc ← mk_instance_cache q(ℕ)
        (Prod.snd ∘ Prod.snd ∘ Prod.snd) <$> prove_zpow c zc nc e₁ n₁ e₂
      | _ => failed
  | q(Monoid.npow $(e₁) $(e₂)) => do
    let n₁ ← e₁.to_rat
    let c ← infer_type e₁ >>= mk_instance_cache
    Prod.snd <$> prove_pow e₁ n₁ c e₂
  | q(DivInvMonoid.zpow $(e₁) $(e₂)) => do
    let n₁ ← e₁.to_rat
    let c ← infer_type e₁ >>= mk_instance_cache
    let zc ← mk_instance_cache q(ℤ)
    let nc ← mk_instance_cache q(ℕ)
    (Prod.snd ∘ Prod.snd ∘ Prod.snd) <$> prove_zpow c zc nc e₁ n₁ e₂
  | _ => failed
#align norm_num.eval_pow norm_num.eval_pow

/-- Given `⊢ p`, returns `(true, ⊢ p = true)`. -/
unsafe def true_intro (p : expr) : tactic (expr × expr) :=
  Prod.mk q(True) <$> mk_app `` eq_true [p]
#align norm_num.true_intro norm_num.true_intro

/-- Given `⊢ ¬ p`, returns `(false, ⊢ p = false)`. -/
unsafe def false_intro (p : expr) : tactic (expr × expr) :=
  Prod.mk q(False) <$> mk_app `` eq_false [p]
#align norm_num.false_intro norm_num.false_intro

theorem not_refl_false_intro {α} (a : α) : (a ≠ a) = False :=
  eq_false <| not_not_intro rfl
#align norm_num.not_refl_false_intro NormNum.not_refl_false_intro

-- see Note [nolint_ge]
@[nolint ge_or_gt]
theorem gt_intro {α} [LT α] (a b : α) (c) (h : (a < b) = c) : (b > a) = c :=
  h
#align norm_num.gt_intro NormNum.gt_intro

-- see Note [nolint_ge]
@[nolint ge_or_gt]
theorem ge_intro {α} [LE α] (a b : α) (c) (h : (a ≤ b) = c) : (b ≥ a) = c :=
  h
#align norm_num.ge_intro NormNum.ge_intro

-- failed to format: unknown constant 'term.pseudo.antiquot'
/-- Evaluates the inequality operations `=`,`<`,`>`,`≤`,`≥`,`≠` on numerals. -/ unsafe
  def
    eval_ineq
    : expr → tactic ( expr × expr )
    |
        q( $ ( e₁ ) < $ ( e₂ ) )
        =>
        do
          let n₁ ← e₁ . to_rat
            let n₂ ← e₂ . to_rat
            let c ← infer_type e₁ >>= mk_instance_cache
            if
              n₁ < n₂
              then
              do let ( _ , p ) ← prove_lt_rat c e₁ e₂ n₁ n₂ true_intro p
              else
              if
                n₁ = n₂
                then
                do let ( _ , p ) ← c ` ` lt_irrefl [ e₁ ] false_intro p
                else
                do
                  let ( c , p' ) ← prove_lt_rat c e₂ e₁ n₂ n₁
                    let ( _ , p ) ← c ` ` not_lt_of_gt [ e₁ , e₂ , p' ]
                    false_intro p
      |
        q( $ ( e₁ ) ≤ $ ( e₂ ) )
        =>
        do
          let n₁ ← e₁ . to_rat
            let n₂ ← e₂ . to_rat
            let c ← infer_type e₁ >>= mk_instance_cache
            if
              n₁ ≤ n₂
              then
              do
                let ( _ , p ) ← if n₁ = n₂ then c ` ` le_refl [ e₁ ] else prove_le_rat c e₁ e₂ n₁ n₂
                  true_intro p
              else
              do
                let ( c , p ) ← prove_lt_rat c e₂ e₁ n₂ n₁
                  let ( _ , p ) ← c ` ` not_le_of_gt [ e₁ , e₂ , p ]
                  false_intro p
      |
        q( $ ( e₁ ) = $ ( e₂ ) )
        =>
        do
          let n₁ ← e₁ . to_rat
            let n₂ ← e₂ . to_rat
            let c ← infer_type e₁ >>= mk_instance_cache
            if
              n₁ = n₂
              then
              mk_eq_refl e₁ >>= true_intro
              else
              do let ( _ , p ) ← prove_ne c e₁ e₂ n₁ n₂ false_intro p
      |
        q( $ ( e₁ ) > $ ( e₂ ) )
        =>
        do
          let ( e , p ) ← mk_app ` ` LT.lt [ e₂ , e₁ ] >>= eval_ineq
            Prod.mk e <$> mk_app ` ` gt_intro [ e₂ , e₁ , e , p ]
      |
        q( $ ( e₁ ) ≥ $ ( e₂ ) )
        =>
        do
          let ( e , p ) ← mk_app ` ` LE.le [ e₂ , e₁ ] >>= eval_ineq
            Prod.mk e <$> mk_app ` ` ge_intro [ e₂ , e₁ , e , p ]
      |
        q( $ ( e₁ ) ≠ $ ( e₂ ) )
        =>
        do
          let n₁ ← e₁ . to_rat
            let n₂ ← e₂ . to_rat
            let c ← infer_type e₁ >>= mk_instance_cache
            if
              n₁ = n₂
              then
              Prod.mk q( False ) <$> mk_app ` ` not_refl_false_intro [ e₁ ]
              else
              do let ( _ , p ) ← prove_ne c e₁ e₂ n₁ n₂ true_intro p
      | _ => failed
#align norm_num.eval_ineq norm_num.eval_ineq

theorem nat_succ_eq (a b c : ℕ) (h₁ : a = b) (h₂ : b + 1 = c) : Nat.succ a = c := by rwa [h₁]
#align norm_num.nat_succ_eq NormNum.nat_succ_eq

/-- Evaluates the expression `nat.succ ... (nat.succ n)` where `n` is a natural numeral.
(We could also just handle `nat.succ n` here and rely on `simp` to work bottom up, but we figure
that towers of successors coming from e.g. `induction` are a common case.) -/
unsafe def prove_nat_succ (ic : instance_cache) : expr → tactic (instance_cache × ℕ × expr × expr)
  | q(Nat.succ $(a)) => do
    let (ic, n, b, p₁) ← prove_nat_succ a
    let n' := n + 1
    let (ic, c) ← ic.ofNat n'
    let (ic, p₂) ← prove_add_nat ic b q(1) c
    return (ic, n', c, q(nat_succ_eq).mk_app [a, b, c, p₁, p₂])
  | e => do
    let n ← e.toNat
    let p ← mk_eq_refl e
    return (ic, n, e, p)
#align norm_num.prove_nat_succ norm_num.prove_nat_succ

theorem int_toNat_pos (a : ℤ) (b : ℕ)
    (h :
      (haveI := @Nat.castCoe ℤ
          b :
          ℤ) =
        a) :
    a.toNat = b := by rw [← h] <;> simp
#align norm_num.int_to_nat_pos NormNum.int_toNat_pos

theorem int_toNat_neg (a : ℤ) (h : 0 < a) : (-a).toNat = 0 := by
  simp only [Int.toNat_of_nonpos, h.le, neg_nonpos]
#align norm_num.int_to_nat_neg NormNum.int_toNat_neg

theorem natAbs_pos (a : ℤ) (b : ℕ)
    (h :
      (haveI := @Nat.castCoe ℤ
          b :
          ℤ) =
        a) :
    a.natAbs = b := by rw [← h] <;> simp
#align norm_num.nat_abs_pos NormNum.natAbs_pos

theorem natAbs_neg (a : ℤ) (b : ℕ)
    (h :
      (haveI := @Nat.castCoe ℤ
          b :
          ℤ) =
        a) :
    (-a).natAbs = b := by rw [← h] <;> simp
#align norm_num.nat_abs_neg NormNum.natAbs_neg

theorem negSucc (a b : ℕ) (c : ℤ) (h₁ : a + 1 = b)
    (h₂ :
      (haveI := @Nat.castCoe ℤ
          b :
          ℤ) =
        c) :
    -[a+1] = -c := by rw [← h₂, ← h₁] <;> rfl
#align norm_num.neg_succ_of_nat NormNum.negSucc

/-- Evaluates `nat.succ`, `int.to_nat`, `int.nat_abs`, `int.neg_succ_of_nat`. -/
unsafe def eval_nat_int : expr → tactic (expr × expr)
  | e@q(Nat.succ _) => do
    let ic ← mk_instance_cache q(ℕ)
    let (_, _, ep) ← prove_nat_succ ic e
    return ep
  | q(Int.toNat $(a)) => do
    let n ← a.to_int
    let ic ← mk_instance_cache q(ℤ)
    if n ≥ 0 then do
        let nc ← mk_instance_cache q(ℕ)
        let (_, _, b, p) ← prove_nat_uncast ic nc a
        pure (b, q(int_toNat_pos).mk_app [a, b, p])
      else do
        let a ← match_neg a
        let (_, p) ← prove_pos ic a
        pure (q(0), q(int_toNat_neg).mk_app [a, p])
  | q(Int.natAbs $(a)) => do
    let n ← a.to_int
    let ic ← mk_instance_cache q(ℤ)
    let nc ← mk_instance_cache q(ℕ)
    if n ≥ 0 then do
        let (_, _, b, p) ← prove_nat_uncast ic nc a
        pure (b, q(natAbs_pos).mk_app [a, b, p])
      else do
        let a ← match_neg a
        let (_, _, b, p) ← prove_nat_uncast ic nc a
        pure (b, q(natAbs_neg).mk_app [a, b, p])
  | q(Int.negSucc $(a)) => do
    let na ← a.toNat
    let ic ← mk_instance_cache q(ℤ)
    let nc ← mk_instance_cache q(ℕ)
    let nb := na + 1
    let (nc, b) ← nc.ofNat nb
    let (nc, p₁) ← prove_add_nat nc a q(1) b
    let (ic, c) ← ic.ofNat nb
    let (_, _, _, p₂) ← prove_nat_uncast ic nc c
    pure (q((-$(c) : ℤ)), q(negSucc).mk_app [a, b, c, p₁, p₂])
  | _ => failed
#align norm_num.eval_nat_int norm_num.eval_nat_int

theorem int_to_nat_cast (a : ℕ) (b : ℤ)
    (h :
      (haveI := @Nat.castCoe ℤ
          a :
          ℤ) =
        b) :
    ↑a = b :=
  Eq.trans (by simp) h
#align norm_num.int_to_nat_cast NormNum.int_to_nat_cast

/-- Evaluates the `↑n` cast operation from `ℕ`, `ℤ`, `ℚ` to an arbitrary type `α`. -/
unsafe def eval_cast : expr → tactic (expr × expr)
  | q(@coe ℕ $(α) $(inst) $(a)) => do
    if inst `` coeToLift then
        if inst `` Nat.castCoe then do
          let n ← a
          let ic ← mk_instance_cache α
          let nc ← mk_instance_cache q(ℕ)
          let (ic, b) ← ic n
          let (_, _, _, p) ← prove_nat_uncast ic nc b
          pure (b, p)
        else
          if inst `` Int.castCoe then do
            let n ← a
            let ic ← mk_instance_cache α
            let zc ← mk_instance_cache q(ℤ)
            let (ic, b) ← ic n
            let (_, _, _, p) ← prove_int_uncast ic zc b
            pure (b, p)
          else
            if inst `` Rat.castCoe then do
              let n ← a
              let cz_inst ← mk_mapp `` CharZero [α, none] >>= mk_instance
              let ic ← mk_instance_cache α
              let qc ← mk_instance_cache q(ℚ)
              let (ic, b) ← ic n
              let (_, _, _, p) ← prove_rat_uncast ic qc cz_inst b n
              pure (b, p)
            else failed
      else
        if inst = q(@coeBase Nat Int Int.hasCoe) then do
          let n ← a
          let ic ← mk_instance_cache q(ℤ)
          let nc ← mk_instance_cache q(ℕ)
          let (ic, b) ← ic n
          let (_, _, _, p) ← prove_nat_uncast ic nc b
          pure (b, q(int_to_nat_cast).mk_app [a, b, p])
        else failed
  | _ => failed
#align norm_num.eval_cast norm_num.eval_cast

/-- This version of `derive` does not fail when the input is already a numeral -/
unsafe def derive.step (e : expr) : tactic (expr × expr) :=
  eval_field e <|> eval_pow e <|> eval_ineq e <|> eval_cast e <|> eval_nat_int e
#align norm_num.derive.step norm_num.derive.step

/-- An attribute for adding additional extensions to `norm_num`. To use this attribute, put
`@[norm_num]` on a tactic of type `expr → tactic (expr × expr)`; the tactic will be called on
subterms by `norm_num`, and it is responsible for identifying that the expression is a numerical
function applied to numerals, for example `nat.fib 17`, and should return the reduced numerical
expression (which must be in `norm_num`-normal form: a natural or rational numeral, i.e. `37`,
`12 / 7` or `-(2 / 3)`, although this can be an expression in any type), and the proof that the
original expression is equal to the rewritten expression.

Failure is used to indicate that this tactic does not apply to the term. For performance reasons,
it is best to detect non-applicability as soon as possible so that the next tactic can have a go,
so generally it will start with a pattern match and then checking that the arguments to the term
are numerals or of the appropriate form, followed by proof construction, which should not fail.

Propositions are treated like any other term. The normal form for propositions is `true` or
`false`, so it should produce a proof of the form `p = true` or `p = false`. `eq_true_intro` can be
used to help here.
-/
@[user_attribute]
protected unsafe def attr : user_attribute (expr → tactic (expr × expr)) Unit
    where
  Name := `norm_num
  descr := "Add norm_num derivers"
  cache_cfg :=
    { mk_cache := fun ns => do
        let t ←
          ns.foldlM
              (fun (t : expr → tactic (expr × expr)) n => do
                let t' ← eval_expr (expr → tactic (expr × expr)) (expr.const n [])
                pure fun e => t' e <|> t e)
              fun _ => failed
        pure fun e => derive.step e <|> t e
      dependencies := [] }
#align norm_num.attr norm_num.attr

add_tactic_doc
  { Name := "norm_num"
    category := DocCategory.attr
    declNames := [`norm_num.attr]
    tags := ["arithmetic", "decision_procedure"] }

/-- Look up the `norm_num` extensions in the cache and return a tactic extending `derive.step` with
additional reduction procedures. -/
unsafe def get_step : tactic (expr → tactic (expr × expr)) :=
  norm_num.attr.get_cache
#align norm_num.get_step norm_num.get_step

/-- Simplify an expression bottom-up using `step` to simplify the subexpressions. -/
unsafe def derive' (step : expr → tactic (expr × expr)) : expr → tactic (expr × expr)
  | e => do
    let e ← instantiate_mvars e
    let (_, e', pr) ←
      ext_simplify_core () { } simp_lemmas.mk (fun _ => failed) (fun _ _ _ _ _ => failed)
          (fun _ _ _ _ e => do
            let (new_e, pr) ← step e
            guard ¬new_e == e
            pure ((), new_e, some pr, tt))
          `eq e
    pure (e', pr)
#align norm_num.derive' norm_num.derive'

/-- Simplify an expression bottom-up using the default `norm_num` set to simplify the
subexpressions. -/
unsafe def derive (e : expr) : tactic (expr × expr) := do
  let f ← get_step
  derive' f e
#align norm_num.derive norm_num.derive

end NormNum

/-- Basic version of `norm_num` that does not call `simp`. It uses the provided `step` tactic
to simplify the expression; use `get_step` to get the default `norm_num` set and `derive.step` for
the basic builtin set of simplifications. -/
unsafe def tactic.norm_num1 (step : expr → tactic (expr × expr)) (loc : Interactive.Loc) :
    tactic Unit := do
  let ns ← loc.get_locals
  let success ← tactic.replace_at (norm_num.derive' step) ns loc.include_goal
  when loc <| try tactic.triv
  when ¬ns <| try tactic.contradiction
  Monad.unlessb success <| done <|> fail "norm_num failed to simplify"
#align tactic.norm_num1 tactic.norm_num1

/-- Normalize numerical expressions. It uses the provided `step` tactic to simplify the expression;
use `get_step` to get the default `norm_num` set and `derive.step` for the basic builtin set of
simplifications. -/
unsafe def tactic.norm_num (step : expr → tactic (expr × expr)) (hs : List simp_arg_type)
    (l : Interactive.Loc) : tactic Unit :=
  repeat1 <|
    orelse' (tactic.norm_num1 step l) <|
      interactive.simp_core { } (tactic.norm_num1 step (Interactive.Loc.ns [none])) false
          (simp_arg_type.except `` one_div :: hs) [] l >>
        skip
#align tactic.norm_num tactic.norm_num

/-- Carry out similar operations as `tactic.norm_num` but on an `expr` rather than a location.
Given an expression `e`, returns `(e', ⊢ e = e')`.
The `no_dflt`, `hs`, and `attr_names` are passed on to `simp`.
Unlike `norm_num`, this tactic does not fail. -/
unsafe def _root_.expr.norm_num (step : expr → tactic (expr × expr)) (no_dflt : Bool := false)
    (hs : List simp_arg_type := []) (attr_names : List Name := []) : expr → tactic (expr × expr) :=
  let simp_step (e : expr) := do
    let (e', p, _) ←
      e.simp { } (tactic.norm_num1 step (Interactive.Loc.ns [none])) no_dflt attr_names
          (simp_arg_type.except `` one_div :: hs)
    return (e', p)
  or_refl_conv fun e => do
    let (e', p') ← norm_num.derive' step e <|> simp_step e
    let (e'', p'') ← _root_.expr.norm_num e'
    let p ← mk_eq_trans p' p''
    return (e'', p)
#align expr.norm_num expr.norm_num

namespace Tactic.Interactive

open NormNum Interactive Interactive.Types

/-- Basic version of `norm_num` that does not call `simp`. -/
unsafe def norm_num1 (loc : parse location) : tactic Unit := do
  let f ← get_step
  tactic.norm_num1 f loc
#align tactic.interactive.norm_num1 tactic.interactive.norm_num1

/-- Normalize numerical expressions. Supports the operations
`+` `-` `*` `/` `^` and `%` over numerical types such as
`ℕ`, `ℤ`, `ℚ`, `ℝ`, `ℂ` and some general algebraic types,
and can prove goals of the form `A = B`, `A ≠ B`, `A < B` and `A ≤ B`,
where `A` and `B` are numerical expressions.
It also has a relatively simple primality prover. -/
unsafe def norm_num (hs : parse simp_arg_list) (l : parse location) : tactic Unit := do
  let f ← get_step
  tactic.norm_num f hs l
#align tactic.interactive.norm_num tactic.interactive.norm_num

add_hint_tactic norm_num

/-- Normalizes a numerical expression and tries to close the goal with the result. -/
unsafe def apply_normed (x : parse texpr) : tactic Unit := do
  let x₁ ← to_expr x
  let (x₂, _) ← derive x₁
  tactic.exact x₂
#align tactic.interactive.apply_normed tactic.interactive.apply_normed

/-- Normalises numerical expressions. It supports the operations `+` `-` `*` `/` `^` and `%` over
numerical types such as `ℕ`, `ℤ`, `ℚ`, `ℝ`, `ℂ`, and can prove goals of the form `A = B`, `A ≠ B`,
`A < B` and `A ≤ B`, where `A` and `B` are numerical expressions.

Add-on tactics marked as `@[norm_num]` can extend the behavior of `norm_num` to include other
functions. This is used to support several other functions on `nat` like `prime`, `min_fac` and
`factors`.
```lean
import data.real.basic

example : (2 : ℝ) + 2 = 4 := by norm_num
example : (12345.2 : ℝ) ≠ 12345.3 := by norm_num
example : (73 : ℝ) < 789/2 := by norm_num
example : 123456789 + 987654321 = 1111111110 := by norm_num
example (R : Type*) [ring R] : (2 : R) + 2 = 4 := by norm_num
example (F : Type*) [linear_ordered_field F] : (2 : F) + 2 < 5 := by norm_num
example : nat.prime (2^13 - 1) := by norm_num
example : ¬ nat.prime (2^11 - 1) := by norm_num
example (x : ℝ) (h : x = 123 + 456) : x = 579 := by norm_num at h; assumption
```

The variant `norm_num1` does not call `simp`.

Both `norm_num` and `norm_num1` can be called inside the `conv` tactic.

The tactic `apply_normed` normalises a numerical expression and tries to close the goal with
the result. Compare:
```lean
def a : ℕ := 2^100
#print a -- 2 ^ 100

def normed_a : ℕ := by apply_normed 2^100
#print normed_a -- 1267650600228229401496703205376
```
-/
add_tactic_doc
  { Name := "norm_num"
    category := DocCategory.tactic
    declNames :=
      [`tactic.interactive.norm_num1, `tactic.interactive.norm_num,
        `tactic.interactive.apply_normed]
    tags := ["arithmetic", "decision procedure"] }

end Tactic.Interactive

/-! ## `conv` tactic -/

namespace Conv.Interactive

open Conv Interactive Tactic.Interactive

open NormNum (derive)

/-- Basic version of `norm_num` that does not call `simp`. -/
unsafe def norm_num1 : conv Unit :=
  replace_lhs derive
#align conv.interactive.norm_num1 conv.interactive.norm_num1

/-- Normalize numerical expressions. Supports the operations
`+` `-` `*` `/` `^` and `%` over numerical types such as
`ℕ`, `ℤ`, `ℚ`, `ℝ`, `ℂ` and some general algebraic types,
and can prove goals of the form `A = B`, `A ≠ B`, `A < B` and `A ≤ B`,
where `A` and `B` are numerical expressions.
It also has a relatively simple primality prover. -/
unsafe def norm_num (hs : parse simp_arg_list) : conv Unit :=
  repeat1 <|
    orelse' norm_num1 <|
      conv.interactive.simp false (simp_arg_type.except `` one_div :: hs) []
        { discharger := tactic.interactive.norm_num1 (Loc.ns [none]) }
#align conv.interactive.norm_num conv.interactive.norm_num

end Conv.Interactive

/-!
## `#norm_num` command
A user command to run `norm_num`. Mostly copied from the `#simp` command.
-/

namespace Tactic

/- ./././Mathport/Syntax/Translate/Tactic/Mathlib/Core.lean:38:34: unsupported: setup_tactic_parser -/
-- With this option, turn off the messages if the result is exactly `true`
initialize
  registerTraceClass.1 `silence_norm_num_if_true

/-- The basic usage is `#norm_num e`, where `e` is an expression,
which will print the `norm_num` form of `e`.

Syntax: `#norm_num` (`only`)? (`[` simp lemma list `]`)? (`with` simp sets)? `:`? expression

This accepts the same options as the `#simp` command.
You can specify additional simp lemmas as usual, for example using
`#norm_num [f, g] : e`, or `#norm_num with attr : e`.
(The colon is optional but helpful for the parser.)
The `only` restricts `norm_num` to using only the provided lemmas, and so
`#norm_num only : e` behaves similarly to `norm_num1`.

Unlike `norm_num`, this command does not fail when no simplifications are made.

`#norm_num` understands local variables, so you can use them to
introduce parameters.
-/
@[user_command]
unsafe def norm_num_cmd (_ : parse <| tk "#norm_num") : lean.parser Unit := do
  let no_dflt ← only_flag
  let hs ← simp_arg_list
  let attr_names ← with_ident_list
  let o ← optional (tk ":")
  let e ← texpr
  let-- Retrieve the `pexpr`s parsed as part of the simp args, and collate them into a big list.
  hs_es := List.join <| hs.map <| Option.toList ∘ simp_arg_type.to_pexpr
  let/- Synthesize a `tactic_state` including local variables as hypotheses under which `expr.simp`
         may be safely called with expected behaviour given the `variables` in the environment. -/
    (ts, mappings)
    ← synthesize_tactic_state_with_variables_as_hyps (e :: hs_es)
  let result
    ←-- Enter the `tactic` monad, *critically* using the synthesized tactic state `ts`.
        lean.parser.of_tactic
        fun _ =>
        (/- Resolve the local variables added by the parser to `e` (when it was parsed) against the local
                 hypotheses added to the `ts : tactic_state` which we are using. -/
          do
            let e ← to_expr e
            let/- Replace the variables referenced in the passed `simp_arg_list` with the `expr`s corresponding
                   to the local hypotheses we created.

                   We would prefer to just elaborate the `pexpr`s encoded in the `simp_arg_list` against the
                   tactic state we have created (as we could with `e` above), but the simplifier expects
                   `pexpr`s and not `expr`s. Thus, we just modify the `pexpr`s now and let `simp` do the
                   elaboration when the time comes.

                   You might think that we could just examine each of these `pexpr`s, call `to_expr` on them,
                   and then call `to_pexpr` afterward and save the results over the original `pexprs`. Due to
                   how functions like `simp_lemmas.add_pexpr` are implemented in the core library, the `simp`
                   framework is not robust enough to handle this method. When pieces of expressions like
                   annotation macros are injected, the direct patten matches in the `simp_lemmas.*` codebase
                   fail, and the lemmas we want don't get added.
                   -/
            hs := hs.map fun sat => sat.replace_subexprs mappings
            let step
              ←-- Try simplifying the expression.
                norm_num.get_step
            Prod.fst <$> e step no_dflt hs attr_names)
          ts
  -- Trace the result.
      when
      (¬is_trace_enabled_for `silence_norm_num_if_true ∨ result ≠ expr.const `true [])
      (trace result)
#align tactic.norm_num_cmd tactic.norm_num_cmd

add_tactic_doc
  { Name := "#norm_num"
    category := DocCategory.cmd
    declNames := [`tactic.norm_num_cmd]
    tags := ["simplification", "arithmetic", "decision procedure"] }

end Tactic

namespace NormNum

section ElementaryNumberTheory

open Tactic

theorem nat_div (a b q r m : ℕ) (hm : q * b = m) (h : r + m = a) (h₂ : r < b) : a / b = q := by
  rw [← h, ← hm, Nat.add_mul_div_right _ _ (lt_of_le_of_lt (Nat.zero_le _) h₂), Nat.div_eq_of_lt h₂,
    zero_add]
#align norm_num.nat_div NormNum.nat_div

theorem int_div (a b q r m : ℤ) (hm : q * b = m) (h : r + m = a) (h₁ : 0 ≤ r) (h₂ : r < b) :
    a / b = q := by
  rw [← h, ← hm, Int.add_mul_ediv_right _ _ (ne_of_gt (lt_of_le_of_lt h₁ h₂)),
    Int.div_eq_zero_of_lt h₁ h₂, zero_add]
#align norm_num.int_div NormNum.int_div

theorem nat_mod (a b q r m : ℕ) (hm : q * b = m) (h : r + m = a) (h₂ : r < b) : a % b = r := by
  rw [← h, ← hm, Nat.add_mul_mod_self_right, Nat.mod_eq_of_lt h₂]
#align norm_num.nat_mod NormNum.nat_mod

theorem int_mod (a b q r m : ℤ) (hm : q * b = m) (h : r + m = a) (h₁ : 0 ≤ r) (h₂ : r < b) :
    a % b = r := by rw [← h, ← hm, Int.add_mul_emod_self, Int.emod_eq_of_lt h₁ h₂]
#align norm_num.int_mod NormNum.int_mod

theorem int_div_neg (a b c' c : ℤ) (h : a / b = c') (h₂ : -c' = c) : a / -b = c :=
  h₂ ▸ h ▸ Int.div_neg _ _
#align norm_num.int_div_neg NormNum.int_div_neg

theorem int_mod_neg (a b c : ℤ) (h : a % b = c) : a % -b = c :=
  (Int.mod_neg _ _).trans h
#align norm_num.int_mod_neg NormNum.int_mod_neg

/-- Given `a`,`b` numerals in `nat` or `int`,
  * `prove_div_mod ic a b ff` returns `(c, ⊢ a / b = c)`
  * `prove_div_mod ic a b tt` returns `(c, ⊢ a % b = c)`
-/
unsafe def prove_div_mod (ic : instance_cache) :
    expr → expr → Bool → tactic (instance_cache × expr × expr)
  | a, b, mod =>
    match match_neg b with
    | some b => do
      let (ic, c', p) ← prove_div_mod a b mod
      if mod then return (ic, c', q(int_mod_neg).mk_app [a, b, c', p])
        else do
          let (ic, c, p₂) ← prove_neg ic c'
          return (ic, c, q(int_div_neg).mk_app [a, b, c', c, p, p₂])
    | none => do
      let nb ← b.toNat
      let na ← a.to_int
      let nq := na / nb
      let nr := na % nb
      let nm := nq * nr
      let (ic, q) ← ic.ofInt nq
      let (ic, r) ← ic.ofInt nr
      let (ic, m, pm) ← prove_mul_rat ic q b nq nb
      let (ic, a') ← ic.of_rat na
      let-- ensure `a` is in normal form
        (ic, p)
        ← prove_add_rat ic r m a' nr nm na
      let (ic, p') ← prove_lt_nat ic r b
      if ic = q(Nat) then
          if mod then return (ic, r, q(nat_mod).mk_app [a, b, q, r, m, pm, p, p'])
          else return (ic, q, q(nat_div).mk_app [a, b, q, r, m, pm, p, p'])
        else
          if ic = q(Int) then do
            let (ic, p₀) ← prove_nonneg ic r
            if mod then return (ic, r, q(int_mod).mk_app [a, b, q, r, m, pm, p, p₀, p'])
              else return (ic, q, q(int_div).mk_app [a, b, q, r, m, pm, p, p₀, p'])
          else failed
#align norm_num.prove_div_mod norm_num.prove_div_mod

theorem dvd_eq_nat (a b c : ℕ) (p) (h₁ : b % a = c) (h₂ : (c = 0) = p) : (a ∣ b) = p :=
  (propext <| by rw [← h₁, Nat.dvd_iff_mod_eq_zero]).trans h₂
#align norm_num.dvd_eq_nat NormNum.dvd_eq_nat

theorem dvd_eq_int (a b c : ℤ) (p) (h₁ : b % a = c) (h₂ : (c = 0) = p) : (a ∣ b) = p :=
  (propext <| by rw [← h₁, Int.dvd_iff_emod_eq_zero]).trans h₂
#align norm_num.dvd_eq_int NormNum.dvd_eq_int

-- failed to format: unknown constant 'term.pseudo.antiquot'
/--
      Evaluates some extra numeric operations on `nat` and `int`, specifically
      `/` and `%`, and `∣` (divisibility). -/
    @[ norm_num ]
    unsafe
  def
    eval_nat_int_ext
    : expr → tactic ( expr × expr )
    |
        q( $ ( a ) / $ ( b ) )
        =>
        do let c ← infer_type a >>= mk_instance_cache Prod.snd <$> prove_div_mod c a b ff
      |
        q( $ ( a ) % $ ( b ) )
        =>
        do let c ← infer_type a >>= mk_instance_cache Prod.snd <$> prove_div_mod c a b tt
      |
        q( $ ( a ) ∣ $ ( b ) )
        =>
        do
          let α ← infer_type a
            let ic ← mk_instance_cache α
            let
              th
                ←
                if
                  α = q( Nat )
                  then
                  return ( q( dvd_eq_nat ) : expr )
                  else
                  if α = q( Int ) then return q( dvd_eq_int ) else failed
            let ( ic , c , p₁ ) ← prove_div_mod ic b a true
            let ( ic , z ) ← ic . mk_app ` ` Zero.zero [ ]
            let ( e' , p₂ ) ← mk_app ` ` Eq [ c , z ] >>= eval_ineq
            return ( e' , th [ a , b , c , e' , p₁ , p₂ ] )
      | _ => failed
#align norm_num.eval_nat_int_ext norm_num.eval_nat_int_ext

end ElementaryNumberTheory

end NormNum