Skip to content

Commit 172ffe4

Browse files
committed
feat: more positivity extensions (#28791)
Add `positivity` extensions for: - `Nat.gcd`, `Nat.lcm`, `Int.gcd`, `Int.lcm`; - `Nat.sqrt`, `Nat.totient`. Co-authored-by: Yury Kudryashov <162619279+yury-harmonic@users.noreply.github.com>
1 parent 904df44 commit 172ffe4

File tree

3 files changed

+100
-0
lines changed

3 files changed

+100
-0
lines changed

Mathlib/Data/Nat/Totient.lean

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,3 +370,19 @@ theorem totient_mul_of_prime_of_not_dvd {p n : ℕ} (hp : p.Prime) (h : ¬p ∣
370370
simpa [h] using coprime_or_dvd_of_prime hp n
371371

372372
end Nat
373+
374+
namespace Mathlib.Meta.Positivity
375+
open Lean Meta Qq
376+
377+
/-- Extension for `Nat.totient`. -/
378+
@[positivity Nat.totient _]
379+
def evalNatTotient : PositivityExt where eval {u α} z p e := do
380+
match u, α, e with
381+
| 0, ~q(ℕ), ~q(Nat.totient $n) =>
382+
assumeInstancesCommute
383+
match ← core z p n with
384+
| .positive pa => return .positive q(Nat.totient_pos.mpr $pa)
385+
| _ => failure
386+
| _, _, _ => throwError "not Nat.totient"
387+
388+
end Mathlib.Meta.Positivity

Mathlib/Tactic/Positivity/Basic.lean

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,82 @@ def evalAscFactorial : PositivityExt where eval {u α} _ _ e := do
443443
pure (.positive q(Nat.ascFactorial_pos $n $k))
444444
| _, _, _ => throwError "failed to match Nat.ascFactorial"
445445

446+
/-- Extension for `Nat.gcd`.
447+
Uses positivity of the left term, if available, then tries the right term.
448+
449+
The implementation relies on the fact that `Positivity.core` on `ℕ` never returns `nonzero`. -/
450+
@[positivity Nat.gcd _ _]
451+
def evalNatGCD : PositivityExt where eval {u α} z p e := do
452+
match u, α, e with
453+
| 0, ~q(ℕ), ~q(Nat.gcd $a $b) =>
454+
assertInstancesCommute
455+
match ← core z p a with
456+
| .positive pa => return .positive q(Nat.gcd_pos_of_pos_left $b $pa)
457+
| _ =>
458+
match ← core z p b with
459+
| .positive pb => return .positive q(Nat.gcd_pos_of_pos_right $a $pb)
460+
| _ => failure
461+
| _, _, _ => throwError "not Nat.gcd"
462+
463+
/-- Extension for `Nat.lcm`. -/
464+
@[positivity Nat.lcm _ _]
465+
def evalNatLCM : PositivityExt where eval {u α} z p e := do
466+
match u, α, e with
467+
| 0, ~q(ℕ), ~q(Nat.lcm $a $b) =>
468+
assertInstancesCommute
469+
match ← core z p a with
470+
| .positive pa =>
471+
match ← core z p b with
472+
| .positive pb =>
473+
return .positive q(Nat.lcm_pos $pa $pb)
474+
| _ => failure
475+
| _ => failure
476+
| _, _, _ => throwError "not Nat.lcm"
477+
478+
/-- Extension for `Nat.sqrt`. -/
479+
@[positivity Nat.sqrt _]
480+
def evalNatSqrt : PositivityExt where eval {u α} z p e := do
481+
match u, α, e with
482+
| 0, ~q(ℕ), ~q(Nat.sqrt $n) =>
483+
assumeInstancesCommute
484+
match ← core z p n with
485+
| .positive pa => return .positive q(Nat.sqrt_pos.mpr $pa)
486+
| _ => failure
487+
| _, _, _ => throwError "not Nat.sqrt"
488+
489+
/-- Extension for `Int.gcd`.
490+
Uses positivity of the left term, if available, then tries the right term. -/
491+
@[positivity Int.gcd _ _]
492+
def evalIntGCD : PositivityExt where eval {u α} _ _ e := do
493+
match u, α, e with
494+
| 0, ~q(ℕ), ~q(Int.gcd $a $b) =>
495+
let z ← synthInstanceQ (q(Zero ℤ) : Q(Type))
496+
let p ← synthInstanceQ (q(PartialOrder ℤ) : Q(Type))
497+
assertInstancesCommute
498+
match (← catchNone (core z p a)).toNonzero with
499+
| some na => return .positive q(Int.gcd_pos_of_ne_zero_left $b $na)
500+
| none =>
501+
match (← core z p b).toNonzero with
502+
| some nb => return .positive q(Int.gcd_pos_of_ne_zero_right $a $nb)
503+
| none => failure
504+
| _, _, _ => throwError "not Int.gcd"
505+
506+
/-- Extension for `Int.lcm`. -/
507+
@[positivity Int.lcm _ _]
508+
def evalIntLCM : PositivityExt where eval {u α} _ _ e := do
509+
match u, α, e with
510+
| 0, ~q(ℕ), ~q(Int.lcm $a $b) =>
511+
let z ← synthInstanceQ (q(Zero ℤ) : Q(Type))
512+
let p ← synthInstanceQ (q(PartialOrder ℤ) : Q(Type))
513+
assertInstancesCommute
514+
match (← core z p a).toNonzero with
515+
| some na =>
516+
match (← core z p b).toNonzero with
517+
| some nb => return .positive q(Int.lcm_pos $na $nb)
518+
| _ => failure
519+
| _ => failure
520+
| _, _, _ => throwError "not Int.lcm"
521+
446522
section NNRat
447523
open NNRat
448524

MathlibTest/positivity.lean

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,14 @@ example (hq : 0 ≤ q) : 0 ≤ q.num := by positivity
234234

235235
end
236236

237+
example (a b : ℕ) (ha : a ≠ 0) : 0 < a.gcd b := by positivity
238+
example (a b : ℤ) (ha : a ≠ 0) : 0 < a.gcd b := by positivity
239+
example (a b : ℕ) (hb : b ≠ 0) : 0 < a.gcd b := by positivity
240+
example (a b : ℤ) (hb : b ≠ 0) : 0 < a.gcd b := by positivity
241+
example (a b : ℕ) (ha : a ≠ 0) (hb : b ≠ 0) : 0 < a.lcm b := by positivity
242+
example (a b : ℤ) (ha : a ≠ 0) (hb : b ≠ 0) : 0 < a.lcm b := by positivity
243+
example (a : ℕ) (ha : a ≠ 0) : 0 < a.sqrt := by positivity
244+
example (a : ℕ) (ha : a ≠ 0) : 0 < a.totient := by positivity
237245

238246
section ENNReal
239247

0 commit comments

Comments
 (0)