Skip to content

Commit 7a71f83

Browse files
committed
feat: add norm_num extensions (#28836)
Add `norm_num` extensions for - `Int.negOfNat`; - `Nat.floor`, `Nat.ceil`, and `Int.round`.
1 parent fc2363b commit 7a71f83

File tree

5 files changed

+251
-15
lines changed

5 files changed

+251
-15
lines changed

Mathlib/Algebra/Order/Floor/Semifield.lean

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,3 +101,70 @@ lemma ceil_le_two_mul (ha : 2⁻¹ ≤ a) : ⌈a⌉₊ ≤ 2 * a :=
101101
end LinearOrderedField
102102

103103
end Nat
104+
105+
namespace Mathlib.Meta.NormNum
106+
107+
open Qq
108+
109+
/-!
110+
### `norm_num` extension for `Nat.floor`
111+
-/
112+
113+
theorem IsNat.natFloor {R : Type*} [Semiring R] [LinearOrder R] [IsStrictOrderedRing R]
114+
[FloorSemiring R] (r : R) (m : ℕ) : IsNat r m → IsNat (⌊r⌋₊) m := by
115+
rintro ⟨⟨⟩⟩
116+
exact ⟨by simp⟩
117+
118+
theorem IsInt.natFloor {R : Type*} [Ring R] [LinearOrder R] [IsStrictOrderedRing R]
119+
[FloorSemiring R] (r : R) (m : ℕ) : IsInt r (.negOfNat m) → IsNat (⌊r⌋₊) 0 := by
120+
rintro ⟨⟨⟩⟩
121+
exact ⟨Nat.floor_of_nonpos <| by simp⟩
122+
123+
theorem IsNNRat.natFloor {R : Type*} [Semifield R] [LinearOrder R] [IsStrictOrderedRing R]
124+
[FloorSemiring R] (r : R) (n d : ℕ) (h : IsNNRat r n d) (res : ℕ) (hres : n / d = res) :
125+
IsNat ⌊r⌋₊ res := by
126+
constructor
127+
rw [← hres, h.to_eq rfl rfl, Nat.floor_div_eq_div, Nat.cast_id]
128+
129+
theorem IsRat.natFloor {R : Type*} [Field R] [LinearOrder R] [IsStrictOrderedRing R]
130+
[FloorSemiring R] (r : R) (n d : ℕ) (h : IsRat r (Int.negOfNat n) d) : IsNat ⌊r⌋₊ 0 := by
131+
rcases h with ⟨hd, rfl⟩
132+
constructor
133+
rw [Nat.cast_zero, Nat.floor_eq_zero]
134+
exact lt_of_le_of_lt (by simp [mul_nonneg]) one_pos
135+
136+
open Lean in
137+
/-- `norm_num` extension for `Nat.floor` -/
138+
@[norm_num ⌊_⌋₊]
139+
def evalNatFloor : NormNumExt where eval {u αZ} e := do
140+
match u, αZ, e with
141+
| 0, ~q(ℕ), ~q(@Nat.floor $α $instSemiring $instPartialOrder $instFloorSemiring $x) =>
142+
match ← derive x with
143+
| .isBool .. => failure
144+
| .isNat sα nb pb => do
145+
let instLinearOrder ← synthInstanceQ q(LinearOrder $α)
146+
let instIsStrictOrderedRing ← synthInstanceQ q(IsStrictOrderedRing $α)
147+
assertInstancesCommute
148+
return .isNat q(inferInstance) nb q(IsNat.natFloor $x _ $pb)
149+
| .isNegNat sα nb pb => do
150+
let instLinearOrder ← synthInstanceQ q(LinearOrder $α)
151+
let instIsStrictOrderedRing ← synthInstanceQ q(IsStrictOrderedRing $α)
152+
assertInstancesCommute
153+
return .isNat q(inferInstance) (mkRawNatLit 0) q(IsInt.natFloor _ _ $pb)
154+
| .isNNRat dα q n d h => do
155+
let instSemifield ← synthInstanceQ q(Semifield $α)
156+
let instLinearOrder ← synthInstanceQ q(LinearOrder $α)
157+
let instIsStrictOrderedRing ← synthInstanceQ q(IsStrictOrderedRing $α)
158+
assertInstancesCommute
159+
have z : Q(ℕ) := mkRawNatLit (q.num.toNat / q.den)
160+
haveI : $z =Q $n / $d := ⟨⟩
161+
return .isNat q(inferInstance) z q(IsNNRat.natFloor _ $n $d $h $z rfl)
162+
| .isNegNNRat _ q n d h => do
163+
let instField ← synthInstanceQ q(Field $α)
164+
let instLinearOrder ← synthInstanceQ q(LinearOrder $α)
165+
let instIsStrictOrderedRing ← synthInstanceQ q(IsStrictOrderedRing $α)
166+
assertInstancesCommute
167+
return .isNat q(inferInstance) (mkRawNatLit 0) q(IsRat.natFloor $x $n $d $h)
168+
| _, _, _ => failure
169+
170+
end Mathlib.Meta.NormNum

Mathlib/Data/NNRat/Floor.lean

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,3 +92,69 @@ theorem floor_natCast_div_natCast (n d : ℕ) : ⌊(↑n / ↑d : ℚ≥0)⌋₊
9292
Rat.natFloor_natCast_div_natCast n d
9393

9494
end NNRat
95+
96+
namespace Mathlib.Meta.NormNum
97+
98+
open Qq
99+
100+
/-!
101+
### `norm_num` extension for `Nat.ceil`
102+
-/
103+
104+
theorem IsNat.natCeil {R : Type*} [Semiring R] [LinearOrder R] [IsStrictOrderedRing R]
105+
[FloorSemiring R] (r : R) (m : ℕ) : IsNat r m → IsNat (⌈r⌉₊) m := by
106+
rintro ⟨⟨⟩⟩
107+
exact ⟨by simp⟩
108+
109+
theorem IsInt.natCeil {R : Type*} [Ring R] [LinearOrder R] [IsStrictOrderedRing R] [FloorSemiring R]
110+
(r : R) (m : ℕ) : IsInt r (.negOfNat m) → IsNat (⌈r⌉₊) 0 := by
111+
rintro ⟨⟨⟩⟩
112+
exact ⟨by simp⟩
113+
114+
theorem IsNNRat.natCeil {R : Type*} [Semifield R] [LinearOrder R] [IsStrictOrderedRing R]
115+
[FloorSemiring R] (r : R) (n d : ℕ) (h : IsNNRat r n d) (res : ℕ)
116+
(hres : ⌈(n / d : ℚ≥0)⌉₊ = res) : IsNat ⌈r⌉₊ res := by
117+
constructor
118+
rw [← hres, h.to_eq rfl rfl, ← @NNRat.ceil_cast R]
119+
simp
120+
121+
theorem IsRat.natCeil {R : Type*} [Field R] [LinearOrder R] [IsStrictOrderedRing R]
122+
[FloorSemiring R] (r : R) (n d : ℕ) (h : IsRat r (.negOfNat n) d) : IsNat ⌈r⌉₊ 0 := by
123+
constructor
124+
simp [h.neg_to_eq, div_nonneg]
125+
126+
open Lean in
127+
/-- `norm_num` extension for `Nat.ceil` -/
128+
@[norm_num ⌈_⌉₊]
129+
def evalNatCeil : NormNumExt where eval {u αZ} e := do
130+
match u, αZ, e with
131+
| 0, ~q(ℕ), ~q(@Nat.ceil $α $instSemiring $instPartialOrder $instFloorSemiring $x) =>
132+
match ← derive x with
133+
| .isBool .. => failure
134+
| .isNat sα nb pb => do
135+
let instLinearOrder ← synthInstanceQ q(LinearOrder $α)
136+
let instIsStrictOrderedRing ← synthInstanceQ q(IsStrictOrderedRing $α)
137+
assertInstancesCommute
138+
return .isNat q(inferInstance) nb q(IsNat.natCeil $x _ $pb)
139+
| .isNegNat sα nb pb => do
140+
let instLinearOrder ← synthInstanceQ q(LinearOrder $α)
141+
let instIsStrictOrderedRing ← synthInstanceQ q(IsStrictOrderedRing $α)
142+
assertInstancesCommute
143+
return .isNat q(inferInstance) (mkRawNatLit 0) q(IsInt.natCeil _ _ $pb)
144+
| .isNNRat _ q n d h => do
145+
let instSemifield ← synthInstanceQ q(Semifield $α)
146+
let instLinearOrder ← synthInstanceQ q(LinearOrder $α)
147+
let instIsStrictOrderedRing ← synthInstanceQ q(IsStrictOrderedRing $α)
148+
assertInstancesCommute
149+
have z : Q(ℕ) := mkRawNatLit (⌈q⌉₊)
150+
letI : $z =Q ⌈($n / $d : NNRat)⌉₊ := ⟨⟩
151+
return .isNat q(inferInstance) z q(IsNNRat.natCeil _ $n $d $h $z rfl)
152+
| .isNegNNRat _ q n d h => do
153+
let instField ← synthInstanceQ q(Field $α)
154+
let instLinearOrder ← synthInstanceQ q(LinearOrder $α)
155+
let instIsStrictOrderedRing ← synthInstanceQ q(IsStrictOrderedRing $α)
156+
assertInstancesCommute
157+
return .isNat q(inferInstance) (mkRawNatLit 0) q(IsRat.natCeil _ _ _ $h)
158+
| _, _, _ => failure
159+
160+
end Mathlib.Meta.NormNum

Mathlib/Data/Rat/Floor.lean

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,59 @@ def evalIntFract : NormNumExt where eval {u α} e := do
283283
return .isRat _ (Int.fract q) n' d q(isRat_intFract_of_isRat_negOfNat _ $n $d $h)
284284
| _, _, _ => failure
285285

286+
/-!
287+
### `norm_num` extension for `round`
288+
-/
289+
290+
theorem isNat_round {R : Type*} [Ring R] [LinearOrder R] [IsStrictOrderedRing R] [FloorRing R]
291+
(r : R) (m : ℕ) : IsNat r m → IsNat (round r) m := by
292+
rintro ⟨⟨⟩⟩; exact ⟨by simp⟩
293+
294+
theorem isInt_round {R : Type*} [Ring R] [LinearOrder R] [IsStrictOrderedRing R] [FloorRing R]
295+
(r : R) (m : ℤ) : IsInt r m → IsInt (round r) m := by
296+
rintro ⟨⟨⟩⟩; exact ⟨by simp⟩
297+
298+
theorem IsRat.isInt_round {R : Type*} [Field R] [LinearOrder R] [IsStrictOrderedRing R]
299+
[FloorRing R] (r : R) (n : ℤ) (d : ℕ) (res : ℤ) (hres : round (n / d : ℚ) = res) :
300+
IsRat r n d → IsInt (round r) res := by
301+
rintro ⟨inv, rfl⟩
302+
subst res
303+
constructor
304+
rw [invOf_eq_inv, ← div_eq_mul_inv]
305+
norm_cast
306+
307+
/-- `norm_num` extension for `round` -/
308+
@[norm_num round _]
309+
def evalRound : NormNumExt where eval {u αZ} e := do
310+
match u, αZ, e with
311+
| 0, ~q(ℤ), ~q(@round $α $instRing $instLinearOrder $instFloorRing $x) =>
312+
match ← derive x with
313+
| .isBool .. => failure
314+
| .isNat sα nb pb => do
315+
let instIsStrictOrderedRing ← synthInstanceQ q(IsStrictOrderedRing $α)
316+
assertInstancesCommute
317+
return .isNat q(inferInstance) nb q(isNat_round $x _ $pb)
318+
| .isNegNat sα nb pb => do
319+
let _instIsStrictOrderedRing ← synthInstanceQ q(IsStrictOrderedRing $α)
320+
assertInstancesCommute
321+
return .isNegNat q(inferInstance) nb q(isInt_round _ _ $pb)
322+
| .isNNRat _ q n d h => do
323+
let _instField ← synthInstanceQ q(Field $α)
324+
let _instIsStrictOrderedRing ← synthInstanceQ q(IsStrictOrderedRing $α)
325+
assertInstancesCommute
326+
have z : Q(ℤ) := mkRawIntLit (round q)
327+
haveI : $z =Q round (Int.ofNat $n / $d : ℚ) := ⟨⟩
328+
return .isInt q(inferInstance) z (round q)
329+
q(IsRat.isInt_round $x $n $d $z rfl (IsNNRat.to_isRat $h))
330+
| .isNegNNRat _ q n d h => do
331+
let _instField ← synthInstanceQ q(Field $α)
332+
let _instIsStrictOrderedRing ← synthInstanceQ q(IsStrictOrderedRing $α)
333+
assertInstancesCommute
334+
have z : Q(ℤ) := mkRawIntLit (round q)
335+
haveI : $z =Q round ((Int.negOfNat $n) / $d : ℚ) := ⟨⟩
336+
return .isInt q(inferInstance) z (round q) q(IsRat.isInt_round $x (.negOfNat $n) $d $z rfl $h)
337+
| _, _, _ => failure
338+
286339
end NormNum
287340

288341
end Rat

Mathlib/Tactic/NormNum/Basic.lean

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,20 @@ theorem isNat_intOfNat : {n n' : ℕ} → IsNat n n' → IsNat (Int.ofNat n) n'
8181
haveI' x : $e =Q Int.ofNat $n := ⟨⟩
8282
return .isNat sℤ n' q(isNat_intOfNat $p)
8383

84+
theorem isInt_negOfNat (m n : ℕ) (h : IsNat m n) : IsInt (Int.negOfNat m) (.negOfNat n) :=
85+
⟨congr_arg Int.negOfNat h.1
86+
87+
/-- `norm_num` extension for `Int.negOfNat`.
88+
89+
It's useful for calling `derive` with the numerator of an `.isNegNNRat` branch. -/
90+
@[norm_num Int.negOfNat _]
91+
def evalNegOfNat : NormNumExt where eval {u αZ} e := do
92+
match u, αZ, e with
93+
| 0, ~q(ℤ), ~q(Int.negOfNat $a) =>
94+
let ⟨n, pn⟩ ← deriveNat (u := 0) a q(inferInstance)
95+
return .isNegNat q(inferInstance) n q(isInt_negOfNat $a $n $pn)
96+
| _ => failure
97+
8498
theorem isNat_natAbs_pos : {n : ℤ} → {a : ℕ} → IsNat n a → IsNat n.natAbs a
8599
| _, _, ⟨rfl⟩ => ⟨rfl⟩
86100

@@ -718,5 +732,3 @@ end NormNum
718732
end Meta
719733

720734
end Mathlib
721-
722-
open Mathlib.Meta.NormNum

MathlibTest/norm_num_ext.lean

Lines changed: 51 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import Mathlib.Tactic.NormNum.NatLog
1414
import Mathlib.Tactic.NormNum.NatSqrt
1515
import Mathlib.Tactic.NormNum.Parity
1616
import Mathlib.Tactic.NormNum.Prime
17+
import Mathlib.Algebra.Order.Floor.Semifield
18+
import Mathlib.Data.NNRat.Floor
1719
import Mathlib.Data.Rat.Floor
1820
import Mathlib.Tactic.NormNum.LegendreSymbol
1921
import Mathlib.Tactic.NormNum.Pow
@@ -422,23 +424,59 @@ end big_operators
422424

423425
section floor
424426

427+
section Semiring
428+
429+
variable (R : Type*) [Semiring R] [LinearOrder R] [IsStrictOrderedRing R] [FloorSemiring R]
430+
431+
example : ⌊(2 : R)⌋₊ = 2 := by norm_num1
432+
example : ⌈(2 : R)⌉₊ = 2 := by norm_num1
433+
434+
end Semiring
435+
436+
section Ring
437+
425438
variable (R : Type*) [Ring R] [LinearOrder R] [IsStrictOrderedRing R] [FloorRing R]
426-
variable (K : Type*) [Field K] [LinearOrder K] [IsStrictOrderedRing K] [FloorRing K]
427439

428-
example : ⌊(-1 : R)⌋ = -1 := by norm_num
429-
example : ⌊(2 : R)⌋ = 2 := by norm_num
430-
example : ⌊(15 / 16 : K)⌋ + 1 = 1 := by norm_num
431-
example : ⌊(-15 / 16 : K)⌋ + 1 = 0 := by norm_num
440+
example : ⌊(-1 : R)⌋ = -1 := by norm_num1
441+
example : ⌊(2 : R)⌋ = 2 := by norm_num1
442+
example : ⌈(-1 : R)⌉ = -1 := by norm_num1
443+
example : ⌈(2 : R)⌉ = 2 := by norm_num1
444+
example : ⌊(-2 : R)⌋₊ = 0 := by norm_num1
445+
example : ⌈(-3 : R)⌉₊ = 0 := by norm_num1
446+
example : round (1 : R) = 1 := by norm_num1
447+
example : Int.fract (2 : R) = 0 := by norm_num1
448+
example : round (-3 : R) = -3 := by norm_num1
449+
example : Int.fract (-3 : R) = 0 := by norm_num1
450+
432451

433-
example : ⌈(-1 : R)⌉ = -1 := by norm_num
434-
example : ⌈(2 : R)⌉ = 2 := by norm_num
435-
example : ⌈(15 / 16 : K)⌉ + 1 = 2 := by norm_num
436-
example : ⌈(-15 / 16 : K)⌉ + 1 = 1 := by norm_num
452+
end Ring
453+
454+
section Semifield
455+
456+
variable (K : Type*) [Semifield K] [LinearOrder K] [IsStrictOrderedRing K] [FloorSemiring K]
457+
458+
example : ⌊(35 / 16 : K)⌋₊ = 2 := by norm_num1
459+
example : ⌈(35 / 16 : K)⌉₊ = 3 := by norm_num1
460+
461+
end Semifield
462+
463+
section Field
464+
465+
variable (K : Type*) [Field K] [LinearOrder K] [IsStrictOrderedRing K] [FloorRing K]
437466

438-
example : Int.fract (2 : R) = 0 := by norm_num
439-
example : Int.fract (16 / 15 : K) = 1 / 15 := by norm_num
440-
example : Int.fract (3.7 : ℚ) = 0.7 := by norm_num
441-
example : Int.fract (-3.7 : ℚ) = 0.3 := by norm_num
467+
example : ⌊(15 / 16 : K)⌋ + 1 = 1 := by norm_num1
468+
example : ⌊(-15 / 16 : K)⌋ + 1 = 0 := by norm_num1
469+
example : ⌈(15 / 16 : K)⌉ + 1 = 2 := by norm_num1
470+
example : ⌈(-15 / 16 : K)⌉ + 1 = 1 := by norm_num1
471+
example : ⌊(-35 / 16 : K)⌋₊ = 0 := by norm_num1
472+
example : ⌈(-35 / 16 : K)⌉₊ = 0 := by norm_num1
473+
example : round (-35 / 16 : K) = -2 := by norm_num1
474+
example : Int.fract (16 / 15 : K) = 1 / 15 := by norm_num1
475+
example : Int.fract (-35 / 16 : K) = 13 / 16 := by norm_num1
476+
example : Int.fract (3.7 : ℚ) = 0.7 := by norm_num1
477+
example : Int.fract (-3.7 : ℚ) = 0.3 := by norm_num1
478+
479+
end Field
442480

443481
end floor
444482

0 commit comments

Comments
 (0)