Skip to content

Commit bfaf1a5

Browse files
committed
feat: simp config opts in norm_num (#7112)
This adds support for `norm_num (config := ...)`, which just forwards the config args to `simp`. The case of `{decide := false}` came up in https://leanprover.zulipchat.com/#narrow/stream/113489-new-members/topic/Prove.20decidable.20statement.20by.20evaluation/near/390390131 . Co-authored-by: Mario Carneiro <di.gama@gmail.com>
1 parent 98a6b53 commit bfaf1a5

File tree

2 files changed

+64
-67
lines changed

2 files changed

+64
-67
lines changed

Mathlib/Tactic/NormNum/Core.lean

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -270,13 +270,13 @@ def normNumAt (g : MVarId) (ctx : Simp.Context) (fvarIdsToSimp : Array FVarId)
270270

271271
open Tactic in
272272
/-- Constructs a simp context from the simp argument syntax. -/
273-
def getSimpContext (args : Syntax) (simpOnly := false) :
274-
TacticM Simp.Context := do
273+
def getSimpContext (cfg args : Syntax) (simpOnly := false) : TacticM Simp.Context := do
274+
let config ← elabSimpConfigCore cfg
275275
let simpTheorems ←
276276
if simpOnly then simpOnlyBuiltins.foldlM (·.addConst ·) {} else getSimpTheorems
277277
let mut { ctx, simprocs := _, starArg } ←
278278
elabSimpArgs args[0] (eraseLocal := false) (kind := .simp) (simprocs := {})
279-
{ simpTheorems := #[simpTheorems], congrTheorems := ← getSimpCongrTheorems }
279+
{ config, simpTheorems := #[simpTheorems], congrTheorems := ← getSimpCongrTheorems }
280280
unless starArg do return ctx
281281
let mut simpTheorems := ctx.simpTheorems
282282
for h in ← getPropHyps do
@@ -294,9 +294,8 @@ Elaborates a call to `norm_num only? [args]` or `norm_num1`.
294294
of `simp` will be used, not any of the post-processing that `simp only` does without lemmas
295295
-/
296296
-- FIXME: had to inline a bunch of stuff from `mkSimpContext` and `simpLocation` here
297-
def elabNormNum (args : Syntax) (loc : Syntax)
298-
(simpOnly := false) (useSimp := true) : TacticM Unit := do
299-
let ctx ← getSimpContext args (!useSimp || simpOnly)
297+
def elabNormNum (cfg args loc : Syntax) (simpOnly := false) (useSimp := true) : TacticM Unit := do
298+
let ctx ← getSimpContext cfg args (!useSimp || simpOnly)
300299
let g ← getMainGoal
301300
let res ← match expandOptLocation loc with
302301
| .targets hyps simplifyTarget => normNumAt g ctx (← getFVarIds hyps) simplifyTarget useSimp
@@ -316,27 +315,29 @@ over numerical types such as `ℕ`, `ℤ`, `ℚ`, `ℝ`, `ℂ` and some general
316315
and can prove goals of the form `A = B`, `A ≠ B`, `A < B` and `A ≤ B`, where `A` and `B` are
317316
numerical expressions. It also has a relatively simple primality prover.
318317
-/
319-
elab (name := normNum) "norm_num" only:&" only"? args:(simpArgs ?) loc:(location ?) : tactic =>
320-
elabNormNum args loc (simpOnly := only.isSome) (useSimp := true)
318+
elab (name := normNum)
319+
"norm_num" cfg:(config ?) only:&" only"? args:(simpArgs ?) loc:(location ?) : tactic =>
320+
elabNormNum cfg args loc (simpOnly := only.isSome) (useSimp := true)
321321

322322
/-- Basic version of `norm_num` that does not call `simp`. -/
323323
elab (name := normNum1) "norm_num1" loc:(location ?) : tactic =>
324-
elabNormNum mkNullNode loc (simpOnly := true) (useSimp := false)
324+
elabNormNum mkNullNode mkNullNode loc (simpOnly := true) (useSimp := false)
325325

326326
open Lean Elab Tactic
327327

328328
@[inherit_doc normNum1] syntax (name := normNum1Conv) "norm_num1" : conv
329329

330330
/-- Elaborator for `norm_num1` conv tactic. -/
331331
@[tactic normNum1Conv] def elabNormNum1Conv : Tactic := fun _ ↦ withMainContext do
332-
let ctx ← getSimpContext mkNullNode true
332+
let ctx ← getSimpContext mkNullNode mkNullNode true
333333
Conv.applySimpResult (← deriveSimp ctx (← instantiateMVars (← Conv.getLhs)) (useSimp := false))
334334

335-
@[inherit_doc normNum] syntax (name := normNumConv) "norm_num" &" only"? (simpArgs)? : conv
335+
@[inherit_doc normNum] syntax (name := normNumConv)
336+
"norm_num" (config)? &" only"? (simpArgs)? : conv
336337

337338
/-- Elaborator for `norm_num` conv tactic. -/
338339
@[tactic normNumConv] def elabNormNumConv : Tactic := fun stx ↦ withMainContext do
339-
let ctx ← getSimpContext stx[2] !stx[1].isNone
340+
let ctx ← getSimpContext stx[1] stx[3] !stx[2].isNone
340341
Conv.applySimpResult (← deriveSimp ctx (← instantiateMVars (← Conv.getLhs)) (useSimp := true))
341342

342343
/--
@@ -355,6 +356,6 @@ Unlike `norm_num`, this command does not fail when no simplifications are made.
355356
356357
`#norm_num` understands local variables, so you can use them to introduce parameters.
357358
-/
358-
macro (name := normNumCmd) "#norm_num" o:(&" only")?
359+
macro (name := normNumCmd) "#norm_num" cfg:(config)? o:(&" only")?
359360
args:(Parser.Tactic.simpArgs)? " :"? ppSpace e:term : command =>
360-
`(command| #conv norm_num $[only%$o]? $(args)? => $e)
361+
`(command| #conv norm_num $(cfg)? $[only%$o]? $(args)? => $e)

test/norm_num_ext.lean

Lines changed: 49 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ set_option maxRecDepth 8000 in
105105
example : Nat.Prime (2 ^ 25 - 39) := by norm_num1
106106
example : ¬ Nat.Prime ((2 ^ 19 - 1) * (2 ^ 25 - 39)) := by norm_num1
107107

108+
example : Nat.Prime 317 := by norm_num (config := {decide := false})
109+
108110
example : Nat.minFac 0 = 2 := by norm_num1
109111
example : Nat.minFac 1 = 1 := by norm_num1
110112
example : Nat.minFac (9 - 7) = 2 := by norm_num1
@@ -266,43 +268,40 @@ example : Nat.minFac 851 = 23 := by norm_num1
266268
-- example : Nat.factors 851 = [23, 37] := by norm_num1
267269

268270
/-
269-
example : ¬ squarefree 0 := by norm_num
270-
example : squarefree 1 := by norm_num
271-
example : squarefree 2 := by norm_num
272-
example : squarefree 3 := by norm_num
273-
example : ¬ squarefree 4 := by norm_num
274-
example : squarefree 5 := by norm_num
275-
example : squarefree 6 := by norm_num
276-
example : squarefree 7 := by norm_num
277-
example : ¬ squarefree 8 := by norm_num
278-
example : ¬ squarefree 9 := by norm_num
279-
example : squarefree 10 := by norm_num
280-
example : squarefree (2*3*5*17) := by norm_num
281-
example : ¬ squarefree (2*3*5*5*17) := by norm_num
282-
example : squarefree 251 := by norm_num
283-
example : squarefree (3 : ℤ) :=
284-
begin
271+
example : ¬ Squarefree 0 := by norm_num1
272+
example : Squarefree 1 := by norm_num1
273+
example : Squarefree 2 := by norm_num1
274+
example : Squarefree 3 := by norm_num1
275+
example : ¬ Squarefree 4 := by norm_num1
276+
example : Squarefree 5 := by norm_num1
277+
example : Squarefree 6 := by norm_num1
278+
example : Squarefree 7 := by norm_num1
279+
example : ¬ Squarefree 8 := by norm_num1
280+
example : ¬ Squarefree 9 := by norm_num1
281+
example : Squarefree 10 := by norm_num1
282+
example : Squarefree (2*3*5*17) := by norm_num1
283+
example : ¬ Squarefree (2*3*5*5*17) := by norm_num1
284+
example : Squarefree 251 := by norm_num1
285+
example : Squarefree (3 : ℤ) := by
285286
-- `norm_num` should fail on this example, instead of producing an incorrect proof.
286-
success_if_fail { norm_num },
287-
exact irreducible.squarefree (prime.irreducible
288-
(Int.prime_iff_Nat_abs_prime.mpr (by norm_num)))
289-
end
290-
example : @squarefree ℕ multiplicative.monoid 1 :=
291-
begin
287+
fail_if_success norm_num1
288+
exact Irreducible.squarefree (Prime.irreducible
289+
(Int.prime_iff_natAbs_prime.mpr (by norm_num)))
290+
291+
example : @Squarefree ℕ Multiplicative.monoid 1 := by
292292
-- `norm_num` should fail on this example, instead of producing an incorrect proof.
293-
success_if_fail { norm_num },
293+
-- fail_if_success norm_num1
294294
-- the statement was deliberately wacky, let's fix it
295-
change squarefree (multiplicative.of_add 1 : multiplicative ℕ),
296-
rIntros x ⟨dx, hd⟩,
297-
revert x dx,
298-
rw multiplicative.of_add.surjective.forall₂,
299-
Intros x dx h,
300-
simp_rw [←of_add_add, multiplicative.of_add.injective.eq_iff] at h,
301-
cases x,
302-
{ simp [is_unit_one], exact is_unit_one },
303-
{ simp only [Nat.succ_add, Nat.add_succ] at h,
304-
cases h },
305-
end
295+
change Squarefree (Multiplicative.ofAdd 1 : Multiplicative ℕ)
296+
rintro x ⟨dx, hd⟩
297+
revert x dx
298+
rw [Multiplicative.ofAdd.surjective.forall₂]
299+
intros x dx h
300+
simp_rw [← ofAdd_add, Multiplicative.ofAdd.injective.eq_iff] at h
301+
cases x
302+
· simp [isUnit_one]
303+
· simp only [Nat.succ_add, Nat.add_succ] at h
304+
cases h
306305
-/
307306

308307
example : Nat.fib 0 = 0 := by norm_num1
@@ -331,23 +330,22 @@ open BigOperators
331330

332331
-- Lists:
333332
-- `by decide` closes the three goals below.
334-
/-
335-
example : ([1, 2, 1, 3]).sum = 7 := by norm_num only
336-
example : (List.range 10).sum = 45 := by norm_num only
337-
example : (List.finRange 10).sum = 45 := by norm_num only
338-
-/
339-
-- example : (([1, 2, 1, 3] : List ℚ).map (fun i => i^2)).sum = 15 := by norm_num [-List.map] --TODO
333+
example : ([1, 2, 1, 3]).sum = 7 := by norm_num (config := {decide := true}) only
334+
example : (List.range 10).sum = 45 := by norm_num (config := {decide := true}) only
335+
example : (List.finRange 10).sum = 45 := by norm_num (config := {decide := true}) only
336+
337+
example : (([1, 2, 1, 3] : List ℚ).map (fun i => i^2)).sum = 15 := by norm_num
340338

341339
-- Multisets:
342340
-- `by decide` closes the three goals below.
343-
/-
344-
example : (1 ::ₘ 2 ::ₘ 1 ::ₘ 3 ::ₘ {}).sum = 7 := by norm_num only
345-
example : ((1 ::ₘ 2 ::ₘ 1 ::ₘ 3 ::ₘ {}).map (fun i => i^2)).sum = 15 := by norm_num only
346-
example : (Multiset.range 10).sum = 45 := by norm_num only
347-
example : (↑[1, 2, 1, 3] : Multiset ℕ).sum = 7 := by norm_num only
348-
-/
349-
-- example : (({1, 2, 1, 3} : Multiset ℚ).map (fun i => i^2)).sum = 15 := by -- TODO
350-
-- norm_num [-Multiset.map_cons]
341+
example : (1 ::ₘ 2 ::ₘ 1 ::ₘ 3 ::ₘ {}).sum = 7 := by norm_num (config := {decide := true}) only
342+
example : ((1 ::ₘ 2 ::ₘ 1 ::ₘ 3 ::ₘ {}).map (fun i => i^2)).sum = 15 := by
343+
norm_num (config := {decide := true}) only
344+
example : (Multiset.range 10).sum = 45 := by norm_num (config := {decide := true}) only
345+
example : (↑[1, 2, 1, 3] : Multiset ℕ).sum = 7 := by norm_num (config := {decide := true}) only
346+
347+
example : (({1, 2, 1, 3} : Multiset ℚ).map (fun i => i^2)).sum = 15 := by
348+
norm_num
351349

352350
-- Finsets:
353351
example : Finset.prod (Finset.cons 2 ∅ (Finset.not_mem_empty _)) (λ x => x) = 2 := by norm_num1
@@ -384,14 +382,12 @@ example (f : ℕ → α) : ∑ i in Finset.mk {0, 1, 2} dec_trivial, f i = f 0 +
384382
-/
385383

386384
-- Combined with other `norm_num` extensions:
387-
/-
388385
example : ∏ i in Finset.range 9, Nat.sqrt (i + 1) = 96 := by norm_num1
389-
example : ∏ i in {1, 4, 9, 16}, Nat.sqrt i = 24 := by norm_num1
390-
example : ∏ i in Finset.Icc 0 8, Nat.sqrt (i + 1) = 96 := by norm_num1
386+
-- example : ∏ i in {1, 4, 9, 16}, Nat.sqrt i = 24 := by norm_num1
387+
-- example : ∏ i in Finset.Icc 0 8, Nat.sqrt (i + 1) = 96 := by norm_num1
391388

392389
-- Nested operations:
393-
example : ∑ i : Fin 2, ∑ j : Fin 2, ![![0, 1], ![2, 3]] i j = 6 := by norm_num1
394-
-/
390+
-- example : ∑ i : Fin 2, ∑ j : Fin 2, ![![0, 1], ![2, 3]] i j = 6 := by norm_num1
395391

396392
end big_operators
397393

0 commit comments

Comments
 (0)