l3lab/ntp-mathlib huggingface dataset:
/-- Two lists of sublists are equal iff their joins coincide, as well as the lengths of the
sublists. -/
theorem eq_iff_join_eq (L L' : List (List α)) :
    L = L' ↔ L.join = L'.join ∧ map length L = map length L' := by
  refine' ⟨fun H => by simp [H], _⟩
  rintro ⟨join_eq, length_eq⟩
  

Proof state:
case intro
α : Type u_1
β : Type u_2
L L' : List (List α)
join_eq : join L = join L'
length_eq : map length L = map length L'
⊢ L = L'

Next tactic:
apply ext_get
/-- Two lists of sublists are equal iff their joins coincide, as well as the lengths of the
sublists. -/
theorem eq_iff_join_eq (L L' : List (List α)) :
    L = L' ↔ L.join = L'.join ∧ map length L = map length L' := by
  refine' ⟨fun H => by simp [H], _⟩
  rintro ⟨join_eq, length_eq⟩
  apply ext_get
  · 

Proof state:
case intro.hl
α : Type u_1
β : Type u_2
L L' : List (List α)
join_eq : join L = join L'
length_eq : map length L = map length L'
⊢ length L = length L'

Next tactic:
have : length (map length L) = length (map length L') := by rw [length_eq]
/-- Two lists of sublists are equal iff their joins coincide, as well as the lengths of the
sublists. -/
theorem eq_iff_join_eq (L L' : List (List α)) :
    L = L' ↔ L.join = L'.join ∧ map length L = map length L' := by
  refine' ⟨fun H => by simp [H], _⟩
  rintro ⟨join_eq, length_eq⟩
  apply ext_get
  · have : length (map length L) = length (map length L') := by 

Proof state:
α : Type u_1
β : Type u_2
L L' : List (List α)
join_eq : join L = join L'
length_eq : map length L = map length L'
⊢ length (map length L) = length (map length L')

Next tactic:
rw [length_eq]
/-- Two lists of sublists are equal iff their joins coincide, as well as the lengths of the
sublists. -/
theorem eq_iff_join_eq (L L' : List (List α)) :
    L = L' ↔ L.join = L'.join ∧ map length L = map length L' := by
  refine' ⟨fun H => by simp [H], _⟩
  rintro ⟨join_eq, length_eq⟩
  apply ext_get
  · have : length (map length L) = length (map length L') := by rw [length_eq]
    

Proof state:
case intro.hl
α : Type u_1
β : Type u_2
L L' : List (List α)
join_eq : join L = join L'
length_eq : map length L = map length L'
this : length (map length L) = length (map length L')
⊢ length L = length L'

Next tactic:
simpa using this
/-- Two lists of sublists are equal iff their joins coincide, as well as the lengths of the
sublists. -/
theorem eq_iff_join_eq (L L' : List (List α)) :
    L = L' ↔ L.join = L'.join ∧ map length L = map length L' := by
  refine' ⟨fun H => by simp [H], _⟩
  rintro ⟨join_eq, length_eq⟩
  apply ext_get
  · have : length (map length L) = length (map length L') := by rw [length_eq]
    simpa using this
  · 

Proof state:
case intro.h
α : Type u_1
β : Type u_2
L L' : List (List α)
join_eq : join L = join L'
length_eq : map length L = map length L'
⊢ ∀ (n : ℕ) (h₁ : n < length L) (h₂ : n < length L'), get L { val := n, isLt := h₁ } = get L' { val := n, isLt := h₂ }

Next tactic:
intro n h₁ h₂
/-- Two lists of sublists are equal iff their joins coincide, as well as the lengths of the
sublists. -/
theorem eq_iff_join_eq (L L' : List (List α)) :
    L = L' ↔ L.join = L'.join ∧ map length L = map length L' := by
  refine' ⟨fun H => by simp [H], _⟩
  rintro ⟨join_eq, length_eq⟩
  apply ext_get
  · have : length (map length L) = length (map length L') := by rw [length_eq]
    simpa using this
  · intro n h₁ h₂
    

Proof state:
case intro.h
α : Type u_1
β : Type u_2
L L' : List (List α)
join_eq : join L = join L'
length_eq : map length L = map length L'
n : ℕ
h₁ : n < length L
h₂ : n < length L'
⊢ get L { val := n, isLt := h₁ } = get L' { val := n, isLt := h₂ }

Next tactic:
rw [← drop_take_succ_join_eq_get, ← drop_take_succ_join_eq_get, join_eq, length_eq]
theorem join_drop_length_sub_one {L : List (List α)} (h : L ≠ []) :
    (L.drop (L.length - 1)).join = L.getLast h := by
  

Proof state:
α : Type u_1
β : Type u_2
L : List (List α)
h : L ≠ []
⊢ join (drop (length L - 1) L) = getLast L h

Next tactic:
induction L using List.reverseRecOn
theorem join_drop_length_sub_one {L : List (List α)} (h : L ≠ []) :
    (L.drop (L.length - 1)).join = L.getLast h := by
  induction L using List.reverseRecOn
  · 

Proof state:
case H0
α : Type u_1
β : Type u_2
h : [] ≠ []
⊢ join (drop (length [] - 1) []) = getLast [] h

Next tactic:
cases h rfl
theorem join_drop_length_sub_one {L : List (List α)} (h : L ≠ []) :
    (L.drop (L.length - 1)).join = L.getLast h := by
  induction L using List.reverseRecOn
  · cases h rfl
  · 

Proof state:
case H1
α : Type u_1
β : Type u_2
l✝ : List (List α)
a✝¹ : List α
a✝ : ∀ (h : l✝ ≠ []), join (drop (length l✝ - 1) l✝) = getLast l✝ h
h : l✝ ++ [a✝¹] ≠ []
⊢ join (drop (length (l✝ ++ [a✝¹]) - 1) (l✝ ++ [a✝¹])) = getLast (l✝ ++ [a✝¹]) h

Next tactic:
simp
/-- We can rebracket `x ++ (l₁ ++ x) ++ (l₂ ++ x) ++ ... ++ (lₙ ++ x)` to
`(x ++ l₁) ++ (x ++ l₂) ++ ... ++ (x ++ lₙ) ++ x` where `L = [l₁, l₂, ..., lₙ]`. -/
theorem append_join_map_append (L : List (List α)) (x : List α) :
    x ++ (List.map (fun l => l ++ x) L).join = (List.map (fun l => x ++ l) L).join ++ x := by
  

Proof state:
α : Type u_1
β : Type u_2
L : List (List α)
x : List α
⊢ x ++ join (map (fun l => l ++ x) L) = join (map (fun l => x ++ l) L) ++ x

Next tactic:
induction' L with _ _ ih
/-- We can rebracket `x ++ (l₁ ++ x) ++ (l₂ ++ x) ++ ... ++ (lₙ ++ x)` to
`(x ++ l₁) ++ (x ++ l₂) ++ ... ++ (x ++ lₙ) ++ x` where `L = [l₁, l₂, ..., lₙ]`. -/
theorem append_join_map_append (L : List (List α)) (x : List α) :
    x ++ (List.map (fun l => l ++ x) L).join = (List.map (fun l => x ++ l) L).join ++ x := by
  induction' L with _ _ ih
  · 

Proof state:
case nil
α : Type u_1
β : Type u_2
x : List α
⊢ x ++ join (map (fun l => l ++ x) []) = join (map (fun l => x ++ l) []) ++ x

Next tactic:
rw [map_nil, join, append_nil, map_nil, join, nil_append]
/-- We can rebracket `x ++ (l₁ ++ x) ++ (l₂ ++ x) ++ ... ++ (lₙ ++ x)` to
`(x ++ l₁) ++ (x ++ l₂) ++ ... ++ (x ++ lₙ) ++ x` where `L = [l₁, l₂, ..., lₙ]`. -/
theorem append_join_map_append (L : List (List α)) (x : List α) :
    x ++ (List.map (fun l => l ++ x) L).join = (List.map (fun l => x ++ l) L).join ++ x := by
  induction' L with _ _ ih
  · rw [map_nil, join, append_nil, map_nil, join, nil_append]
  · 

Proof state:
case cons
α : Type u_1
β : Type u_2
x head✝ : List α
tail✝ : List (List α)
ih : x ++ join (map (fun l => l ++ x) tail✝) = join (map (fun l => x ++ l) tail✝) ++ x
⊢ x ++ join (map (fun l => l ++ x) (head✝ :: tail✝)) = join (map (fun l => x ++ l) (head✝ :: tail✝)) ++ x

Next tactic:
rw [map_cons, join, map_cons, join, append_assoc, ih, append_assoc, append_assoc]
/-- Reversing a join is the same as reversing the order of parts and reversing all parts. -/
theorem reverse_join (L : List (List α)) :
    L.join.reverse = (List.map List.reverse L).reverse.join := by
  

Proof state:
α : Type u_1
β : Type u_2
L : List (List α)
⊢ reverse (join L) = join (reverse (map reverse L))

Next tactic:
induction' L with _ _ ih
/-- Reversing a join is the same as reversing the order of parts and reversing all parts. -/
theorem reverse_join (L : List (List α)) :
    L.join.reverse = (List.map List.reverse L).reverse.join := by
  induction' L with _ _ ih
  · 

Proof state:
case nil
α : Type u_1
β : Type u_2
⊢ reverse (join []) = join (reverse (map reverse []))

Next tactic:
rfl
/-- Reversing a join is the same as reversing the order of parts and reversing all parts. -/
theorem reverse_join (L : List (List α)) :
    L.join.reverse = (List.map List.reverse L).reverse.join := by
  induction' L with _ _ ih
  · rfl
  · 

Proof state:
case cons
α : Type u_1
β : Type u_2
head✝ : List α
tail✝ : List (List α)
ih : reverse (join tail✝) = join (reverse (map reverse tail✝))
⊢ reverse (join (head✝ :: tail✝)) = join (reverse (map reverse (head✝ :: tail✝)))

Next tactic:
rw [join, reverse_append, ih, map_cons, reverse_cons', join_concat]
/-- Joining a reverse is the same as reversing all parts and reversing the joined result. -/
theorem join_reverse (L : List (List α)) :
    L.reverse.join = (List.map List.reverse L).join.reverse := by
  

Proof state:
α : Type u_1
β : Type u_2
L : List (List α)
⊢ join (reverse L) = reverse (join (map reverse L))

Next tactic:
simpa [reverse_reverse, map_reverse] using congr_arg List.reverse (reverse_join L.reverse)
/-- Any member of `l : List (List α))` is a sublist of `l.join` -/
lemma sublist_join (l : List (List α)) {s : List α} (hs : s ∈ l) :
    List.Sublist s (l.join) := by
  

Proof state:
α : Type u_1
β : Type u_2
l : List (List α)
s : List α
hs : s ∈ l
⊢ s <+ join l

Next tactic:
induction l with
  | nil =>
    exfalso
    exact not_mem_nil s hs
  | cons t m ht =>
    cases mem_cons.mp hs with
    | inl h =>
      rw [h]
      simp only [join_cons, sublist_append_left]
    | inr h =>
      simp only [join_cons]
      exact sublist_append_of_sublist_right (ht h)
/-- Any member of `l : List (List α))` is a sublist of `l.join` -/
lemma sublist_join (l : List (List α)) {s : List α} (hs : s ∈ l) :
    List.Sublist s (l.join) := by
  induction l with
  

Proof state:
case nil
α : Type u_1
β : Type u_2
s : List α
hs : s ∈ []
⊢ s <+ join []

Next tactic:
| nil =>
    exfalso
    exact not_mem_nil s hs
/-- Any member of `l : List (List α))` is a sublist of `l.join` -/
lemma sublist_join (l : List (List α)) {s : List α} (hs : s ∈ l) :
    List.Sublist s (l.join) := by
  induction l with
  | nil =>
    

Proof state:
case nil
α : Type u_1
β : Type u_2
s : List α
hs : s ∈ []
⊢ s <+ join []

Next tactic:
exfalso
/-- Any member of `l : List (List α))` is a sublist of `l.join` -/
lemma sublist_join (l : List (List α)) {s : List α} (hs : s ∈ l) :
    List.Sublist s (l.join) := by
  induction l with
  | nil =>
    exfalso
    

Proof state:
case nil.h
α : Type u_1
β : Type u_2
s : List α
hs : s ∈ []
⊢ False

Next tactic:
exact not_mem_nil s hs
/-- Any member of `l : List (List α))` is a sublist of `l.join` -/
lemma sublist_join (l : List (List α)) {s : List α} (hs : s ∈ l) :
    List.Sublist s (l.join) := by
  induction l with
  | nil =>
    exfalso
    exact not_mem_nil s hs
  

Proof state:
case cons
α : Type u_1
β : Type u_2
s t : List α
m : List (List α)
ht : s ∈ m → s <+ join m
hs : s ∈ t :: m
⊢ s <+ join (t :: m)

Next tactic:
| cons t m ht =>
    cases mem_cons.mp hs with
    | inl h =>
      rw [h]
      simp only [join_cons, sublist_append_left]
    | inr h =>
      simp only [join_cons]
      exact sublist_append_of_sublist_right (ht h)
/-- Any member of `l : List (List α))` is a sublist of `l.join` -/
lemma sublist_join (l : List (List α)) {s : List α} (hs : s ∈ l) :
    List.Sublist s (l.join) := by
  induction l with
  | nil =>
    exfalso
    exact not_mem_nil s hs
  | cons t m ht =>
    

Proof state:
case cons
α : Type u_1
β : Type u_2
s t : List α
m : List (List α)
ht : s ∈ m → s <+ join m
hs : s ∈ t :: m
⊢ s <+ join (t :: m)

Next tactic:
cases mem_cons.mp hs with
    | inl h =>
      rw [h]
      simp only [join_cons, sublist_append_left]
    | inr h =>
      simp only [join_cons]
      exact sublist_append_of_sublist_right (ht h)
/-- Any member of `l : List (List α))` is a sublist of `l.join` -/
lemma sublist_join (l : List (List α)) {s : List α} (hs : s ∈ l) :
    List.Sublist s (l.join) := by
  induction l with
  | nil =>
    exfalso
    exact not_mem_nil s hs
  | cons t m ht =>
    

Proof state:
case cons
α : Type u_1
β : Type u_2
s t : List α
m : List (List α)
ht : s ∈ m → s <+ join m
hs : s ∈ t :: m
x✝ : s = t ∨ s ∈ m
⊢ s <+ join (t :: m)

Next tactic:
cases mem_cons.mp hs with
    | inl h =>
      rw [h]
      simp only [join_cons, sublist_append_left]
    | inr h =>
      simp only [join_cons]
      exact sublist_append_of_sublist_right (ht h)
/-- Any member of `l : List (List α))` is a sublist of `l.join` -/
lemma sublist_join (l : List (List α)) {s : List α} (hs : s ∈ l) :
    List.Sublist s (l.join) := by
  induction l with
  | nil =>
    exfalso
    exact not_mem_nil s hs
  | cons t m ht =>
    cases mem_cons.mp hs with
    

Proof state:
case cons.inl
α : Type u_1
β : Type u_2
s t : List α
m : List (List α)
ht : s ∈ m → s <+ join m
hs : s ∈ t :: m
h : s = t
⊢ s <+ join (t :: m)

Next tactic:
| inl h =>
      rw [h]
      simp only [join_cons, sublist_append_left]
/-- Any member of `l : List (List α))` is a sublist of `l.join` -/
lemma sublist_join (l : List (List α)) {s : List α} (hs : s ∈ l) :
    List.Sublist s (l.join) := by
  induction l with
  | nil =>
    exfalso
    exact not_mem_nil s hs
  | cons t m ht =>
    cases mem_cons.mp hs with
    | inl h =>
      

Proof state:
case cons.inl
α : Type u_1
β : Type u_2
s t : List α
m : List (List α)
ht : s ∈ m → s <+ join m
hs : s ∈ t :: m
h : s = t
⊢ s <+ join (t :: m)

Next tactic:
rw [h]
/-- Any member of `l : List (List α))` is a sublist of `l.join` -/
lemma sublist_join (l : List (List α)) {s : List α} (hs : s ∈ l) :
    List.Sublist s (l.join) := by
  induction l with
  | nil =>
    exfalso
    exact not_mem_nil s hs
  | cons t m ht =>
    cases mem_cons.mp hs with
    | inl h =>
      rw [h]
      

Proof state:
case cons.inl
α : Type u_1
β : Type u_2
s t : List α
m : List (List α)
ht : s ∈ m → s <+ join m
hs : s ∈ t :: m
h : s = t
⊢ t <+ join (t :: m)

Next tactic:
simp only [join_cons, sublist_append_left]
/-- Any member of `l : List (List α))` is a sublist of `l.join` -/
lemma sublist_join (l : List (List α)) {s : List α} (hs : s ∈ l) :
    List.Sublist s (l.join) := by
  induction l with
  | nil =>
    exfalso
    exact not_mem_nil s hs
  | cons t m ht =>
    cases mem_cons.mp hs with
    | inl h =>
      rw [h]
      simp only [join_cons, sublist_append_left]
    

Proof state:
case cons.inr
α : Type u_1
β : Type u_2
s t : List α
m : List (List α)
ht : s ∈ m → s <+ join m
hs : s ∈ t :: m
h : s ∈ m
⊢ s <+ join (t :: m)

Next tactic:
| inr h =>
      simp only [join_cons]
      exact sublist_append_of_sublist_right (ht h)
/-- Any member of `l : List (List α))` is a sublist of `l.join` -/
lemma sublist_join (l : List (List α)) {s : List α} (hs : s ∈ l) :
    List.Sublist s (l.join) := by
  induction l with
  | nil =>
    exfalso
    exact not_mem_nil s hs
  | cons t m ht =>
    cases mem_cons.mp hs with
    | inl h =>
      rw [h]
      simp only [join_cons, sublist_append_left]
    | inr h =>
      

Proof state:
case cons.inr
α : Type u_1
β : Type u_2
s t : List α
m : List (List α)
ht : s ∈ m → s <+ join m
hs : s ∈ t :: m
h : s ∈ m
⊢ s <+ join (t :: m)

Next tactic:
simp only [join_cons]
/-- Any member of `l : List (List α))` is a sublist of `l.join` -/
lemma sublist_join (l : List (List α)) {s : List α} (hs : s ∈ l) :
    List.Sublist s (l.join) := by
  induction l with
  | nil =>
    exfalso
    exact not_mem_nil s hs
  | cons t m ht =>
    cases mem_cons.mp hs with
    | inl h =>
      rw [h]
      simp only [join_cons, sublist_append_left]
    | inr h =>
      simp only [join_cons]
      

Proof state:
case cons.inr
α : Type u_1
β : Type u_2
s t : List α
m : List (List α)
ht : s ∈ m → s <+ join m
hs : s ∈ t :: m
h : s ∈ m
⊢ s <+ t ++ join m

Next tactic:
exact sublist_append_of_sublist_right (ht h)
theorem modEq_zero_iff_dvd : a ≡ 0 [MOD n] ↔ n ∣ a := by 

Proof state:
m n a b c d : ℕ
⊢ a ≡ 0 [MOD n] ↔ n ∣ a

Next tactic:
rw [ModEq, zero_mod, dvd_iff_mod_eq_zero]
theorem modEq_iff_dvd : a ≡ b [MOD n] ↔ (n : ℤ) ∣ b - a := by
  

Proof state:
m n a b c d : ℕ
⊢ a ≡ b [MOD n] ↔ ↑n ∣ ↑b - ↑a

Next tactic:
rw [ModEq, eq_comm, ← Int.coe_nat_inj', Int.coe_nat_mod, Int.coe_nat_mod,
    Int.emod_eq_emod_iff_emod_sub_eq_zero, Int.dvd_iff_emod_eq_zero]
/-- A variant of `modEq_iff_dvd` with `Nat` divisibility -/
theorem modEq_iff_dvd' (h : a ≤ b) : a ≡ b [MOD n] ↔ n ∣ b - a := by
  

Proof state:
m n a b c d : ℕ
h : a ≤ b
⊢ a ≡ b [MOD n] ↔ n ∣ b - a

Next tactic:
rw [modEq_iff_dvd, ← Int.coe_nat_dvd, Int.ofNat_sub h]
protected theorem mul_left' (c : ℕ) (h : a ≡ b [MOD n]) : c * a ≡ c * b [MOD c * n] := by
  

Proof state:
m n a b c✝ d c : ℕ
h : a ≡ b [MOD n]
⊢ c * a ≡ c * b [MOD c * n]

Next tactic:
unfold ModEq at *
protected theorem mul_left' (c : ℕ) (h : a ≡ b [MOD n]) : c * a ≡ c * b [MOD c * n] := by
  unfold ModEq at *; 

Proof state:
m n a b c✝ d c : ℕ
h : a % n = b % n
⊢ c * a % (c * n) = c * b % (c * n)

Next tactic:
rw [mul_mod_mul_left, mul_mod_mul_left, h]
protected theorem mul_right' (c : ℕ) (h : a ≡ b [MOD n]) : a * c ≡ b * c [MOD n * c] := by
  

Proof state:
m n a b c✝ d c : ℕ
h : a ≡ b [MOD n]
⊢ a * c ≡ b * c [MOD n * c]

Next tactic:
rw [mul_comm a, mul_comm b, mul_comm n]
protected theorem mul_right' (c : ℕ) (h : a ≡ b [MOD n]) : a * c ≡ b * c [MOD n * c] := by
  rw [mul_comm a, mul_comm b, mul_comm n]; 

Proof state:
m n a b c✝ d c : ℕ
h : a ≡ b [MOD n]
⊢ c * a ≡ c * b [MOD c * n]

Next tactic:
exact h.mul_left' c
@[gcongr]
protected theorem mul_right (c : ℕ) (h : a ≡ b [MOD n]) : a * c ≡ b * c [MOD n] := by
  

Proof state:
m n a b c✝ d c : ℕ
h : a ≡ b [MOD n]
⊢ a * c ≡ b * c [MOD n]

Next tactic:
rw [mul_comm a, mul_comm b]
@[gcongr]
protected theorem mul_right (c : ℕ) (h : a ≡ b [MOD n]) : a * c ≡ b * c [MOD n] := by
  rw [mul_comm a, mul_comm b]; 

Proof state:
m n a b c✝ d c : ℕ
h : a ≡ b [MOD n]
⊢ c * a ≡ c * b [MOD n]

Next tactic:
exact h.mul_left c
@[gcongr]
protected theorem pow (m : ℕ) (h : a ≡ b [MOD n]) : a ^ m ≡ b ^ m [MOD n] := by
  

Proof state:
m✝ n a b c d m : ℕ
h : a ≡ b [MOD n]
⊢ a ^ m ≡ b ^ m [MOD n]

Next tactic:
induction m with
  | zero => rfl
  | succ d hd =>
    rw[pow_succ, pow_succ]
    exact hd.mul h
@[gcongr]
protected theorem pow (m : ℕ) (h : a ≡ b [MOD n]) : a ^ m ≡ b ^ m [MOD n] := by
  induction m with
  

Proof state:
case zero
m n a b c d : ℕ
h : a ≡ b [MOD n]
⊢ a ^ zero ≡ b ^ zero [MOD n]

Next tactic:
| zero => rfl
@[gcongr]
protected theorem pow (m : ℕ) (h : a ≡ b [MOD n]) : a ^ m ≡ b ^ m [MOD n] := by
  induction m with
  | zero => 

Proof state:
case zero
m n a b c d : ℕ
h : a ≡ b [MOD n]
⊢ a ^ zero ≡ b ^ zero [MOD n]

Next tactic:
rfl
@[gcongr]
protected theorem pow (m : ℕ) (h : a ≡ b [MOD n]) : a ^ m ≡ b ^ m [MOD n] := by
  induction m with
  | zero => rfl
  

Proof state:
case succ
m n a b c d✝ : ℕ
h : a ≡ b [MOD n]
d : ℕ
hd : a ^ d ≡ b ^ d [MOD n]
⊢ a ^ succ d ≡ b ^ succ d [MOD n]

Next tactic:
| succ d hd =>
    rw[pow_succ, pow_succ]
    exact hd.mul h
@[gcongr]
protected theorem pow (m : ℕ) (h : a ≡ b [MOD n]) : a ^ m ≡ b ^ m [MOD n] := by
  induction m with
  | zero => rfl
  | succ d hd =>
    

Proof state:
case succ
m n a b c d✝ : ℕ
h : a ≡ b [MOD n]
d : ℕ
hd : a ^ d ≡ b ^ d [MOD n]
⊢ a ^ succ d ≡ b ^ succ d [MOD n]

Next tactic:
rw[pow_succ, pow_succ]
@[gcongr]
protected theorem pow (m : ℕ) (h : a ≡ b [MOD n]) : a ^ m ≡ b ^ m [MOD n] := by
  induction m with
  | zero => rfl
  | succ d hd =>
    rw[pow_succ, pow_succ]
    

Proof state:
case succ
m n a b c d✝ : ℕ
h : a ≡ b [MOD n]
d : ℕ
hd : a ^ d ≡ b ^ d [MOD n]
⊢ a ^ d * a ≡ b ^ d * b [MOD n]

Next tactic:
exact hd.mul h
@[gcongr]
protected theorem add (h₁ : a ≡ b [MOD n]) (h₂ : c ≡ d [MOD n]) : a + c ≡ b + d [MOD n] := by
  

Proof state:
m n a b c d : ℕ
h₁ : a ≡ b [MOD n]
h₂ : c ≡ d [MOD n]
⊢ a + c ≡ b + d [MOD n]

Next tactic:
rw [modEq_iff_dvd, Int.ofNat_add, Int.ofNat_add, add_sub_add_comm]
@[gcongr]
protected theorem add (h₁ : a ≡ b [MOD n]) (h₂ : c ≡ d [MOD n]) : a + c ≡ b + d [MOD n] := by
  rw [modEq_iff_dvd, Int.ofNat_add, Int.ofNat_add, add_sub_add_comm]
  

Proof state:
m n a b c d : ℕ
h₁ : a ≡ b [MOD n]
h₂ : c ≡ d [MOD n]
⊢ ↑n ∣ ↑b - ↑a + (↑d - ↑c)

Next tactic:
exact dvd_add h₁.dvd h₂.dvd
protected theorem add_left_cancel (h₁ : a ≡ b [MOD n]) (h₂ : a + c ≡ b + d [MOD n]) :
    c ≡ d [MOD n] := by
  

Proof state:
m n a b c d : ℕ
h₁ : a ≡ b [MOD n]
h₂ : a + c ≡ b + d [MOD n]
⊢ c ≡ d [MOD n]

Next tactic:
simp only [modEq_iff_dvd, Int.ofNat_add] at *
protected theorem add_left_cancel (h₁ : a ≡ b [MOD n]) (h₂ : a + c ≡ b + d [MOD n]) :
    c ≡ d [MOD n] := by
  simp only [modEq_iff_dvd, Int.ofNat_add] at *
  

Proof state:
m n a b c d : ℕ
h₁ : ↑n ∣ ↑b - ↑a
h₂ : ↑n ∣ ↑b + ↑d - (↑a + ↑c)
⊢ ↑n ∣ ↑d - ↑c

Next tactic:
rw [add_sub_add_comm] at h₂
protected theorem add_left_cancel (h₁ : a ≡ b [MOD n]) (h₂ : a + c ≡ b + d [MOD n]) :
    c ≡ d [MOD n] := by
  simp only [modEq_iff_dvd, Int.ofNat_add] at *
  rw [add_sub_add_comm] at h₂
  

Proof state:
m n a b c d : ℕ
h₁ : ↑n ∣ ↑b - ↑a
h₂ : ↑n ∣ ↑b - ↑a + (↑d - ↑c)
⊢ ↑n ∣ ↑d - ↑c

Next tactic:
convert _root_.dvd_sub h₂ h₁ using 1
protected theorem add_left_cancel (h₁ : a ≡ b [MOD n]) (h₂ : a + c ≡ b + d [MOD n]) :
    c ≡ d [MOD n] := by
  simp only [modEq_iff_dvd, Int.ofNat_add] at *
  rw [add_sub_add_comm] at h₂
  convert _root_.dvd_sub h₂ h₁ using 1
  

Proof state:
case h.e'_4
m n a b c d : ℕ
h₁ : ↑n ∣ ↑b - ↑a
h₂ : ↑n ∣ ↑b - ↑a + (↑d - ↑c)
⊢ ↑d - ↑c = ↑b - ↑a + (↑d - ↑c) - (↑b - ↑a)

Next tactic:
rw [add_sub_cancel']
protected theorem add_right_cancel (h₁ : c ≡ d [MOD n]) (h₂ : a + c ≡ b + d [MOD n]) :
    a ≡ b [MOD n] := by
  

Proof state:
m n a b c d : ℕ
h₁ : c ≡ d [MOD n]
h₂ : a + c ≡ b + d [MOD n]
⊢ a ≡ b [MOD n]

Next tactic:
rw [add_comm a, add_comm b] at h₂
protected theorem add_right_cancel (h₁ : c ≡ d [MOD n]) (h₂ : a + c ≡ b + d [MOD n]) :
    a ≡ b [MOD n] := by
  rw [add_comm a, add_comm b] at h₂
  

Proof state:
m n a b c d : ℕ
h₁ : c ≡ d [MOD n]
h₂ : c + a ≡ d + b [MOD n]
⊢ a ≡ b [MOD n]

Next tactic:
exact h₁.add_left_cancel h₂
/-- Cancel left multiplication on both sides of the `≡` and in the modulus.

For cancelling left multiplication in the modulus, see `Nat.ModEq.of_mul_left`. -/
protected theorem mul_left_cancel' {a b c m : ℕ} (hc : c ≠ 0) :
    c * a ≡ c * b [MOD c * m] → a ≡ b [MOD m] := by
  

Proof state:
m✝ n a✝ b✝ c✝ d a b c m : ℕ
hc : c ≠ 0
⊢ c * a ≡ c * b [MOD c * m] → a ≡ b [MOD m]

Next tactic:
simp [modEq_iff_dvd, ← mul_sub, mul_dvd_mul_iff_left (by simp [hc] : (c : ℤ) ≠ 0)]
/-- Cancel left multiplication on both sides of the `≡` and in the modulus.

For cancelling left multiplication in the modulus, see `Nat.ModEq.of_mul_left`. -/
protected theorem mul_left_cancel' {a b c m : ℕ} (hc : c ≠ 0) :
    c * a ≡ c * b [MOD c * m] → a ≡ b [MOD m] := by
  simp [modEq_iff_dvd, ← mul_sub, mul_dvd_mul_iff_left (by 

Proof state:
m✝ n a✝ b✝ c✝ d a b c m : ℕ
hc : c ≠ 0
⊢ ↑c ≠ 0

Next tactic:
simp [hc]
/-- Cancel right multiplication on both sides of the `≡` and in the modulus.

For cancelling right multiplication in the modulus, see `Nat.ModEq.of_mul_right`. -/
protected theorem mul_right_cancel' {a b c m : ℕ} (hc : c ≠ 0) :
    a * c ≡ b * c [MOD m * c] → a ≡ b [MOD m] := by
  

Proof state:
m✝ n a✝ b✝ c✝ d a b c m : ℕ
hc : c ≠ 0
⊢ a * c ≡ b * c [MOD m * c] → a ≡ b [MOD m]

Next tactic:
simp [modEq_iff_dvd, ← sub_mul, mul_dvd_mul_iff_right (by simp [hc] : (c : ℤ) ≠ 0)]
/-- Cancel right multiplication on both sides of the `≡` and in the modulus.

For cancelling right multiplication in the modulus, see `Nat.ModEq.of_mul_right`. -/
protected theorem mul_right_cancel' {a b c m : ℕ} (hc : c ≠ 0) :
    a * c ≡ b * c [MOD m * c] → a ≡ b [MOD m] := by
  simp [modEq_iff_dvd, ← sub_mul, mul_dvd_mul_iff_right (by 

Proof state:
m✝ n a✝ b✝ c✝ d a b c m : ℕ
hc : c ≠ 0
⊢ ↑c ≠ 0

Next tactic:
simp [hc]
/-- Cancel left multiplication in the modulus.

For cancelling left multiplication on both sides of the `≡`, see `nat.modeq.mul_left_cancel'`. -/
lemma of_mul_left (m : ℕ) (h : a ≡ b [MOD m * n]) : a ≡ b [MOD n] := by
  

Proof state:
m✝ n a b c d m : ℕ
h : a ≡ b [MOD m * n]
⊢ a ≡ b [MOD n]

Next tactic:
rw [modEq_iff_dvd] at *
/-- Cancel left multiplication in the modulus.

For cancelling left multiplication on both sides of the `≡`, see `nat.modeq.mul_left_cancel'`. -/
lemma of_mul_left (m : ℕ) (h : a ≡ b [MOD m * n]) : a ≡ b [MOD n] := by
  rw [modEq_iff_dvd] at *
  

Proof state:
m✝ n a b c d m : ℕ
h : ↑(m * n) ∣ ↑b - ↑a
⊢ ↑n ∣ ↑b - ↑a

Next tactic:
exact (dvd_mul_left (n : ℤ) (m : ℤ)).trans h
theorem of_div (h : a / c ≡ b / c [MOD m / c]) (ha : c ∣ a) (ha : c ∣ b) (ha : c ∣ m) :
    a ≡ b [MOD m] := by 

Proof state:
m n a b c d : ℕ
h : a / c ≡ b / c [MOD m / c]
ha✝¹ : c ∣ a
ha✝ : c ∣ b
ha : c ∣ m
⊢ a ≡ b [MOD m]

Next tactic:
convert h.mul_left' c
theorem of_div (h : a / c ≡ b / c [MOD m / c]) (ha : c ∣ a) (ha : c ∣ b) (ha : c ∣ m) :
    a ≡ b [MOD m] := by convert h.mul_left' c <;> 

Proof state:
case h.e'_1
m n a b c d : ℕ
h : a / c ≡ b / c [MOD m / c]
ha✝¹ : c ∣ a
ha✝ : c ∣ b
ha : c ∣ m
⊢ m = c * (m / c)

Next tactic:
rwa [Nat.mul_div_cancel']