/- Some utilities to prove termination -/ import Lean import Mathlib.Tactic.Core import Base.Utils import Base.Arith namespace Utils open Lean Lean.Elab Command Term Lean.Meta Tactic -- Inspired by the `clear` tactic def clearFvarIds (fvarIds : Array FVarId) : TacticM Unit := do let fvarIds ← withMainContext <| sortFVarIds fvarIds for fvarId in fvarIds.reverse do withMainContext do let mvarId ← (← getMainGoal).clear fvarId replaceMainGoal [mvarId] /- Utility function for proofs of termination (i.e., inside `decreasing_by`). Clean up the local context by removing all assumptions containing occurrences of `invImage` (those are introduced automatically when doing proofs of termination). We do so because we often need to simplify the context in the proofs, and if we simplify those assumptions they tend to make the context blow up. -/ def removeInvImageAssumptions : TacticM Unit := do withMainContext do -- Get the local declarations let ctx ← Lean.MonadLCtx.getLCtx let decls ← ctx.getDecls -- Retrieve the list of local declarations which contain `invertImage` let containsInvertImage (decl : LocalDecl) : MetaM Bool := do let expr := decl.toExpr reduceVisit ( fun _ b e => pure ( b || match e with | .const name _ => name == ``invImage | _ => false)) false (← inferType expr) let filtDecls ← liftM (decls.filterM containsInvertImage) -- It can happen that other variables depend on the variables we want to clear: -- filter them. let allFVarsInTypes ← decls.foldlM (fun hs d => do let hs ← getFVarIds (← inferType d.toExpr) hs -- Explore the body if it is not opaque match d.value? with | none => pure hs | some e => getFVarIds e hs ) HashSet.empty let fvarIds := filtDecls.map fun d => d.fvarId let fvarIds := fvarIds.filter (fun id => ¬ allFVarsInTypes.contains id) -- Clear them clearFvarIds fvarIds.toArray elab "remove_invImage_assumptions" : tactic => removeInvImageAssumptions -- Auxiliary function def scalarDecrTac_apply_lemmas : TacticM Unit := do withMainContext do let goal ← getMainGoal let rec applyFirst (names : List Name) : TacticM (List MVarId) := match names with | [] => pure [goal] | name :: names => -- Should use try ... catch or Lean.observing? -- Generally speaking we should use Lean.observing? to restore the state, -- but with tactics the try ... catch variant seems to work try do let th ← mkConstWithFreshMVarLevels name goal.apply th catch _ => do applyFirst names let ngoals ← applyFirst [``Arith.to_int_to_nat_lt, ``Arith.to_int_sub_to_nat_lt] setGoals ngoals elab "scalar_decr_tac_apply_lemmas" : tactic => scalarDecrTac_apply_lemmas -- For termination proofs syntax "scalar_decr_tac" : tactic macro_rules | `(tactic| scalar_decr_tac) => `(tactic| simp_wf <;> -- Simplify the context - otherwise simp_all below will blow up remove_invImage_assumptions <;> -- Transform the goal a bit scalar_decr_tac_apply_lemmas <;> -- Finish simp_all <;> scalar_tac) end Utils