File: Mathlib/NumberTheory/LegendreSymbol/JacobiSymbol.lean

full_name: jacobiSym
code: /-- The Jacobi symbol of `a` and `b` -/
def jacobiSym (a : ℤ) (b : ℕ) : ℤ 
goal: ℤ

full_name: NumberTheorySymbols._aux_Mathlib_NumberTheory_LegendreSymbol_JacobiSymbol___unexpand_jacobiSym_1
code: @[inherit_doc]
scoped[NumberTheorySymbols] notation "J(" a " | " b ")" => jacobiSym a b
goal: Lean.PrettyPrinter.Unexpander

full_name: NumberTheorySymbols.«_aux_Mathlib_NumberTheory_LegendreSymbol_JacobiSymbol___macroRules_NumberTheorySymbols_termJ(_|_)_1»
code: @[inherit_doc]
scoped[NumberTheorySymbols] notation "J(" a " | " b ")" => jacobiSym a b
goal: Lean.Macro

full_name: jacobiSym.zero_right
code: /-- The symbol `J(a | 0)` has the value `1`. -/
@[simp]
theorem zero_right (a : ℤ) : J(a | 0) = 1 
goal: jacobiSym a 0 = 1

full_name: jacobiSym.one_right
code: /-- The symbol `J(a | 1)` has the value `1`. -/
@[simp]
theorem one_right (a : ℤ) : J(a | 1) = 1 
goal: jacobiSym a 1 = 1

full_name: jacobiSym.legendreSym.to_jacobiSym
code: /-- The Legendre symbol `legendreSym p a` with an integer `a` and a prime number `p`
is the same as the Jacobi symbol `J(a | p)`. -/
theorem legendreSym.to_jacobiSym (p : ℕ) [fp : Fact p.Prime] (a : ℤ) :
    legendreSym p a = J(a | p) 
goal: legendreSym p a = jacobiSym a p

full_name: jacobiSym.mul_right'
code: /-- The Jacobi symbol is multiplicative in its second argument. -/
theorem mul_right' (a : ℤ) {b₁ b₂ : ℕ} (hb₁ : b₁ ≠ 0) (hb₂ : b₂ ≠ 0) :
    J(a | b₁ * b₂) = J(a | b₁) * J(a | b₂) 
goal: jacobiSym a (b₁ * b₂) = jacobiSym a b₁ * jacobiSym a b₂

full_name: jacobiSym.mul_right
code: /-- The Jacobi symbol is multiplicative in its second argument. -/
theorem mul_right (a : ℤ) (b₁ b₂ : ℕ) [NeZero b₁] [NeZero b₂] :
    J(a | b₁ * b₂) = J(a | b₁) * J(a | b₂) 
goal: jacobiSym a (b₁ * b₂) = jacobiSym a b₁ * jacobiSym a b₂

full_name: jacobiSym.trichotomy
code: /-- The Jacobi symbol takes only the values `0`, `1` and `-1`. -/
theorem trichotomy (a : ℤ) (b : ℕ) : J(a | b) = 0 ∨ J(a | b) = 1 ∨ J(a | b) = -1 
goal: jacobiSym a b = 0 ∨ jacobiSym a b = 1 ∨ jacobiSym a b = -1

full_name: jacobiSym.one_left
code: /-- The symbol `J(1 | b)` has the value `1`. -/
@[simp]
theorem one_left (b : ℕ) : J(1 | b) = 1 
goal: jacobiSym 1 b = 1

full_name: jacobiSym.mul_left
code: /-- The Jacobi symbol is multiplicative in its first argument. -/
theorem mul_left (a₁ a₂ : ℤ) (b : ℕ) : J(a₁ * a₂ | b) = J(a₁ | b) * J(a₂ | b) 
goal: jacobiSym (a₁ * a₂) b = jacobiSym a₁ b * jacobiSym a₂ b

full_name: jacobiSym.eq_zero_iff_not_coprime
code: /-- The symbol `J(a | b)` vanishes iff `a` and `b` are not coprime (assuming `b ≠ 0`). -/
theorem eq_zero_iff_not_coprime {a : ℤ} {b : ℕ} [NeZero b] : J(a | b) = 0 ↔ a.gcd b ≠ 1 
goal: jacobiSym a b = 0 ↔ a.gcd ↑b ≠ 1

full_name: jacobiSym.ne_zero
code: /-- The symbol `J(a | b)` is nonzero when `a` and `b` are coprime. -/
protected theorem ne_zero {a : ℤ} {b : ℕ} (h : a.gcd b = 1) : J(a | b) ≠ 0 
goal: jacobiSym a b ≠ 0

full_name: jacobiSym.eq_zero_iff
code: /-- The symbol `J(a | b)` vanishes if and only if `b ≠ 0` and `a` and `b` are not coprime. -/
theorem eq_zero_iff {a : ℤ} {b : ℕ} : J(a | b) = 0 ↔ b ≠ 0 ∧ a.gcd b ≠ 1 
goal: jacobiSym a b = 0 ↔ b ≠ 0 ∧ a.gcd ↑b ≠ 1

full_name: jacobiSym.zero_left
code: /-- The symbol `J(0 | b)` vanishes when `b > 1`. -/
theorem zero_left {b : ℕ} (hb : 1 < b) : J(0 | b) = 0 
goal: jacobiSym 0 b = 0

full_name: jacobiSym.eq_one_or_neg_one
code: /-- The symbol `J(a | b)` takes the value `1` or `-1` if `a` and `b` are coprime. -/
theorem eq_one_or_neg_one {a : ℤ} {b : ℕ} (h : a.gcd b = 1) : J(a | b) = 1 ∨ J(a | b) = -1 
goal: jacobiSym a b = 1 ∨ jacobiSym a b = -1

full_name: jacobiSym.pow_left
code: /-- We have that `J(a^e | b) = J(a | b)^e`. -/
theorem pow_left (a : ℤ) (e b : ℕ) : J(a ^ e | b) = J(a | b) ^ e 
goal: jacobiSym (a ^ e) b = jacobiSym a b ^ e

full_name: jacobiSym.pow_right
code: /-- We have that `J(a | b^e) = J(a | b)^e`. -/
theorem pow_right (a : ℤ) (b e : ℕ) : J(a | b ^ e) = J(a | b) ^ e 
goal: jacobiSym a (b ^ e) = jacobiSym a b ^ e

full_name: jacobiSym.sq_one
code: /-- The square of `J(a | b)` is `1` when `a` and `b` are coprime. -/
theorem sq_one {a : ℤ} {b : ℕ} (h : a.gcd b = 1) : J(a | b) ^ 2 = 1 
goal: jacobiSym a b ^ 2 = 1

full_name: jacobiSym.sq_one'
code: /-- The symbol `J(a^2 | b)` is `1` when `a` and `b` are coprime. -/
theorem sq_one' {a : ℤ} {b : ℕ} (h : a.gcd b = 1) : J(a ^ 2 | b) = 1 
goal: jacobiSym (a ^ 2) b = 1

full_name: jacobiSym.mod_left
code: /-- The symbol `J(a | b)` depends only on `a` mod `b`. -/
theorem mod_left (a : ℤ) (b : ℕ) : J(a | b) = J(a % b | b) 
goal: jacobiSym a b = jacobiSym (a % ↑b) b

full_name: jacobiSym.mod_left'
code: /-- The symbol `J(a | b)` depends only on `a` mod `b`. -/
theorem mod_left' {a₁ a₂ : ℤ} {b : ℕ} (h : a₁ % b = a₂ % b) : J(a₁ | b) = J(a₂ | b) 
goal: jacobiSym a₁ b = jacobiSym a₂ b

full_name: jacobiSym.prime_dvd_of_eq_neg_one
code: /-- If `p` is prime, `J(a | p) = -1` and `p` divides `x^2 - a*y^2`, then `p` must divide
`x` and `y`. -/
theorem prime_dvd_of_eq_neg_one {p : ℕ} [Fact p.Prime] {a : ℤ} (h : J(a | p) = -1) {x y : ℤ}
    (hxy : ↑p ∣ (x ^ 2 - a * y ^ 2 : ℤ)) : ↑p ∣ x ∧ ↑p ∣ y 
goal: ↑p ∣ x ∧ ↑p ∣ y

full_name: jacobiSym.list_prod_left
code: /-- We can pull out a product over a list in the first argument of the Jacobi symbol. -/
theorem list_prod_left {l : List ℤ} {n : ℕ} : J(l.prod | n) = (l.map fun a => J(a | n)).prod 
goal: jacobiSym l.prod n = (List.map (fun a => jacobiSym a n) l).prod

full_name: jacobiSym.list_prod_right
code: /-- We can pull out a product over a list in the second argument of the Jacobi symbol. -/
theorem list_prod_right {a : ℤ} {l : List ℕ} (hl : ∀ n ∈ l, n ≠ 0) :
    J(a | l.prod) = (l.map fun n => J(a | n)).prod 
goal: jacobiSym a l.prod = (List.map (fun n => jacobiSym a n) l).prod

full_name: jacobiSym.eq_neg_one_at_prime_divisor_of_eq_neg_one
code: /-- If `J(a | n) = -1`, then `n` has a prime divisor `p` such that `J(a | p) = -1`. -/
theorem eq_neg_one_at_prime_divisor_of_eq_neg_one {a : ℤ} {n : ℕ} (h : J(a | n) = -1) :
    ∃ p : ℕ, p.Prime ∧ p ∣ n ∧ J(a | p) = -1 
goal: ∃ p, Nat.Prime p ∧ p ∣ n ∧ jacobiSym a p = -1

full_name: ZMod.nonsquare_of_jacobiSym_eq_neg_one
code: /-- If `J(a | b)` is `-1`, then `a` is not a square modulo `b`. -/
theorem nonsquare_of_jacobiSym_eq_neg_one {a : ℤ} {b : ℕ} (h : J(a | b) = -1) :
    ¬IsSquare (a : ZMod b) 
goal: ¬IsSquare ↑a

full_name: ZMod.nonsquare_iff_jacobiSym_eq_neg_one
code: /-- If `p` is prime, then `J(a | p)` is `-1` iff `a` is not a square modulo `p`. -/
theorem nonsquare_iff_jacobiSym_eq_neg_one {a : ℤ} {p : ℕ} [Fact p.Prime] :
    J(a | p) = -1 ↔ ¬IsSquare (a : ZMod p) 
goal: jacobiSym a p = -1 ↔ ¬IsSquare ↑a

full_name: ZMod.isSquare_of_jacobiSym_eq_one
code: /-- If `p` is prime and `J(a | p) = 1`, then `a` is a square mod `p`. -/
theorem isSquare_of_jacobiSym_eq_one {a : ℤ} {p : ℕ} [Fact p.Prime] (h : J(a | p) = 1) :
    IsSquare (a : ZMod p) 
goal: IsSquare ↑a

full_name: jacobiSym.value_at
code: /-- If `χ` is a multiplicative function such that `J(a | p) = χ p` for all odd primes `p`,
then `J(a | b)` equals `χ b` for all odd natural numbers `b`. -/
theorem value_at (a : ℤ) {R : Type*} [CommSemiring R] (χ : R →* ℤ)
    (hp : ∀ (p : ℕ) (pp : p.Prime), p ≠ 2 → @legendreSym p ⟨pp⟩ a = χ p) {b : ℕ} (hb : Odd b) :
    J(a | b) = χ b 
goal: jacobiSym a b = χ ↑b

full_name: jacobiSym.at_neg_one
code: /-- If `b` is odd, then `J(-1 | b)` is given by `χ₄ b`. -/
theorem at_neg_one {b : ℕ} (hb : Odd b) : J(-1 | b) = χ₄ b 
goal: jacobiSym (-1) b = ZMod.χ₄ ↑b

full_name: jacobiSym.neg
code: /-- If `b` is odd, then `J(-a | b) = χ₄ b * J(a | b)`. -/
protected theorem neg (a : ℤ) {b : ℕ} (hb : Odd b) : J(-a | b) = χ₄ b * J(a | b) 
goal: jacobiSym (-a) b = ZMod.χ₄ ↑b * jacobiSym a b

full_name: jacobiSym.at_two
code: /-- If `b` is odd, then `J(2 | b)` is given by `χ₈ b`. -/
theorem at_two {b : ℕ} (hb : Odd b) : J(2 | b) = χ₈ b 
goal: jacobiSym 2 b = ZMod.χ₈ ↑b

full_name: jacobiSym.at_neg_two
code: /-- If `b` is odd, then `J(-2 | b)` is given by `χ₈' b`. -/
theorem at_neg_two {b : ℕ} (hb : Odd b) : J(-2 | b) = χ₈' b 
goal: jacobiSym (-2) b = ZMod.χ₈' ↑b

full_name: jacobiSym.div_four_left
code: theorem div_four_left {a : ℤ} {b : ℕ} (ha4 : a % 4 = 0) (hb2 : b % 2 = 1) :
    J(a / 4 | b) = J(a | b) 
goal: jacobiSym (a / 4) b = jacobiSym a b

full_name: jacobiSym.even_odd
code: theorem even_odd {a : ℤ} {b : ℕ} (ha2 : a % 2 = 0) (hb2 : b % 2 = 1) :
    (if b % 8 = 3 ∨ b % 8 = 5 then -J(a / 2 | b) else J(a / 2 | b)) = J(a | b) 
goal: (if b % 8 = 3 ∨ b % 8 = 5 then -jacobiSym (a / 2) b else jacobiSym (a / 2) b) = jacobiSym a b

full_name: qrSign
code: /-- The bi-multiplicative map giving the sign in the Law of Quadratic Reciprocity -/
def qrSign (m n : ℕ) : ℤ 
goal: ℤ

full_name: qrSign.neg_one_pow
code: /-- We can express `qrSign m n` as a power of `-1` when `m` and `n` are odd. -/
theorem neg_one_pow {m n : ℕ} (hm : Odd m) (hn : Odd n) :
    qrSign m n = (-1) ^ (m / 2 * (n / 2)) 
goal: qrSign m n = (-1) ^ (m / 2 * (n / 2))

full_name: qrSign.sq_eq_one
code: /-- When `m` and `n` are odd, then the square of `qrSign m n` is `1`. -/
theorem sq_eq_one {m n : ℕ} (hm : Odd m) (hn : Odd n) : qrSign m n ^ 2 = 1 
goal: qrSign m n ^ 2 = 1

full_name: qrSign.mul_left
code: /-- `qrSign` is multiplicative in the first argument. -/
theorem mul_left (m₁ m₂ n : ℕ) : qrSign (m₁ * m₂) n = qrSign m₁ n * qrSign m₂ n 
goal: qrSign (m₁ * m₂) n = qrSign m₁ n * qrSign m₂ n

full_name: qrSign.mul_right
code: /-- `qrSign` is multiplicative in the second argument. -/
theorem mul_right (m n₁ n₂ : ℕ) [NeZero n₁] [NeZero n₂] :
    qrSign m (n₁ * n₂) = qrSign m n₁ * qrSign m n₂ 
goal: qrSign m (n₁ * n₂) = qrSign m n₁ * qrSign m n₂

full_name: qrSign.symm
code: /-- `qrSign` is symmetric when both arguments are odd. -/
protected theorem symm {m n : ℕ} (hm : Odd m) (hn : Odd n) : qrSign m n = qrSign n m 
goal: qrSign m n = qrSign n m

full_name: qrSign.eq_iff_eq
code: /-- We can move `qrSign m n` from one side of an equality to the other when `m` and `n` are odd. -/
theorem eq_iff_eq {m n : ℕ} (hm : Odd m) (hn : Odd n) (x y : ℤ) :
    qrSign m n * x = y ↔ x = qrSign m n * y 
goal: qrSign m n * x = y ↔ x = qrSign m n * y

full_name: jacobiSym.quadratic_reciprocity'
code: /-- The **Law of Quadratic Reciprocity for the Jacobi symbol**, version with `qrSign` -/
theorem quadratic_reciprocity' {a b : ℕ} (ha : Odd a) (hb : Odd b) :
    J(a | b) = qrSign b a * J(b | a) 
goal: jacobiSym (↑a) b = qrSign b a * jacobiSym (↑b) a

full_name: jacobiSym.quadratic_reciprocity
code: /-- The Law of Quadratic Reciprocity for the Jacobi symbol -/
theorem quadratic_reciprocity {a b : ℕ} (ha : Odd a) (hb : Odd b) :
    J(a | b) = (-1) ^ (a / 2 * (b / 2)) * J(b | a) 
goal: jacobiSym (↑a) b = (-1) ^ (a / 2 * (b / 2)) * jacobiSym (↑b) a

full_name: jacobiSym.quadratic_reciprocity_one_mod_four
code: /-- The Law of Quadratic Reciprocity for the Jacobi symbol: if `a` and `b` are natural numbers
with `a % 4 = 1` and `b` odd, then `J(a | b) = J(b | a)`. -/
theorem quadratic_reciprocity_one_mod_four {a b : ℕ} (ha : a % 4 = 1) (hb : Odd b) :
    J(a | b) = J(b | a) 
goal: jacobiSym (↑a) b = jacobiSym (↑b) a

full_name: jacobiSym.quadratic_reciprocity_one_mod_four'
code: /-- The Law of Quadratic Reciprocity for the Jacobi symbol: if `a` and `b` are natural numbers
with `a` odd and `b % 4 = 1`, then `J(a | b) = J(b | a)`. -/
theorem quadratic_reciprocity_one_mod_four' {a b : ℕ} (ha : Odd a) (hb : b % 4 = 1) :
    J(a | b) = J(b | a) 
goal: jacobiSym (↑a) b = jacobiSym (↑b) a

full_name: jacobiSym.quadratic_reciprocity_three_mod_four
code: /-- The Law of Quadratic Reciprocity for the Jacobi symbol: if `a` and `b` are natural numbers
both congruent to `3` mod `4`, then `J(a | b) = -J(b | a)`. -/
theorem quadratic_reciprocity_three_mod_four {a b : ℕ} (ha : a % 4 = 3) (hb : b % 4 = 3) :
    J(a | b) = -J(b | a) 
goal: jacobiSym (↑a) b = -jacobiSym (↑b) a

full_name: jacobiSym.quadratic_reciprocity_if
code: theorem quadratic_reciprocity_if {a b : ℕ} (ha2 : a % 2 = 1) (hb2 : b % 2 = 1) :
    (if a % 4 = 3 ∧ b % 4 = 3 then -J(b | a) else J(b | a)) = J(a | b) 
goal: (if a % 4 = 3 ∧ b % 4 = 3 then -jacobiSym (↑b) a else jacobiSym (↑b) a) = jacobiSym (↑a) b

full_name: jacobiSym.mod_right'
code: /-- The Jacobi symbol `J(a | b)` depends only on `b` mod `4*a` (version for `a : ℕ`). -/
theorem mod_right' (a : ℕ) {b : ℕ} (hb : Odd b) : J(a | b) = J(a | b % (4 * a)) 
goal: jacobiSym (↑a) b = jacobiSym (↑a) (b % (4 * a))

full_name: jacobiSym.mod_right
code: /-- The Jacobi symbol `J(a | b)` depends only on `b` mod `4*a`. -/
theorem mod_right (a : ℤ) {b : ℕ} (hb : Odd b) : J(a | b) = J(a | b % (4 * a.natAbs)) 
goal: jacobiSym a b = jacobiSym a (b % (4 * a.natAbs))

full_name: _private.Mathlib.NumberTheory.LegendreSymbol.JacobiSymbol.0.fastJacobiSymAux
code: /-- Computes `J(a | b)` (or `-J(a | b)` if `flip` is set to `true`) given assumptions, by reducing
`a` to odd by repeated division and then using quadratic reciprocity to swap `a`, `b`. -/
private def fastJacobiSymAux (a b : ℕ) (flip : Bool) (ha0 : a > 0) : ℤ 
goal: ℤ

full_name: _private.Mathlib.NumberTheory.LegendreSymbol.JacobiSymbol.0.fastJacobiSymAux.eq_jacobiSym
code: private theorem fastJacobiSymAux.eq_jacobiSym {a b : ℕ} {flip : Bool} {ha0 : a > 0}
    (hb2 : b % 2 = 1) (hb1 : b > 1) :
    fastJacobiSymAux a b flip ha0 = if flip then -J(a | b) else J(a | b) 
goal: fastJacobiSymAux a b flip ha0 = if flip = true then -jacobiSym (↑a) b else jacobiSym (↑a) b

full_name: _private.Mathlib.NumberTheory.LegendreSymbol.JacobiSymbol.0.fastJacobiSym
code: /-- Computes `J(a | b)` by reducing `b` to odd by repeated division and then using
`fastJacobiSymAux`. -/
private def fastJacobiSym (a : ℤ) (b : ℕ) : ℤ 
goal: ℤ

full_name: _private.Mathlib.NumberTheory.LegendreSymbol.JacobiSymbol.0.fastJacobiSym.eq
code: @[csimp] private theorem fastJacobiSym.eq : jacobiSym = fastJacobiSym 
goal: jacobiSym = fastJacobiSym

full_name: _private.Mathlib.NumberTheory.LegendreSymbol.JacobiSymbol.0.fastLegendreSym
code: /-- Computes `legendreSym p a` using `fastJacobiSym`. -/
@[inline, nolint unusedArguments]
private def fastLegendreSym (p : ℕ) [Fact p.Prime] (a : ℤ) : ℤ 
goal: ℤ

full_name: _private.Mathlib.NumberTheory.LegendreSymbol.JacobiSymbol.0.fastLegendreSym.eq
code: @[csimp] private theorem fastLegendreSym.eq : legendreSym = fastLegendreSym 
goal: legendreSym = fastLegendreSym