[System] You are Leanstral, a Lean 4 proof assistant. Complete the task below using the provided Lean source context. The target mathlib commit is 5e932f97dd25535344f80f9dd8da3aab83df0fe6 (module Mathlib.AlgebraicTopology.SimplicialSet.CategoryWithFibrations).

[User]
The following Lean code fails to compile. Fix it.

```lean
lemma I_le_monomorphisms : I.{u} ≤ monomorphisms _
:= by thisIsWrongLemmaNameXYZ
rintro _ _ _ ⟨n⟩
  exact monomorphisms.infer_property _
```

## Compiler diagnostic
```
[CompilationUnit(i_begin=0, i_end=60, messages=[Message(data='unknown tactic', pos=Position(line=2, column=7), pos_end=None, severity=<Severity.ERROR: 3>, kind=None), Message(data='invalid use of explicit universe parameters, `I` is a local variable', pos=Position(line=1, column=27), pos_end=Position(line=1, column=28), severity=<Severity.ERROR: 3>, kind=None), Message(data="Function expected at\n  monomorphisms\nbut this term has type\n  ?m.2\n\nNote: Expected a function because this term is being applied to the argument\n  _\n\nHint: The identifier `monomorphisms` is unknown, and Lean's `autoImplicit` option causes an unknown identifier to be treated as an implicitly bound variable with an unknown type. However, the unknown type cannot be a function, and a function is what Lean expects here. This is often the result of a typo or a missing `import` or `open` statement.", pos=Position(line=1, column=35), pos_end=Position(line=1, column=50), severity=<Severity.ERROR: 3>, kind=None), Message(data='unsolved goals\nx✝¹ : Sort u_1\nI : x✝¹\nx✝ : Sort u_2\nmonomorphisms : x✝\n⊢ sorry ≤ sorry', pos=Position(line=2, column=3), pos_end=Position(line=2, column=29), severity=<Severity.ERROR: 3>, kind='Tactic.unsolvedGoals')], invocations=None, goal_state=None, goal_src_boundaries=None, new_constants=None), CompilationUnit(i_begin=60, i_end=142, messages=[], invocations=None, goal_state=None, goal_src_boundaries=None, new_constants=None)]
```

Return a corrected, compiling version of the code.

<s>[SYSTEM_PROMPT]You are Leanstral, a Lean 4 proof assistant. Complete the task below using the provided Lean source context. The target mathlib commit is 5e932f97dd25535344f80f9dd8da3aab83df0fe6 (module Mathlib.AlgebraicTopology.SimplicialSet.CategoryWithFibrations).[/SYSTEM_PROMPT][MODEL_SETTINGS]{"reasoning_effort": "none"}[/MODEL_SETTINGS][INST]The following Lean code fails to compile. Fix it.

```lean
lemma I_le_monomorphisms : I.{u} ≤ monomorphisms _
:= by thisIsWrongLemmaNameXYZ
rintro _ _ _ ⟨n⟩
  exact monomorphisms.infer_property _
```

## Compiler diagnostic
```
[CompilationUnit(i_begin=0, i_end=60, messages=[Message(data='unknown tactic', pos=Position(line=2, column=7), pos_end=None, severity=<Severity.ERROR: 3>, kind=None), Message(data='invalid use of explicit universe parameters, `I` is a local variable', pos=Position(line=1, column=27), pos_end=Position(line=1, column=28), severity=<Severity.ERROR: 3>, kind=None), Message(data="Function expected at\n  monomorphisms\nbut this term has type\n  ?m.2\n\nNote: Expected a function because this term is being applied to the argument\n  _\n\nHint: The identifier `monomorphisms` is unknown, and Lean's `autoImplicit` option causes an unknown identifier to be treated as an implicitly bound variable with an unknown type. However, the unknown type cannot be a function, and a function is what Lean expects here. This is often the result of a typo or a missing `import` or `open` statement.", pos=Position(line=1, column=35), pos_end=Position(line=1, column=50), severity=<Severity.ERROR: 3>, kind=None), Message(data='unsolved goals\nx✝¹ : Sort u_1\nI : x✝¹\nx✝ : Sort u_2\nmonomorphisms : x✝\n⊢ sorry ≤ sorry', pos=Position(line=2, column=3), pos_end=Position(line=2, column=29), severity=<Severity.ERROR: 3>, kind='Tactic.unsolvedGoals')], invocations=None, goal_state=None, goal_src_boundaries=None, new_constants=None), CompilationUnit(i_begin=60, i_end=142, messages=[], invocations=None, goal_state=None, goal_src_boundaries=None, new_constants=None)]
```

Return a corrected, compiling version of the code.[/INST]


[System] You are Leanstral, a Lean 4 proof assistant. Complete the task below using the provided Lean source context. The target mathlib commit is 5e932f97dd25535344f80f9dd8da3aab83df0fe6 (module Mathlib.Tactic.FunProp.Decl).

[User]
Prove the following theorem. The retrieved context below may help.

```lean
def isFunProp (e : Expr) : MetaM Bool
```

## Retrieved context
-- snippet 1
```lean
-- Mathlib.Tactic.FunProp.Decl.FunPropDeclsExt (L45-L57)
abbrev FunPropDeclsExt := SimpleScopedEnvExtension FunPropDecl FunPropDecls
/-- Extension storing function properties tracked and used by the `fun_prop` attribute and tactic,
including, for example, `Continuous`, `Measurable`, `Differentiable`, etc. -/
initialize funPropDeclsExt : FunPropDeclsExt ←
  registerSimpleScopedEnvExtension {
    name := by exact decl_name%
    initial := {}
    addEntry := fun d e =>
      {d with decls := d.decls.insertKeyValue e.path e}
  }

/-- Register new function property. -/
```

-- snippet 2
```lean
-- Mathlib.Tactic.FunProp.Decl.addFunPropDecl (L58-L92)
def addFunPropDecl (declName : Name) : MetaM Unit := do
let info ← getConstInfo declName

  let (xs, bi, b) ← forallMetaTelescope info.type

  if ¬b.isProp then
    throwError "invalid fun_prop declaration, has to be `Prop`-valued function"

  let lvls := info.levelParams.map (fun l => Level.param l)
  let e := mkAppN (.const declName lvls) xs
  let path ← DiscrTree.mkPath e

  -- find the argument position of the function `f` in `P f`
  let mut some funArgId ← (xs.zip bi).findIdxM? fun (x,bi) => do
    if (← inferType x).isForall && bi.isExplicit then
      return true
    else
      return false
    | throwError "invalid fun_prop declaration, can't find argument of type `α → β`"

  let decl : FunPropDecl := {
    funPropName := declName
    path := path
    funArgId := funArgId
  }

  modifyEnv fun env => funPropDeclsExt.addEntry env decl

  trace[Meta.Tactic.fun_prop.attr]
    "added new function property `{declName}`\nlook up pattern is `{path}`"

/-- Is `e` a function property statement? If yes return function property declaration and
the function it talks about. -/
```

-- snippet 3
```lean
-- Mathlib.Tactic.FunProp.Decl.getFunProp (L93-L111)
def getFunProp? (e : Expr) : MetaM (Option (FunPropDecl × Expr)) := do
let ext := funPropDeclsExt.getState (← getEnv)

  let decls ← ext.decls.getMatch e (← read)

  if h : decls.size = 0 then
    return none
  else
    if decls.size > 1 then
      throwError "fun_prop bug: expression {← ppExpr e} matches multiple function properties\n\
        {decls.map (fun d => d.funPropName)}"

    let decl := decls[0]
    unless decl.funArgId < e.getAppNumArgs do return none
    let f := e.getArg! decl.funArgId

    return (decl,f)

/-- Is `e` a function property statement? -/
```

-- snippet 4
```lean
-- Mathlib.Tactic.FunProp.Decl.isFunPropGoal (L115-L119)
def isFunPropGoal (e : Expr) : MetaM Bool := do
forallTelescope e fun _ b =>
  return (← getFunProp? b).isSome

/-- Returns function property declaration from `e = P f`. -/
```

-- snippet 5
```lean
-- Mathlib.Tactic.FunProp.Decl.getFunPropDecl (L120-L126)
def getFunPropDecl? (e : Expr) : MetaM (Option FunPropDecl) := do
match ← getFunProp? e with
  | some (decl, _) => return decl
  | none => return none

/-- Returns function `f` from `e = P f` and `P` is function property. -/
```

-- snippet 6
```lean
-- Mathlib.Tactic.FunProp.Decl.getFunPropFun (L127-L134)
def getFunPropFun? (e : Expr) : MetaM (Option Expr) := do
match ← getFunProp? e with
  | some (_, f) => return f
  | none => return none

open Elab Term in
/-- Turn tactic syntax into a discharger function. -/
```

-- snippet 7
```lean
-- Mathlib.Tactic.FunProp.Decl.tacticToDischarge (L135-L160)
def tacticToDischarge (tacticCode : TSyntax `tactic) : Expr → MetaM (Option Expr) := fun e =>
withTraceNode `Meta.Tactic.fun_prop
    (fun _ => do pure s!"discharging: {← ppExpr e}") do
    let mvar ← mkFreshExprSyntheticOpaqueMVar e `funProp.discharger
    let runTac? : TermElabM (Option Expr) :=
      try
        instantiateMVarDeclMVars mvar.mvarId!

        let _ ←
          withSynthesize (postpone := .no) do
            Tactic.run mvar.mvarId! (Tactic.evalTactic tacticCode *> Tactic.pruneSolvedGoals)

        let result ← instantiateMVars mvar
        if result.hasExprMVar then
          return none
        else
          return some result
      catch _ =>
        return none
    let (result?, _) ← runTac?.run {} {}

    return result?

end Meta.FunProp

end Mathlib
```

-- snippet 8
```lean
-- Mathlib.Tactic.FunProp.Core.bvarAppCase (L389-L402)
def bvarAppCase (funPropDecl : FunPropDecl) (e : Expr) (fData : FunctionData)
    (funProp : Expr → FunPropM (Option Result)) : FunPropM (Option Result) := do
if (← fData.isMorApplication) != .none then
    applyMorRules funPropDecl e fData funProp
  else
    if let some (f, g) ← fData.nontrivialDecomposition then
      applyCompRule funPropDecl e f g funProp
    else
      applyApplyRule funPropDecl e funProp

/--
Get candidate theorems from the environment for function property `funPropDecl` and
function `funName`. -/
```

-- snippet 9
```lean
-- Mathlib.Tactic.FunProp.FunctionData.getFunctionData (L77-L118)
def getFunctionData (f : Expr) : MetaM FunctionData := do
lambdaTelescope f fun xs b => do

    let xId := xs[0]!.fvarId!

    Mor.withApp b fun fn args => do

      let mut fn := fn
      let mut args := args

      -- revert projection in fn
      if let .proj n i x := fn then
        let some info := getStructureInfo? (← getEnv) n | unreachable!
        let some projName := info.getProjFn? i | unreachable!
        let p ← mkAppM projName #[x]
        fn := p.getAppFn
        args := p.getAppArgs.map (fun a => {expr:=a}) ++ args

      let mainArgs := args
        |>.mapIdx (fun i ⟨arg,_⟩ => if arg.containsFVar xId then some i else none)
        |>.filterMap id

      return {
        lctx := ← getLCtx
        insts := ← getLocalInstances
        fn := fn
        args := args
        mainVar := xs[0]!
        mainArgs := mainArgs
      }

/-- Result of `getFunctionData?`. It returns function data if the function is in the form
`fun x ↦ f y₁ ... yₙ`. Two other cases are `fun x ↦ let y := ...` or `fun x y ↦ ...` -/
inductive MaybeFunctionData where
  /-- Can't generate function data as function body has let binder. -/
  | letE (f : Expr)
  /-- Can't generate function data as function body has lambda binder. -/
  | lam (f : Expr)
  /-- Function data has been successfully generated. -/
  | data (fData : FunctionData)

/-- Turn `MaybeFunctionData` to the function. -/
```

-- snippet 10
```lean
-- Mathlib.Tactic.FunProp.Core.applyCompRule (L231-L253)
def applyCompRule (funPropDecl : FunPropDecl) (e f g : Expr)
    (funProp : Expr → FunPropM (Option Result)) : FunPropM (Option Result) := do
let thms ← getLambdaTheorems funPropDecl.funPropName .comp
  if thms.size = 0 then
    let msg := s!"missing composition rule to prove `{← ppExpr e}`"
    logError msg
    trace[Meta.Tactic.fun_prop] msg
    return none

  for thm in thms do
    let .comp id_f id_g := thm.thmArgs | return none
    if let some r ← tryTheoremWithHint? e (.decl thm.thmName) #[(id_f, f), (id_g, g)] funProp then
      return r

  return none

/--
Try to prove `e` using *pi lambda theorem*.

For example, `e = q(Continuous fun x y ↦ f x y)` and `funPropDecl` is `FunPropDecl` for
`Continuous`
-/
```

-- snippet 11
```lean
-- Mathlib.Tactic.FunProp.Core.synthesizeArgs (L50-L106)
def synthesizeArgs (thmId : Origin) (xs : Array Expr)
    (funProp : Expr → FunPropM (Option Result)) :
    FunPropM Bool := do
let mut postponed : Array Expr := #[]
  for x in xs do
    let type ← inferType x
    if (← instantiateMVars x).isMVar then

      -- try type class
      if (← isClass? type).isSome then
        if (← synthesizeInstance thmId x type) then
          continue
      else if (← isFunPropGoal type) then
        -- try function property
        if let some ⟨proof⟩ ← funProp type then
          if (← isDefEq x proof) then
            continue
          else do
            trace[Meta.Tactic.fun_prop]
              "{← ppOrigin thmId}, failed to assign proof{indentExpr type}"
            return false
      else
        -- try user provided discharger
        let ctx : Context ← read
        if (← isProp type) then
          if let some proof ← ctx.disch type then
            if (← isDefEq x proof) then
              continue
            else do
              trace[Meta.Tactic.fun_prop]
                "{← ppOrigin thmId}, failed to assign proof{indentExpr type}"
              return false
          else
            logError s!"Failed to prove necessary assumption `{← ppExpr type}` \
                        when applying theorem `{← ppOrigin' thmId}`."

      if ¬(← isProp type) then
        postponed := postponed.push x
        continue
      else
        trace[Meta.Tactic.fun_prop]
          "{← ppOrigin thmId}, failed to discharge hypotheses{indentExpr type}"
        return false

  for x in postponed do
    if (← instantiateMVars x).isMVar then
      logError s!"Failed to infer `({← ppExpr x} : {← ppExpr (← inferType x)})` \
      when applying theorem `{← ppOrigin' thmId}`."

      trace[Meta.Tactic.fun_prop]
        "{← ppOrigin thmId}, failed to infer `({← ppExpr x} : {← ppExpr (← inferType x)})`"
      return false

  return true

/-- Try to apply theorem - core function -/
```

-- snippet 12
```lean
-- Mathlib.Tactic.FunProp.Core.cacheResult (L615-L619)
def cacheResult (e : Expr) (r : Result) : FunPropM Result := do -- return proof?
modify (fun s => { s with cache := s.cache.insert e { expr := q(True), proof? := r.proof} })
  return r

/-- Cache for failed goals such that `fun_prop` can fail fast next time. -/
```

-- snippet 13
```lean
-- Mathlib.Tactic.FunProp.Core.letCase (L279-L320)
def letCase (funPropDecl : FunPropDecl) (e : Expr) (f : Expr)
    (funProp : Expr → FunPropM (Option Result)) :
    FunPropM (Option Result) := do
match f with
  | .lam xName xType (.letE yName yType yValue yBody _) xBi => do
    let yType  := yType.consumeMData
    let yValue := yValue.consumeMData
    let yBody  := yBody.consumeMData
    -- We perform reduction because the type is quite often of the form
    -- `(fun x ↦ Y) #0` which is just `Y`
    -- Usually this is caused by the usage of `FunLike`
    let yType := yType.headBeta
    if (yType.hasLooseBVar 0) then
      throwError "dependent type encountered {← ppExpr (Expr.forallE xName xType yType default)}"

    -- let binding can be pulled out of the lambda function
    if ¬(yValue.hasLooseBVar 0) then
      let body := yBody.swapBVars 0 1
      let e' := mkLet yName yType yValue
        (e.setArg (funPropDecl.funArgId) (.lam xName xType body xBi))
      return ← funProp e'

    match (yBody.hasLooseBVar 0), (yBody.hasLooseBVar 1) with
    | true, true =>
      let f ← mkUncurryFun 2 (Expr.lam xName xType (.lam yName yType yBody default) xBi)
      let g := Expr.lam xName xType (binderInfo := default)
        (mkAppN (← mkConstWithFreshMVarLevels ``Prod.mk) #[xType,yType,.bvar 0, yValue])
      applyCompRule funPropDecl e f g funProp

    | true, false =>
      let f := Expr.lam yName yType yBody default
      let g := Expr.lam xName xType yValue default
      applyCompRule funPropDecl e f g funProp

    | false, _ =>
      let f := Expr.lam xName xType (yBody.lowerLooseBVars 1 1) xBi
      funProp (e.setArg (funPropDecl.funArgId) f)

  | _ => throwError "expected expression of the form `fun x ↦ lam y := ..; ..`"

/-- Prove function property of using *morphism theorems*. -/
```

-- snippet 14
```lean
-- Mathlib.Tactic.FunProp.Core.getDeclTheorems (L403-L422)
def getDeclTheorems (funPropDecl : FunPropDecl) (funName : Name)
    (mainArgs : Array Nat) (appliedArgs : Nat) : MetaM (Array FunctionTheorem) := do
let thms ← getTheoremsForFunction funName funPropDecl.funPropName

  let thms := thms
    |>.filter (fun thm => (isOrderedSubsetOf mainArgs thm.mainArgs))
    |>.qsort (fun t s =>
      let dt := (Int.ofNat t.appliedArgs - Int.ofNat appliedArgs).natAbs
      let ds := (Int.ofNat s.appliedArgs - Int.ofNat appliedArgs).natAbs
      match compare dt ds with
      | .lt => true
      | .gt => false
      | .eq => t.mainArgs.size < s.mainArgs.size)
  -- todo: sorting and filtering
  return thms

/--
Get candidate theorems from the local context for function property `funPropDecl` and
function `funName`. -/
```

-- snippet 15
```lean
-- Mathlib.Tactic.FunProp.Core.applyMorRules (L321-L346)
def applyMorRules (funPropDecl : FunPropDecl) (e : Expr) (fData : FunctionData)
    (funProp : Expr → FunPropM (Option Result)) : FunPropM (Option Result) := do
trace[Debug.Meta.Tactic.fun_prop] "applying morphism theorems to {← ppExpr e}"

  match ← fData.isMorApplication with
  | .none => throwError "fun_prop bug: invalid use of mor rules on {← ppExpr e}"
  | .underApplied =>
    applyPiRule funPropDecl e funProp
  | .overApplied =>
    let some (f, g) ← fData.peeloffArgDecomposition | return none
    applyCompRule funPropDecl e f g funProp
  | .exact =>

    let candidates ← getMorphismTheorems e

    trace[Meta.Tactic.fun_prop]
      "candidate morphism theorems: {← candidates.mapM fun c => ppOrigin (.decl c.thmName)}"

    for c in candidates do
      if let some r ← tryTheorem? e (.decl c.thmName) funProp then
        return r

    trace[Debug.Meta.Tactic.fun_prop] "no theorem matched"
    return none

/-- Prove function property of using *transition theorems*. -/
```

-- snippet 16
```lean
-- Mathlib.Tactic.FunProp.Core.constAppCase (L565-L614)
def constAppCase (funPropDecl : FunPropDecl) (e : Expr) (fData : FunctionData)
    (funProp : Expr → FunPropM (Option Result)) : FunPropM (Option Result) := do
let some (funName, _) := fData.fn.const?
    | throwError "fun_prop bug: invelid use of const app case"
  let globalThms ← getDeclTheorems funPropDecl funName fData.mainArgs fData.args.size

  trace[Meta.Tactic.fun_prop]
    s!"candidate theorems for {funName} {← globalThms.mapM fun thm => ppOrigin' thm.thmOrigin}"

  if let some r ← tryTheorems funPropDecl e fData globalThms funProp then
    return r

  -- Try local theorems - this is useful for recursive functions
  let localThms ← getLocalTheorems funPropDecl (.decl funName) fData.mainArgs fData.args.size
  if localThms.size ≠ 0 then
    trace[Meta.Tactic.fun_prop]
      s!"candidate local theorems for {funName} \
        {← localThms.mapM fun thm => ppOrigin' thm.thmOrigin}"
  if let some r ← tryTheorems funPropDecl e fData localThms funProp then
    return r

  -- log error if no global or local theorems were found
  if globalThms.size = 0 && localThms.size = 0 then
     logError s!"No theorems found for `{funName}` in order to prove `{← ppExpr e}`"

  if (← fData.isMorApplication) != .none then
    if let some r ← applyMorRules funPropDecl e fData funProp then
      return r

  if let some (f, g) ← fData.nontrivialDecomposition then
    trace[Meta.Tactic.fun_prop]
      s!"failed applying `{funPropDecl.funPropName}` theorems for `{funName}`
         trying again after decomposing function as: `({← ppExpr f}) ∘ ({← ppExpr g})`"

    if let some r ← applyCompRule funPropDecl e f g funProp then
      return r
  else
    trace[Meta.Tactic.fun_prop]
      s!"failed applying `{funPropDecl.funPropName}` theorems for `{funName}`
         now trying to prove `{funPropDecl.funPropName}` from another function property"

    if let some r ← applyTransitionRules e funProp then
      return r

  return none

/-- Cache result if it does not have any subgoals. -/
```

-- snippet 17
```lean
-- Mathlib.Tactic.FunProp.Core.applyApplyRule (L215-L230)
def applyApplyRule (funPropDecl : FunPropDecl) (e : Expr)
    (funProp : Expr → FunPropM (Option Result)) : FunPropM (Option Result) := do
let thms := (← getLambdaTheorems funPropDecl.funPropName .apply)
  for thm in thms do
    if let some r ← tryTheoremWithHint? e (.decl thm.thmName) #[] funProp then
      return r

  return none

/--
Try to prove `e` using *composition lambda theorem*.

For example, `e = q(Continuous fun x ↦ f (g x))` and `funPropDecl` is `FunPropDecl` for
`Continuous`

You also have to provide the functions `f` and `g`. -/
```

-- snippet 18
```lean
-- Mathlib.Tactic.FunProp.Core.applyIdRule (L175-L194)
def applyIdRule (funPropDecl : FunPropDecl) (e : Expr)
    (funProp : Expr → FunPropM (Option Result)) : FunPropM (Option Result) := do
let thms ← getLambdaTheorems funPropDecl.funPropName .id
  if thms.size = 0 then
    let msg := s!"missing identity rule to prove `{← ppExpr e}`"
    logError msg
    trace[Meta.Tactic.fun_prop] msg
    return none

  for thm in thms do
    if let some r ← tryTheoremWithHint? e (.decl thm.thmName) #[] funProp then
      return r

  return none

/--
Try to prove `e` using the *constant lambda theorem*.

For example, `e = q(Continuous fun x ↦ y)` and `funPropDecl` is `FunPropDecl` for `Continuous`.
-/
```

-- snippet 19
```lean
-- Mathlib.Tactic.FunProp.Core.getLocalTheorems (L423-L473)
def getLocalTheorems (funPropDecl : FunPropDecl) (funOrigin : Origin)
    (mainArgs : Array Nat) (appliedArgs : Nat) : FunPropM (Array FunctionTheorem) := do
let mut thms : Array FunctionTheorem := #[]
  let lctx ← getLCtx
  for var in lctx do
    if (var.kind = Lean.LocalDeclKind.auxDecl) then
      continue
    let type ← instantiateMVars var.type
    let thm? : Option FunctionTheorem ←
      forallTelescope type fun _ b => do
      let b ← whnfR b
      let some (decl, f) ← getFunProp? b | return none
      unless decl.funPropName = funPropDecl.funPropName do return none

      let .data fData ← getFunctionData? f (← unfoldNamePred)
        | return none
      unless (fData.getFnOrigin == funOrigin) do return none

      unless isOrderedSubsetOf mainArgs fData.mainArgs do return none

      let dec? ← fData.nontrivialDecomposition

      let thm : FunctionTheorem := {
        funPropName := funPropDecl.funPropName
        thmOrigin := .fvar var.fvarId
        funOrigin := funOrigin
        mainArgs := fData.mainArgs
        appliedArgs := fData.args.size
        priority := eval_prio default
        form := if dec?.isSome then .comp else .uncurried
      }

      return some thm

    if let some thm := thm? then
      thms := thms.push thm

  thms := thms
    |>.qsort (fun t s =>
      let dt := (Int.ofNat t.appliedArgs - Int.ofNat appliedArgs).natAbs
      let ds := (Int.ofNat s.appliedArgs - Int.ofNat appliedArgs).natAbs
      match compare dt ds with
      | .lt => true
      | .gt => false
      | .eq => t.mainArgs.size < s.mainArgs.size)

  return thms

/-- Try to apply *function theorems* `thms` to `e`. -/
```

-- snippet 20
```lean
-- Mathlib.Tactic.FunProp.Core.tryTheoremCore (L107-L124)
def tryTheoremCore (xs : Array Expr) (val : Expr) (type : Expr) (e : Expr)
    (thmId : Origin) (funProp : Expr → FunPropM (Option Result)) : FunPropM (Option Result) := do
withTraceNode `Meta.Tactic.fun_prop
    (fun _ => return s!"applying: {← ppOrigin' thmId}") do

  if (← isDefEq type e) then

    if ¬(← synthesizeArgs thmId xs funProp) then
      return none
    let proof ← instantiateMVars (mkAppN val xs)

    return some { proof := proof }
  else
    trace[Meta.Tactic.fun_prop] "failed to unify {← ppOrigin thmId}\n{type}\nwith\n{e}"
    return none

/-- Try to apply a theorem provided some of the theorem arguments. -/
```

-- snippet 21
```lean
-- Mathlib.Tactic.FunProp.Core.applyPiRule (L254-L278)
def applyPiRule (funPropDecl : FunPropDecl) (e : Expr)
    (funProp : Expr → FunPropM (Option Result)) : FunPropM (Option Result) := do
let thms ← getLambdaTheorems funPropDecl.funPropName .pi
  if thms.size = 0 then
    let msg := s!"missing pi rule to prove `{← ppExpr e}`"
    logError msg
    trace[Meta.Tactic.fun_prop] msg
    return none

  for thm in thms do
    if let some r ← tryTheoremWithHint? e (.decl thm.thmName) #[] funProp then
      return r

  return none

/--
Try to prove `e = q(P (fun x ↦ let y := φ x; ψ x y)`.

For example,
  - `funPropDecl` is `FunPropDecl` for `Continuous`
  - `e = q(Continuous fun x ↦ let y := φ x; ψ x y)`
  - `f = q(fun x ↦ let y := φ x; ψ x y)`
-/
```

-- snippet 22
```lean
-- Mathlib.Tactic.FunProp.Core.applyTransitionRules (L347-L370)
def applyTransitionRules (e : Expr) (funProp : Expr → FunPropM (Option Result)) :
    FunPropM (Option Result) := do
withIncreasedTransitionDepth do

  let candidates ← getTransitionTheorems e

  trace[Meta.Tactic.fun_prop]
    "candidate transition theorems: {← candidates.mapM fun c => ppOrigin (.decl c.thmName)}"

  for c in candidates do
    if let some r ← tryTheorem? e (.decl c.thmName) funProp then
      return r

  trace[Debug.Meta.Tactic.fun_prop] "no theorem matched"
  return none

/-- Try to remove applied argument i.e. prove `P (fun x ↦ f x y)` from `P (fun x ↦ f x)`.

For example
  - `funPropDecl` is `FunPropDecl` for `Continuous`
  - `e = q(Continuous fun x ↦ foo (bar x) y)`
  - `fData` contains info on `fun x ↦ foo (bar x) y`
  This tries to prove `Continuous fun x ↦ foo (bar x) y` from `Continuous fun x ↦ foo (bar x)`
-/
```

-- snippet 23
```lean
-- Mathlib.Tactic.FunProp.Core.synthesizeInstance (L31-L49)
def synthesizeInstance (thmId : Origin) (x type : Expr) : MetaM Bool := do
match (← trySynthInstance type) with
  | .some val =>
    if (← withReducibleAndInstances <| isDefEq x val) then
      return true
    else
      trace[Meta.Tactic.fun_prop]
"{← ppOrigin thmId}, failed to assign instance{indentExpr type}
synthesized value{indentExpr val}\nis not definitionally equal to{indentExpr x}"
      return false
  | _ =>
    trace[Meta.Tactic.fun_prop]
      "{← ppOrigin thmId}, failed to synthesize instance{indentExpr type}"
    return false

/-- Synthesize arguments `xs` either with typeclass synthesis, with `fun_prop` or with
discharger. -/
```

-- snippet 24
```lean
-- Mathlib.Tactic.FunProp.FunctionData.FunctionData (L44-L49)
def FunctionData.toExpr (f : FunctionData) : MetaM Expr := do
withLCtx f.lctx f.insts do
    let body := Mor.mkAppN f.fn f.args
    mkLambdaFVars #[f.mainVar] body

/-- Is `f` an identity function? -/
```

-- snippet 25
```lean
-- Mathlib.Tactic.FunProp.Core.fvarAppCase (L529-L564)
def fvarAppCase (funPropDecl : FunPropDecl) (e : Expr) (fData : FunctionData)
    (funProp : Expr → FunPropM (Option Result)) : FunPropM (Option Result) := do
-- fvar theorems are almost exclusively in uncurried form so we decompose if we can
  if let some (f, g) ← fData.nontrivialDecomposition then
    applyCompRule funPropDecl e f g funProp
  else
    let .fvar id := fData.fn | throwError "fun_prop bug: invalid use of fvar app case"
    let thms ← getLocalTheorems funPropDecl (.fvar id) fData.mainArgs fData.args.size
    trace[Meta.Tactic.fun_prop]
      s!"candidate local theorems for {←ppExpr (.fvar id)} \
         {← thms.mapM fun thm => ppOrigin' thm.thmOrigin}"

    if let some r ← tryTheorems funPropDecl e fData thms funProp then
      return r

    if let some f ← fData.unfoldHeadFVar? then
      let e' := e.setArg funPropDecl.funArgId f
      if let some r ← funProp e' then
        return r

    if (← fData.isMorApplication) != .none then
      if let some r ← applyMorRules funPropDecl e fData funProp then
        return r

    if (← fData.nontrivialDecomposition).isNone then
      if let some r ← applyTransitionRules e funProp then
        return r

    if thms.size = 0 then
      logError s!"No theorems found for `{← ppExpr (.fvar id)}` in order to prove `{← ppExpr e}`"

    return none

/-- Prove function property of `fun x ↦ f x₁ ... xₙ` where `f` is declared function. -/
```

-- snippet 26
```lean
-- Mathlib.Tactic.FunProp.Core.tryTheoremWithHint (L125-L164)
def tryTheoremWithHint? (e : Expr) (thmOrigin : Origin)
    (hint : Array (Nat × Expr))
    (funProp : Expr → FunPropM (Option Result)) (newMCtxDepth : Bool := false) :
FunPropM (Option Result) := do
  let go : FunPropM (Option Result) := do
    let thmProof ← thmOrigin.getValue
    -- for `fvar`s we need to instantiate the metavariables of its type.
    let type ← instantiateMVars <| ← inferType thmProof
    let (xs, _, type) ← forallMetaTelescope type

    for (i,x) in hint do
      try
        for (id,v) in hint do
          xs[id]!.mvarId!.assignIfDefEq v
      catch _ =>
        trace[Debug.Meta.Tactic.fun_prop]
          "failed to use hint {i} `{← ppExpr x} when applying theorem {← ppOrigin thmOrigin}"

    tryTheoremCore xs thmProof type e thmOrigin funProp

  -- `simp` introduces new meta variable context depth for some reason
  -- This is probably to avoid mvar assignment when trying a theorem fails
  --
  -- However, in `fun_prop` case this is not completely desirable
  -- For example, I want to be able to solve a goal with mvars like `ContDiff ℝ ?n f` using local
  -- hypothesis `(h : ContDiff ℝ ∞ f)` and assign `∞` to the mvar `?n`.
  --
  -- This could be problematic if there are two local hypothesis `(hinf : ContDiff ℝ ∞ f)` and
  -- `(h1 : ContDiff ℝ 1 f)` and apart from solving `ContDiff ℝ ?n f` there is also a subgoal
  -- `2 ≤ ?n`. If `fun_prop` decides to try `h1` first it would assign `1` to `?n` and then there
  -- is no hope solving `2 ≤ 1` and it won't be able to apply `hinf` after trying `h1` as `n?` is
  -- assigned already. Ideally `fun_prop` would roll back the `MetaM.State`. This issue did not
  -- come up yet so I didn't bother and I'm worried about the performance impact.
  if newMCtxDepth then
    withNewMCtxDepth go
  else
    go

/-- Try to apply a theorem `thmOrigin` to the goal `e`. -/
```

-- snippet 27
```lean
-- Mathlib.Tactic.FunProp.Core.tryTheorem (L165-L174)
def tryTheorem? (e : Expr) (thmOrigin : Origin) (funProp : Expr → FunPropM (Option Result))
    (newMCtxDepth : Bool := false) : FunPropM (Option Result) :=
tryTheoremWithHint? e thmOrigin #[] funProp newMCtxDepth

/--
Try to prove `e` using the *identity lambda theorem*.

For example, `e = q(Continuous fun x ↦ x)` and `funPropDecl` is `FunPropDecl` for `Continuous`.
-/
```

-- snippet 28
```lean
-- Mathlib.Tactic.FunProp.Core.cacheFailure (L620-L702)
def cacheFailure (e : Expr) : FunPropM Unit := do -- return proof?
modify (fun s => { s with failureCache := s.failureCache.insert e })

mutual
  /-- Main `funProp` function. Returns proof of `e`. -/
  partial def funProp (e : Expr) : FunPropM (Option Result) := do

    let e ← instantiateMVars e

    withTraceNode `Meta.Tactic.fun_prop
      (fun _ => do pure s!"{← ppExpr e}") do

    -- check cache for successful goals
    if let some { expr := _, proof? := some proof } := (← get).cache.find? e then
      trace[Meta.Tactic.fun_prop] "reusing previously found proof for {e}"
      return some { proof := proof }
    else if (← get).failureCache.contains e then
      trace[Meta.Tactic.fun_prop] "skipping proof search, proving {e} was tried already and failed"
      return none
    else
      -- take care of forall and let binders and run main
      match e with
      | .letE .. =>
        letTelescope e fun xs b => do
          let some r ← funProp b
            | return none
          cacheResult e {proof := ← mkLambdaFVars (generalizeNondepLet := false) xs r.proof }
      | .forallE .. =>
        forallTelescope e fun xs b => do
          let some r ← funProp b
            | return none
          cacheResult e {proof := ← mkLambdaFVars xs r.proof }
      | .mdata _ e' => funProp e'
      | _ =>
        if let some r ← main e then
          cacheResult e r
        else
          cacheFailure e
          return none

  /-- Main `funProp` function. Returns proof of `e`. -/
  private partial def main (e : Expr) : FunPropM (Option Result) := do

    let some (funPropDecl, f) ← getFunProp? e
      | return none

    increaseSteps

    -- if function starts with let bindings move them the top of `e` and try again
    if f.isLet then
      return ← funProp (← mapLetTelescope f fun _ b => pure <| e.setArg funPropDecl.funArgId b)

    match ← getFunctionData? f (← unfoldNamePred) with
    | .letE f =>
      trace[Debug.Meta.Tactic.fun_prop] "let case on {← ppExpr f}"
      let e := e.setArg funPropDecl.funArgId f -- update e with reduced f
      letCase funPropDecl e f funProp
    | .lam f =>
      trace[Debug.Meta.Tactic.fun_prop] "pi case on {← ppExpr f}"
      let e := e.setArg funPropDecl.funArgId f -- update e with reduced f
      applyPiRule funPropDecl e funProp
    | .data fData =>
      let e := e.setArg funPropDecl.funArgId (← fData.toExpr) -- update e with reduced f

      if fData.isIdentityFun then
        applyIdRule funPropDecl e funProp
      else if fData.isConstantFun then
        applyConstRule funPropDecl e funProp
      else
        match fData.fn with
        | .fvar id =>
          if id == fData.mainVar.fvarId! then
            bvarAppCase funPropDecl e fData funProp
          else
            fvarAppCase funPropDecl e fData funProp
        | .const .. | .proj .. => do
          constAppCase funPropDecl e fData funProp
        | _ =>
          trace[Debug.Meta.Tactic.fun_prop] "unknown case, ctor: {f.ctorName}\n{e}"
          return none
```

-- snippet 29
```lean
-- Mathlib.Tactic.FunProp.Core.tryTheorems (L474-L528)
def tryTheorems (funPropDecl : FunPropDecl) (e : Expr) (fData : FunctionData)
    (thms : Array FunctionTheorem) (funProp : Expr → FunPropM (Option Result)) :
    FunPropM (Option Result) := do
-- none - decomposition not tried
  -- some none - decomposition failed
  -- some some (f, g) - successful decomposition
  let mut dec? : Option (Option (Expr × Expr)) := none

  for thm in thms do

    trace[Debug.Meta.Tactic.fun_prop] s!"trying theorem {← ppOrigin' thm.thmOrigin}"

    match compare thm.appliedArgs fData.args.size with
    | .lt =>
      trace[Meta.Tactic.fun_prop] s!"removing argument to later use {← ppOrigin' thm.thmOrigin}"
      if let some r ← removeArgRule funPropDecl e fData funProp then
        return r
      continue
    | .gt =>
      trace[Meta.Tactic.fun_prop] s!"adding argument to later use {← ppOrigin' thm.thmOrigin}"
      if let some r ← applyPiRule funPropDecl e funProp then
        return r
      continue
    | .eq =>
      if thm.form == .comp then
        if let some r ← tryTheorem? e thm.thmOrigin funProp then
          return r
      else

        if thm.mainArgs.size == fData.mainArgs.size then
          if dec?.isNone then
            dec? := some (← fData.nontrivialDecomposition)
          match dec? with
          | some none =>
            if let some r ← tryTheorem? e thm.thmOrigin funProp then
              return r
          | some (some (f, g)) =>
            trace[Meta.Tactic.fun_prop]
              s!"decomposing to later use {←ppOrigin' thm.thmOrigin} as:
                   ({← ppExpr f}) ∘ ({← ppExpr g})"
            if let some r ← applyCompRule funPropDecl e f g funProp then
              return r
          | _ => continue
        else
          let some (f, g) ← fData.decompositionOverArgs thm.mainArgs | continue
          trace[Meta.Tactic.fun_prop]
            s!"decomposing to later use {←ppOrigin' thm.thmOrigin} as:
                 ({← ppExpr f}) ∘ ({← ppExpr g})"
          if let some r ← applyCompRule funPropDecl e f g funProp then
            return r
      -- todo: decompose if uncurried and arguments do not match exactly
  return none

/-- Prove function property of `fun x ↦ f x₁ ... xₙ` where `f` is free variable. -/
```

-- snippet 30
```lean
-- Mathlib.Tactic.FunProp.Core.applyConstRule (L195-L214)
def applyConstRule (funPropDecl : FunPropDecl) (e : Expr)
    (funProp : Expr → FunPropM (Option Result)) : FunPropM (Option Result) := do
let thms ← getLambdaTheorems funPropDecl.funPropName .const
  if thms.size = 0 then
    let msg := s!"missing constant rule to prove `{← ppExpr e}`"
    logError msg
    trace[Meta.Tactic.fun_prop] msg
    return none
  for thm in thms do
    let .const := thm.thmArgs | return none
    if let some r ← tryTheorem? e (.decl thm.thmName) funProp then
      return r

  return none

/--
Try to prove `e` using the *apply lambda theorem*.

For example, `e = q(Continuous fun f ↦ f x)` and `funPropDecl` is `FunPropDecl` for `Continuous`.
-/
```

-- snippet 31
```lean
-- Mathlib.Tactic.FunProp.Core.removeArgRule (L371-L388)
def removeArgRule (funPropDecl : FunPropDecl) (e : Expr) (fData : FunctionData)
    (funProp : Expr → FunPropM (Option Result)) :
    FunPropM (Option Result) := do
match h : fData.args.size with
  | 0 => throwError "fun_prop bug: invalid use of remove arg case {←ppExpr e}"
  | n + 1 =>
    let arg := fData.args[n]

    if arg.coe.isSome then
      -- if have to apply morphisms rules if we deal with morphisms
      return ← applyMorRules funPropDecl e fData funProp
    else
      let some (f, g) ← fData.peeloffArgDecomposition | return none
      applyCompRule funPropDecl e f g funProp

/-- Prove function property of `fun f ↦ f x₁ ... xₙ`. -/
```

-- snippet 32
```lean
-- Mathlib.Tactic.FunProp.Elab.funPropTac (L68-L179)
def funPropTac : Tactic
  | `(tactic| fun_prop $cfg:optConfig $[$d]? $[[$names,*]]?) => do

    let goal ← getMainGoal
    goal.withContext do
      let goalType ← goal.getType

      -- the whnf and telescope is here because the goal can be
      -- `∀ y, let f := fun x => x + y; Continuous fun x => x + f x`
-- However it is still not complete solution. How should we deal with mix of let and forall?
      withReducible <| forallTelescopeReducing (← whnfR goalType) fun _ type => do
        unless (← getFunProp? type).isSome do
          let hint :=
            if let some n := type.getAppFn.constName?
            then s!" Maybe you forgot marking `{n}` with `@[fun_prop]`."
            else ""
          throwError "`{← ppExpr type}` is not a `fun_prop` goal!{hint}"

      let cfg ← elabFunPropConfig cfg

      let disch ← show MetaM (Expr → MetaM (Option Expr)) from do
        match d with
        | none => pure emptyDischarge
        | some d =>
          match d with
          | `(discharger| (discharger:=$tac)) => pure <| tacticToDischarge (← `(tactic| ($tac)))
          | _ => pure emptyDischarge

      let namesToUnfold : Array Name :=
        match names with
        | none => #[]
        | some ns => ns.getElems.map (fun n => n.getId)

      let namesToUnfold := namesToUnfold.append defaultNamesToUnfold

      let ctx : Context :=
        { config := cfg,
          disch := disch
          constToUnfold := .ofArray namesToUnfold _}
      let env ← getEnv
      let s := {
        morTheorems        := morTheoremsExt.getState env
        transitionTheorems := transitionTheoremsExt.getState env }
      let (r?, s) ← funProp goalType ctx |>.run s
      if let some r := r? then
        goal.assign r.proof
      else
        let mut msg := s!"`fun_prop` was unable to prove `{← Meta.ppExpr goalType}`\n\n"

        msg := msg ++ "Issues:"
        msg := s.msgLog.foldl (init := msg) (fun msg m => msg ++ "\n  " ++ m)

        throwError msg

  | _ => throwUnsupportedSyntax

/-- Command that prints all function properties attached to a function.

For example
```
#print_fun_prop_theorems HAdd.hAdd
```
might print out
```
Continuous
  continuous_add, args: [4,5], priority: 1000
  continuous_const_add, args: [5], priority: 1000
  continuous_add_const, args [4], priority: 1000
  ...
Differentiable
  Differentiable.add, args: [4,5], priority: 1000
  Differentiable.add_const, args: [4], priority: 1000
  Differentiable.const_add, args: [5], priority: 1000
  ...
```

You can also see only theorems about a concrete function property
```
#print_fun_prop_theorems HAdd.hAdd Continuous
```
-/
elab "#print_fun_prop_theorems " funIdent:ident funProp:(ident)? : command => do

  let funName ← ensureNonAmbiguous funIdent (← resolveGlobalConst funIdent)
  let funProp? ← funProp.mapM (fun stx => do
    ensureNonAmbiguous stx (← resolveGlobalConst stx))

  let theorems := (functionTheoremsExt.getState (← getEnv)).theorems.getD funName {}

  let logTheorems (funProp : Name) (thms : Array FunctionTheorem) : Command.CommandElabM Unit := do
    let mut msg : MessageData := ""
    msg := msg ++ m!"{← Meta.ppOrigin (.decl funProp)}"
    for thm in thms do
      msg := msg ++ m!"\n  {← Meta.ppOrigin (.decl thm.thmOrigin.name)}, \
                 args: {thm.mainArgs}, form: {thm.form}"
      pure ()
    logInfo msg

  match funProp? with
  | none =>
    for (funProp,thms) in theorems do
      logTheorems funProp thms
  | some funProp =>
    logTheorems funProp (theorems.getD funProp #[])

end Meta.FunProp

end Mathlib
```

<s>[SYSTEM_PROMPT]You are Leanstral, a Lean 4 proof assistant. Complete the task below using the provided Lean source context. The target mathlib commit is 5e932f97dd25535344f80f9dd8da3aab83df0fe6 (module Mathlib.Tactic.FunProp.Decl).[/SYSTEM_PROMPT][MODEL_SETTINGS]{"reasoning_effort": "none"}[/MODEL_SETTINGS][INST]Prove the following theorem. The retrieved context below may help.

```lean
def isFunProp (e : Expr) : MetaM Bool
```

## Retrieved context
-- snippet 1
```lean
-- Mathlib.Tactic.FunProp.Decl.FunPropDeclsExt (L45-L57)
abbrev FunPropDeclsExt := SimpleScopedEnvExtension FunPropDecl FunPropDecls
/-- Extension storing function properties tracked and used by the `fun_prop` attribute and tactic,
including, for example, `Continuous`, `Measurable`, `Differentiable`, etc. -/
initialize funPropDeclsExt : FunPropDeclsExt ←
  registerSimpleScopedEnvExtension {
    name := by exact decl_name%
    initial := {}
    addEntry := fun d e =>
      {d with decls := d.decls.insertKeyValue e.path e}
  }

/-- Register new function property. -/
```

-- snippet 2
```lean
-- Mathlib.Tactic.FunProp.Decl.addFunPropDecl (L58-L92)
def addFunPropDecl (declName : Name) : MetaM Unit := do
let info ← getConstInfo declName

  let (xs, bi, b) ← forallMetaTelescope info.type

  if ¬b.isProp then
    throwError "invalid fun_prop declaration, has to be `Prop`-valued function"

  let lvls := info.levelParams.map (fun l => Level.param l)
  let e := mkAppN (.const declName lvls) xs
  let path ← DiscrTree.mkPath e

  -- find the argument position of the function `f` in `P f`
  let mut some funArgId ← (xs.zip bi).findIdxM? fun (x,bi) => do
    if (← inferType x).isForall && bi.isExplicit then
      return true
    else
      return false
    | throwError "invalid fun_prop declaration, can't find argument of type `α → β`"

  let decl : FunPropDecl := {
    funPropName := declName
    path := path
    funArgId := funArgId
  }

  modifyEnv fun env => funPropDeclsExt.addEntry env decl

  trace[Meta.Tactic.fun_prop.attr]
    "added new function property `{declName}`\nlook up pattern is `{path}`"

/-- Is `e` a function property statement? If yes return function property declaration and
the function it talks about. -/
```

-- snippet 3
```lean
-- Mathlib.Tactic.FunProp.Decl.getFunProp (L93-L111)
def getFunProp? (e : Expr) : MetaM (Option (FunPropDecl × Expr)) := do
let ext := funPropDeclsExt.getState (← getEnv)

  let decls ← ext.decls.getMatch e (← read)

  if h : decls.size = 0 then
    return none
  else
    if decls.size > 1 then
      throwError "fun_prop bug: expression {← ppExpr e} matches multiple function properties\n\
        {decls.map (fun d => d.funPropName)}"

    let decl := decls[0]
    unless decl.funArgId < e.getAppNumArgs do return none
    let f := e.getArg! decl.funArgId

    return (decl,f)

/-- Is `e` a function property statement? -/
```

-- snippet 4
```lean
-- Mathlib.Tactic.FunProp.Decl.isFunPropGoal (L115-L119)
def isFunPropGoal (e : Expr) : MetaM Bool := do
forallTelescope e fun _ b =>
  return (← getFunProp? b).isSome

/-- Returns function property declaration from `e = P f`. -/
```

-- snippet 5
```lean
-- Mathlib.Tactic.FunProp.Decl.getFunPropDecl (L120-L126)
def getFunPropDecl? (e : Expr) : MetaM (Option FunPropDecl) := do
match ← getFunProp? e with
  | some (decl, _) => return decl
  | none => return none

/-- Returns function `f` from `e = P f` and `P` is function property. -/
```

-- snippet 6
```lean
-- Mathlib.Tactic.FunProp.Decl.getFunPropFun (L127-L134)
def getFunPropFun? (e : Expr) : MetaM (Option Expr) := do
match ← getFunProp? e with
  | some (_, f) => return f
  | none => return none

open Elab Term in
/-- Turn tactic syntax into a discharger function. -/
```

-- snippet 7
```lean
-- Mathlib.Tactic.FunProp.Decl.tacticToDischarge (L135-L160)
def tacticToDischarge (tacticCode : TSyntax `tactic) : Expr → MetaM (Option Expr) := fun e =>
withTraceNode `Meta.Tactic.fun_prop
    (fun _ => do pure s!"discharging: {← ppExpr e}") do
    let mvar ← mkFreshExprSyntheticOpaqueMVar e `funProp.discharger
    let runTac? : TermElabM (Option Expr) :=
      try
        instantiateMVarDeclMVars mvar.mvarId!

        let _ ←
          withSynthesize (postpone := .no) do
            Tactic.run mvar.mvarId! (Tactic.evalTactic tacticCode *> Tactic.pruneSolvedGoals)

        let result ← instantiateMVars mvar
        if result.hasExprMVar then
          return none
        else
          return some result
      catch _ =>
        return none
    let (result?, _) ← runTac?.run {} {}

    return result?

end Meta.FunProp

end Mathlib
```

-- snippet 8
```lean
-- Mathlib.Tactic.FunProp.Core.bvarAppCase (L389-L402)
def bvarAppCase (funPropDecl : FunPropDecl) (e : Expr) (fData : FunctionData)
    (funProp : Expr → FunPropM (Option Result)) : FunPropM (Option Result) := do
if (← fData.isMorApplication) != .none then
    applyMorRules funPropDecl e fData funProp
  else
    if let some (f, g) ← fData.nontrivialDecomposition then
      applyCompRule funPropDecl e f g funProp
    else
      applyApplyRule funPropDecl e funProp

/--
Get candidate theorems from the environment for function property `funPropDecl` and
function `funName`. -/
```

-- snippet 9
```lean
-- Mathlib.Tactic.FunProp.FunctionData.getFunctionData (L77-L118)
def getFunctionData (f : Expr) : MetaM FunctionData := do
lambdaTelescope f fun xs b => do

    let xId := xs[0]!.fvarId!

    Mor.withApp b fun fn args => do

      let mut fn := fn
      let mut args := args

      -- revert projection in fn
      if let .proj n i x := fn then
        let some info := getStructureInfo? (← getEnv) n | unreachable!
        let some projName := info.getProjFn? i | unreachable!
        let p ← mkAppM projName #[x]
        fn := p.getAppFn
        args := p.getAppArgs.map (fun a => {expr:=a}) ++ args

      let mainArgs := args
        |>.mapIdx (fun i ⟨arg,_⟩ => if arg.containsFVar xId then some i else none)
        |>.filterMap id

      return {
        lctx := ← getLCtx
        insts := ← getLocalInstances
        fn := fn
        args := args
        mainVar := xs[0]!
        mainArgs := mainArgs
      }

/-- Result of `getFunctionData?`. It returns function data if the function is in the form
`fun x ↦ f y₁ ... yₙ`. Two other cases are `fun x ↦ let y := ...` or `fun x y ↦ ...` -/
inductive MaybeFunctionData where
  /-- Can't generate function data as function body has let binder. -/
  | letE (f : Expr)
  /-- Can't generate function data as function body has lambda binder. -/
  | lam (f : Expr)
  /-- Function data has been successfully generated. -/
  | data (fData : FunctionData)

/-- Turn `MaybeFunctionData` to the function. -/
```

-- snippet 10
```lean
-- Mathlib.Tactic.FunProp.Core.applyCompRule (L231-L253)
def applyCompRule (funPropDecl : FunPropDecl) (e f g : Expr)
    (funProp : Expr → FunPropM (Option Result)) : FunPropM (Option Result) := do
let thms ← getLambdaTheorems funPropDecl.funPropName .comp
  if thms.size = 0 then
    let msg := s!"missing composition rule to prove `{← ppExpr e}`"
    logError msg
    trace[Meta.Tactic.fun_prop] msg
    return none

  for thm in thms do
    let .comp id_f id_g := thm.thmArgs | return none
    if let some r ← tryTheoremWithHint? e (.decl thm.thmName) #[(id_f, f), (id_g, g)] funProp then
      return r

  return none

/--
Try to prove `e` using *pi lambda theorem*.

For example, `e = q(Continuous fun x y ↦ f x y)` and `funPropDecl` is `FunPropDecl` for
`Continuous`
-/
```

-- snippet 11
```lean
-- Mathlib.Tactic.FunProp.Core.synthesizeArgs (L50-L106)
def synthesizeArgs (thmId : Origin) (xs : Array Expr)
    (funProp : Expr → FunPropM (Option Result)) :
    FunPropM Bool := do
let mut postponed : Array Expr := #[]
  for x in xs do
    let type ← inferType x
    if (← instantiateMVars x).isMVar then

      -- try type class
      if (← isClass? type).isSome then
        if (← synthesizeInstance thmId x type) then
          continue
      else if (← isFunPropGoal type) then
        -- try function property
        if let some ⟨proof⟩ ← funProp type then
          if (← isDefEq x proof) then
            continue
          else do
            trace[Meta.Tactic.fun_prop]
              "{← ppOrigin thmId}, failed to assign proof{indentExpr type}"
            return false
      else
        -- try user provided discharger
        let ctx : Context ← read
        if (← isProp type) then
          if let some proof ← ctx.disch type then
            if (← isDefEq x proof) then
              continue
            else do
              trace[Meta.Tactic.fun_prop]
                "{← ppOrigin thmId}, failed to assign proof{indentExpr type}"
              return false
          else
            logError s!"Failed to prove necessary assumption `{← ppExpr type}` \
                        when applying theorem `{← ppOrigin' thmId}`."

      if ¬(← isProp type) then
        postponed := postponed.push x
        continue
      else
        trace[Meta.Tactic.fun_prop]
          "{← ppOrigin thmId}, failed to discharge hypotheses{indentExpr type}"
        return false

  for x in postponed do
    if (← instantiateMVars x).isMVar then
      logError s!"Failed to infer `({← ppExpr x} : {← ppExpr (← inferType x)})` \
      when applying theorem `{← ppOrigin' thmId}`."

      trace[Meta.Tactic.fun_prop]
        "{← ppOrigin thmId}, failed to infer `({← ppExpr x} : {← ppExpr (← inferType x)})`"
      return false

  return true

/-- Try to apply theorem - core function -/
```

-- snippet 12
```lean
-- Mathlib.Tactic.FunProp.Core.cacheResult (L615-L619)
def cacheResult (e : Expr) (r : Result) : FunPropM Result := do -- return proof?
modify (fun s => { s with cache := s.cache.insert e { expr := q(True), proof? := r.proof} })
  return r

/-- Cache for failed goals such that `fun_prop` can fail fast next time. -/
```

-- snippet 13
```lean
-- Mathlib.Tactic.FunProp.Core.letCase (L279-L320)
def letCase (funPropDecl : FunPropDecl) (e : Expr) (f : Expr)
    (funProp : Expr → FunPropM (Option Result)) :
    FunPropM (Option Result) := do
match f with
  | .lam xName xType (.letE yName yType yValue yBody _) xBi => do
    let yType  := yType.consumeMData
    let yValue := yValue.consumeMData
    let yBody  := yBody.consumeMData
    -- We perform reduction because the type is quite often of the form
    -- `(fun x ↦ Y) #0` which is just `Y`
    -- Usually this is caused by the usage of `FunLike`
    let yType := yType.headBeta
    if (yType.hasLooseBVar 0) then
      throwError "dependent type encountered {← ppExpr (Expr.forallE xName xType yType default)}"

    -- let binding can be pulled out of the lambda function
    if ¬(yValue.hasLooseBVar 0) then
      let body := yBody.swapBVars 0 1
      let e' := mkLet yName yType yValue
        (e.setArg (funPropDecl.funArgId) (.lam xName xType body xBi))
      return ← funProp e'

    match (yBody.hasLooseBVar 0), (yBody.hasLooseBVar 1) with
    | true, true =>
      let f ← mkUncurryFun 2 (Expr.lam xName xType (.lam yName yType yBody default) xBi)
      let g := Expr.lam xName xType (binderInfo := default)
        (mkAppN (← mkConstWithFreshMVarLevels ``Prod.mk) #[xType,yType,.bvar 0, yValue])
      applyCompRule funPropDecl e f g funProp

    | true, false =>
      let f := Expr.lam yName yType yBody default
      let g := Expr.lam xName xType yValue default
      applyCompRule funPropDecl e f g funProp

    | false, _ =>
      let f := Expr.lam xName xType (yBody.lowerLooseBVars 1 1) xBi
      funProp (e.setArg (funPropDecl.funArgId) f)

  | _ => throwError "expected expression of the form `fun x ↦ lam y := ..; ..`"

/-- Prove function property of using *morphism theorems*. -/
```

-- snippet 14
```lean
-- Mathlib.Tactic.FunProp.Core.getDeclTheorems (L403-L422)
def getDeclTheorems (funPropDecl : FunPropDecl) (funName : Name)
    (mainArgs : Array Nat) (appliedArgs : Nat) : MetaM (Array FunctionTheorem) := do
let thms ← getTheoremsForFunction funName funPropDecl.funPropName

  let thms := thms
    |>.filter (fun thm => (isOrderedSubsetOf mainArgs thm.mainArgs))
    |>.qsort (fun t s =>
      let dt := (Int.ofNat t.appliedArgs - Int.ofNat appliedArgs).natAbs
      let ds := (Int.ofNat s.appliedArgs - Int.ofNat appliedArgs).natAbs
      match compare dt ds with
      | .lt => true
      | .gt => false
      | .eq => t.mainArgs.size < s.mainArgs.size)
  -- todo: sorting and filtering
  return thms

/--
Get candidate theorems from the local context for function property `funPropDecl` and
function `funName`. -/
```

-- snippet 15
```lean
-- Mathlib.Tactic.FunProp.Core.applyMorRules (L321-L346)
def applyMorRules (funPropDecl : FunPropDecl) (e : Expr) (fData : FunctionData)
    (funProp : Expr → FunPropM (Option Result)) : FunPropM (Option Result) := do
trace[Debug.Meta.Tactic.fun_prop] "applying morphism theorems to {← ppExpr e}"

  match ← fData.isMorApplication with
  | .none => throwError "fun_prop bug: invalid use of mor rules on {← ppExpr e}"
  | .underApplied =>
    applyPiRule funPropDecl e funProp
  | .overApplied =>
    let some (f, g) ← fData.peeloffArgDecomposition | return none
    applyCompRule funPropDecl e f g funProp
  | .exact =>

    let candidates ← getMorphismTheorems e

    trace[Meta.Tactic.fun_prop]
      "candidate morphism theorems: {← candidates.mapM fun c => ppOrigin (.decl c.thmName)}"

    for c in candidates do
      if let some r ← tryTheorem? e (.decl c.thmName) funProp then
        return r

    trace[Debug.Meta.Tactic.fun_prop] "no theorem matched"
    return none

/-- Prove function property of using *transition theorems*. -/
```

-- snippet 16
```lean
-- Mathlib.Tactic.FunProp.Core.constAppCase (L565-L614)
def constAppCase (funPropDecl : FunPropDecl) (e : Expr) (fData : FunctionData)
    (funProp : Expr → FunPropM (Option Result)) : FunPropM (Option Result) := do
let some (funName, _) := fData.fn.const?
    | throwError "fun_prop bug: invelid use of const app case"
  let globalThms ← getDeclTheorems funPropDecl funName fData.mainArgs fData.args.size

  trace[Meta.Tactic.fun_prop]
    s!"candidate theorems for {funName} {← globalThms.mapM fun thm => ppOrigin' thm.thmOrigin}"

  if let some r ← tryTheorems funPropDecl e fData globalThms funProp then
    return r

  -- Try local theorems - this is useful for recursive functions
  let localThms ← getLocalTheorems funPropDecl (.decl funName) fData.mainArgs fData.args.size
  if localThms.size ≠ 0 then
    trace[Meta.Tactic.fun_prop]
      s!"candidate local theorems for {funName} \
        {← localThms.mapM fun thm => ppOrigin' thm.thmOrigin}"
  if let some r ← tryTheorems funPropDecl e fData localThms funProp then
    return r

  -- log error if no global or local theorems were found
  if globalThms.size = 0 && localThms.size = 0 then
     logError s!"No theorems found for `{funName}` in order to prove `{← ppExpr e}`"

  if (← fData.isMorApplication) != .none then
    if let some r ← applyMorRules funPropDecl e fData funProp then
      return r

  if let some (f, g) ← fData.nontrivialDecomposition then
    trace[Meta.Tactic.fun_prop]
      s!"failed applying `{funPropDecl.funPropName}` theorems for `{funName}`
         trying again after decomposing function as: `({← ppExpr f}) ∘ ({← ppExpr g})`"

    if let some r ← applyCompRule funPropDecl e f g funProp then
      return r
  else
    trace[Meta.Tactic.fun_prop]
      s!"failed applying `{funPropDecl.funPropName}` theorems for `{funName}`
         now trying to prove `{funPropDecl.funPropName}` from another function property"

    if let some r ← applyTransitionRules e funProp then
      return r

  return none

/-- Cache result if it does not have any subgoals. -/
```

-- snippet 17
```lean
-- Mathlib.Tactic.FunProp.Core.applyApplyRule (L215-L230)
def applyApplyRule (funPropDecl : FunPropDecl) (e : Expr)
    (funProp : Expr → FunPropM (Option Result)) : FunPropM (Option Result) := do
let thms := (← getLambdaTheorems funPropDecl.funPropName .apply)
  for thm in thms do
    if let some r ← tryTheoremWithHint? e (.decl thm.thmName) #[] funProp then
      return r

  return none

/--
Try to prove `e` using *composition lambda theorem*.

For example, `e = q(Continuous fun x ↦ f (g x))` and `funPropDecl` is `FunPropDecl` for
`Continuous`

You also have to provide the functions `f` and `g`. -/
```

-- snippet 18
```lean
-- Mathlib.Tactic.FunProp.Core.applyIdRule (L175-L194)
def applyIdRule (funPropDecl : FunPropDecl) (e : Expr)
    (funProp : Expr → FunPropM (Option Result)) : FunPropM (Option Result) := do
let thms ← getLambdaTheorems funPropDecl.funPropName .id
  if thms.size = 0 then
    let msg := s!"missing identity rule to prove `{← ppExpr e}`"
    logError msg
    trace[Meta.Tactic.fun_prop] msg
    return none

  for thm in thms do
    if let some r ← tryTheoremWithHint? e (.decl thm.thmName) #[] funProp then
      return r

  return none

/--
Try to prove `e` using the *constant lambda theorem*.

For example, `e = q(Continuous fun x ↦ y)` and `funPropDecl` is `FunPropDecl` for `Continuous`.
-/
```

-- snippet 19
```lean
-- Mathlib.Tactic.FunProp.Core.getLocalTheorems (L423-L473)
def getLocalTheorems (funPropDecl : FunPropDecl) (funOrigin : Origin)
    (mainArgs : Array Nat) (appliedArgs : Nat) : FunPropM (Array FunctionTheorem) := do
let mut thms : Array FunctionTheorem := #[]
  let lctx ← getLCtx
  for var in lctx do
    if (var.kind = Lean.LocalDeclKind.auxDecl) then
      continue
    let type ← instantiateMVars var.type
    let thm? : Option FunctionTheorem ←
      forallTelescope type fun _ b => do
      let b ← whnfR b
      let some (decl, f) ← getFunProp? b | return none
      unless decl.funPropName = funPropDecl.funPropName do return none

      let .data fData ← getFunctionData? f (← unfoldNamePred)
        | return none
      unless (fData.getFnOrigin == funOrigin) do return none

      unless isOrderedSubsetOf mainArgs fData.mainArgs do return none

      let dec? ← fData.nontrivialDecomposition

      let thm : FunctionTheorem := {
        funPropName := funPropDecl.funPropName
        thmOrigin := .fvar var.fvarId
        funOrigin := funOrigin
        mainArgs := fData.mainArgs
        appliedArgs := fData.args.size
        priority := eval_prio default
        form := if dec?.isSome then .comp else .uncurried
      }

      return some thm

    if let some thm := thm? then
      thms := thms.push thm

  thms := thms
    |>.qsort (fun t s =>
      let dt := (Int.ofNat t.appliedArgs - Int.ofNat appliedArgs).natAbs
      let ds := (Int.ofNat s.appliedArgs - Int.ofNat appliedArgs).natAbs
      match compare dt ds with
      | .lt => true
      | .gt => false
      | .eq => t.mainArgs.size < s.mainArgs.size)

  return thms

/-- Try to apply *function theorems* `thms` to `e`. -/
```

-- snippet 20
```lean
-- Mathlib.Tactic.FunProp.Core.tryTheoremCore (L107-L124)
def tryTheoremCore (xs : Array Expr) (val : Expr) (type : Expr) (e : Expr)
    (thmId : Origin) (funProp : Expr → FunPropM (Option Result)) : FunPropM (Option Result) := do
withTraceNode `Meta.Tactic.fun_prop
    (fun _ => return s!"applying: {← ppOrigin' thmId}") do

  if (← isDefEq type e) then

    if ¬(← synthesizeArgs thmId xs funProp) then
      return none
    let proof ← instantiateMVars (mkAppN val xs)

    return some { proof := proof }
  else
    trace[Meta.Tactic.fun_prop] "failed to unify {← ppOrigin thmId}\n{type}\nwith\n{e}"
    return none

/-- Try to apply a theorem provided some of the theorem arguments. -/
```

-- snippet 21
```lean
-- Mathlib.Tactic.FunProp.Core.applyPiRule (L254-L278)
def applyPiRule (funPropDecl : FunPropDecl) (e : Expr)
    (funProp : Expr → FunPropM (Option Result)) : FunPropM (Option Result) := do
let thms ← getLambdaTheorems funPropDecl.funPropName .pi
  if thms.size = 0 then
    let msg := s!"missing pi rule to prove `{← ppExpr e}`"
    logError msg
    trace[Meta.Tactic.fun_prop] msg
    return none

  for thm in thms do
    if let some r ← tryTheoremWithHint? e (.decl thm.thmName) #[] funProp then
      return r

  return none

/--
Try to prove `e = q(P (fun x ↦ let y := φ x; ψ x y)`.

For example,
  - `funPropDecl` is `FunPropDecl` for `Continuous`
  - `e = q(Continuous fun x ↦ let y := φ x; ψ x y)`
  - `f = q(fun x ↦ let y := φ x; ψ x y)`
-/
```

-- snippet 22
```lean
-- Mathlib.Tactic.FunProp.Core.applyTransitionRules (L347-L370)
def applyTransitionRules (e : Expr) (funProp : Expr → FunPropM (Option Result)) :
    FunPropM (Option Result) := do
withIncreasedTransitionDepth do

  let candidates ← getTransitionTheorems e

  trace[Meta.Tactic.fun_prop]
    "candidate transition theorems: {← candidates.mapM fun c => ppOrigin (.decl c.thmName)}"

  for c in candidates do
    if let some r ← tryTheorem? e (.decl c.thmName) funProp then
      return r

  trace[Debug.Meta.Tactic.fun_prop] "no theorem matched"
  return none

/-- Try to remove applied argument i.e. prove `P (fun x ↦ f x y)` from `P (fun x ↦ f x)`.

For example
  - `funPropDecl` is `FunPropDecl` for `Continuous`
  - `e = q(Continuous fun x ↦ foo (bar x) y)`
  - `fData` contains info on `fun x ↦ foo (bar x) y`
  This tries to prove `Continuous fun x ↦ foo (bar x) y` from `Continuous fun x ↦ foo (bar x)`
-/
```

-- snippet 23
```lean
-- Mathlib.Tactic.FunProp.Core.synthesizeInstance (L31-L49)
def synthesizeInstance (thmId : Origin) (x type : Expr) : MetaM Bool := do
match (← trySynthInstance type) with
  | .some val =>
    if (← withReducibleAndInstances <| isDefEq x val) then
      return true
    else
      trace[Meta.Tactic.fun_prop]
"{← ppOrigin thmId}, failed to assign instance{indentExpr type}
synthesized value{indentExpr val}\nis not definitionally equal to{indentExpr x}"
      return false
  | _ =>
    trace[Meta.Tactic.fun_prop]
      "{← ppOrigin thmId}, failed to synthesize instance{indentExpr type}"
    return false

/-- Synthesize arguments `xs` either with typeclass synthesis, with `fun_prop` or with
discharger. -/
```

-- snippet 24
```lean
-- Mathlib.Tactic.FunProp.FunctionData.FunctionData (L44-L49)
def FunctionData.toExpr (f : FunctionData) : MetaM Expr := do
withLCtx f.lctx f.insts do
    let body := Mor.mkAppN f.fn f.args
    mkLambdaFVars #[f.mainVar] body

/-- Is `f` an identity function? -/
```

-- snippet 25
```lean
-- Mathlib.Tactic.FunProp.Core.fvarAppCase (L529-L564)
def fvarAppCase (funPropDecl : FunPropDecl) (e : Expr) (fData : FunctionData)
    (funProp : Expr → FunPropM (Option Result)) : FunPropM (Option Result) := do
-- fvar theorems are almost exclusively in uncurried form so we decompose if we can
  if let some (f, g) ← fData.nontrivialDecomposition then
    applyCompRule funPropDecl e f g funProp
  else
    let .fvar id := fData.fn | throwError "fun_prop bug: invalid use of fvar app case"
    let thms ← getLocalTheorems funPropDecl (.fvar id) fData.mainArgs fData.args.size
    trace[Meta.Tactic.fun_prop]
      s!"candidate local theorems for {←ppExpr (.fvar id)} \
         {← thms.mapM fun thm => ppOrigin' thm.thmOrigin}"

    if let some r ← tryTheorems funPropDecl e fData thms funProp then
      return r

    if let some f ← fData.unfoldHeadFVar? then
      let e' := e.setArg funPropDecl.funArgId f
      if let some r ← funProp e' then
        return r

    if (← fData.isMorApplication) != .none then
      if let some r ← applyMorRules funPropDecl e fData funProp then
        return r

    if (← fData.nontrivialDecomposition).isNone then
      if let some r ← applyTransitionRules e funProp then
        return r

    if thms.size = 0 then
      logError s!"No theorems found for `{← ppExpr (.fvar id)}` in order to prove `{← ppExpr e}`"

    return none

/-- Prove function property of `fun x ↦ f x₁ ... xₙ` where `f` is declared function. -/
```

-- snippet 26
```lean
-- Mathlib.Tactic.FunProp.Core.tryTheoremWithHint (L125-L164)
def tryTheoremWithHint? (e : Expr) (thmOrigin : Origin)
    (hint : Array (Nat × Expr))
    (funProp : Expr → FunPropM (Option Result)) (newMCtxDepth : Bool := false) :
FunPropM (Option Result) := do
  let go : FunPropM (Option Result) := do
    let thmProof ← thmOrigin.getValue
    -- for `fvar`s we need to instantiate the metavariables of its type.
    let type ← instantiateMVars <| ← inferType thmProof
    let (xs, _, type) ← forallMetaTelescope type

    for (i,x) in hint do
      try
        for (id,v) in hint do
          xs[id]!.mvarId!.assignIfDefEq v
      catch _ =>
        trace[Debug.Meta.Tactic.fun_prop]
          "failed to use hint {i} `{← ppExpr x} when applying theorem {← ppOrigin thmOrigin}"

    tryTheoremCore xs thmProof type e thmOrigin funProp

  -- `simp` introduces new meta variable context depth for some reason
  -- This is probably to avoid mvar assignment when trying a theorem fails
  --
  -- However, in `fun_prop` case this is not completely desirable
  -- For example, I want to be able to solve a goal with mvars like `ContDiff ℝ ?n f` using local
  -- hypothesis `(h : ContDiff ℝ ∞ f)` and assign `∞` to the mvar `?n`.
  --
  -- This could be problematic if there are two local hypothesis `(hinf : ContDiff ℝ ∞ f)` and
  -- `(h1 : ContDiff ℝ 1 f)` and apart from solving `ContDiff ℝ ?n f` there is also a subgoal
  -- `2 ≤ ?n`. If `fun_prop` decides to try `h1` first it would assign `1` to `?n` and then there
  -- is no hope solving `2 ≤ 1` and it won't be able to apply `hinf` after trying `h1` as `n?` is
  -- assigned already. Ideally `fun_prop` would roll back the `MetaM.State`. This issue did not
  -- come up yet so I didn't bother and I'm worried about the performance impact.
  if newMCtxDepth then
    withNewMCtxDepth go
  else
    go

/-- Try to apply a theorem `thmOrigin` to the goal `e`. -/
```

-- snippet 27
```lean
-- Mathlib.Tactic.FunProp.Core.tryTheorem (L165-L174)
def tryTheorem? (e : Expr) (thmOrigin : Origin) (funProp : Expr → FunPropM (Option Result))
    (newMCtxDepth : Bool := false) : FunPropM (Option Result) :=
tryTheoremWithHint? e thmOrigin #[] funProp newMCtxDepth

/--
Try to prove `e` using the *identity lambda theorem*.

For example, `e = q(Continuous fun x ↦ x)` and `funPropDecl` is `FunPropDecl` for `Continuous`.
-/
```

-- snippet 28
```lean
-- Mathlib.Tactic.FunProp.Core.cacheFailure (L620-L702)
def cacheFailure (e : Expr) : FunPropM Unit := do -- return proof?
modify (fun s => { s with failureCache := s.failureCache.insert e })

mutual
  /-- Main `funProp` function. Returns proof of `e`. -/
  partial def funProp (e : Expr) : FunPropM (Option Result) := do

    let e ← instantiateMVars e

    withTraceNode `Meta.Tactic.fun_prop
      (fun _ => do pure s!"{← ppExpr e}") do

    -- check cache for successful goals
    if let some { expr := _, proof? := some proof } := (← get).cache.find? e then
      trace[Meta.Tactic.fun_prop] "reusing previously found proof for {e}"
      return some { proof := proof }
    else if (← get).failureCache.contains e then
      trace[Meta.Tactic.fun_prop] "skipping proof search, proving {e} was tried already and failed"
      return none
    else
      -- take care of forall and let binders and run main
      match e with
      | .letE .. =>
        letTelescope e fun xs b => do
          let some r ← funProp b
            | return none
          cacheResult e {proof := ← mkLambdaFVars (generalizeNondepLet := false) xs r.proof }
      | .forallE .. =>
        forallTelescope e fun xs b => do
          let some r ← funProp b
            | return none
          cacheResult e {proof := ← mkLambdaFVars xs r.proof }
      | .mdata _ e' => funProp e'
      | _ =>
        if let some r ← main e then
          cacheResult e r
        else
          cacheFailure e
          return none

  /-- Main `funProp` function. Returns proof of `e`. -/
  private partial def main (e : Expr) : FunPropM (Option Result) := do

    let some (funPropDecl, f) ← getFunProp? e
      | return none

    increaseSteps

    -- if function starts with let bindings move them the top of `e` and try again
    if f.isLet then
      return ← funProp (← mapLetTelescope f fun _ b => pure <| e.setArg funPropDecl.funArgId b)

    match ← getFunctionData? f (← unfoldNamePred) with
    | .letE f =>
      trace[Debug.Meta.Tactic.fun_prop] "let case on {← ppExpr f}"
      let e := e.setArg funPropDecl.funArgId f -- update e with reduced f
      letCase funPropDecl e f funProp
    | .lam f =>
      trace[Debug.Meta.Tactic.fun_prop] "pi case on {← ppExpr f}"
      let e := e.setArg funPropDecl.funArgId f -- update e with reduced f
      applyPiRule funPropDecl e funProp
    | .data fData =>
      let e := e.setArg funPropDecl.funArgId (← fData.toExpr) -- update e with reduced f

      if fData.isIdentityFun then
        applyIdRule funPropDecl e funProp
      else if fData.isConstantFun then
        applyConstRule funPropDecl e funProp
      else
        match fData.fn with
        | .fvar id =>
          if id == fData.mainVar.fvarId! then
            bvarAppCase funPropDecl e fData funProp
          else
            fvarAppCase funPropDecl e fData funProp
        | .const .. | .proj .. => do
          constAppCase funPropDecl e fData funProp
        | _ =>
          trace[Debug.Meta.Tactic.fun_prop] "unknown case, ctor: {f.ctorName}\n{e}"
          return none
```

-- snippet 29
```lean
-- Mathlib.Tactic.FunProp.Core.tryTheorems (L474-L528)
def tryTheorems (funPropDecl : FunPropDecl) (e : Expr) (fData : FunctionData)
    (thms : Array FunctionTheorem) (funProp : Expr → FunPropM (Option Result)) :
    FunPropM (Option Result) := do
-- none - decomposition not tried
  -- some none - decomposition failed
  -- some some (f, g) - successful decomposition
  let mut dec? : Option (Option (Expr × Expr)) := none

  for thm in thms do

    trace[Debug.Meta.Tactic.fun_prop] s!"trying theorem {← ppOrigin' thm.thmOrigin}"

    match compare thm.appliedArgs fData.args.size with
    | .lt =>
      trace[Meta.Tactic.fun_prop] s!"removing argument to later use {← ppOrigin' thm.thmOrigin}"
      if let some r ← removeArgRule funPropDecl e fData funProp then
        return r
      continue
    | .gt =>
      trace[Meta.Tactic.fun_prop] s!"adding argument to later use {← ppOrigin' thm.thmOrigin}"
      if let some r ← applyPiRule funPropDecl e funProp then
        return r
      continue
    | .eq =>
      if thm.form == .comp then
        if let some r ← tryTheorem? e thm.thmOrigin funProp then
          return r
      else

        if thm.mainArgs.size == fData.mainArgs.size then
          if dec?.isNone then
            dec? := some (← fData.nontrivialDecomposition)
          match dec? with
          | some none =>
            if let some r ← tryTheorem? e thm.thmOrigin funProp then
              return r
          | some (some (f, g)) =>
            trace[Meta.Tactic.fun_prop]
              s!"decomposing to later use {←ppOrigin' thm.thmOrigin} as:
                   ({← ppExpr f}) ∘ ({← ppExpr g})"
            if let some r ← applyCompRule funPropDecl e f g funProp then
              return r
          | _ => continue
        else
          let some (f, g) ← fData.decompositionOverArgs thm.mainArgs | continue
          trace[Meta.Tactic.fun_prop]
            s!"decomposing to later use {←ppOrigin' thm.thmOrigin} as:
                 ({← ppExpr f}) ∘ ({← ppExpr g})"
          if let some r ← applyCompRule funPropDecl e f g funProp then
            return r
      -- todo: decompose if uncurried and arguments do not match exactly
  return none

/-- Prove function property of `fun x ↦ f x₁ ... xₙ` where `f` is free variable. -/
```

-- snippet 30
```lean
-- Mathlib.Tactic.FunProp.Core.applyConstRule (L195-L214)
def applyConstRule (funPropDecl : FunPropDecl) (e : Expr)
    (funProp : Expr → FunPropM (Option Result)) : FunPropM (Option Result) := do
let thms ← getLambdaTheorems funPropDecl.funPropName .const
  if thms.size = 0 then
    let msg := s!"missing constant rule to prove `{← ppExpr e}`"
    logError msg
    trace[Meta.Tactic.fun_prop] msg
    return none
  for thm in thms do
    let .const := thm.thmArgs | return none
    if let some r ← tryTheorem? e (.decl thm.thmName) funProp then
      return r

  return none

/--
Try to prove `e` using the *apply lambda theorem*.

For example, `e = q(Continuous fun f ↦ f x)` and `funPropDecl` is `FunPropDecl` for `Continuous`.
-/
```

-- snippet 31
```lean
-- Mathlib.Tactic.FunProp.Core.removeArgRule (L371-L388)
def removeArgRule (funPropDecl : FunPropDecl) (e : Expr) (fData : FunctionData)
    (funProp : Expr → FunPropM (Option Result)) :
    FunPropM (Option Result) := do
match h : fData.args.size with
  | 0 => throwError "fun_prop bug: invalid use of remove arg case {←ppExpr e}"
  | n + 1 =>
    let arg := fData.args[n]

    if arg.coe.isSome then
      -- if have to apply morphisms rules if we deal with morphisms
      return ← applyMorRules funPropDecl e fData funProp
    else
      let some (f, g) ← fData.peeloffArgDecomposition | return none
      applyCompRule funPropDecl e f g funProp

/-- Prove function property of `fun f ↦ f x₁ ... xₙ`. -/
```

-- snippet 32
```lean
-- Mathlib.Tactic.FunProp.Elab.funPropTac (L68-L179)
def funPropTac : Tactic
  | `(tactic| fun_prop $cfg:optConfig $[$d]? $[[$names,*]]?) => do

    let goal ← getMainGoal
    goal.withContext do
      let goalType ← goal.getType

      -- the whnf and telescope is here because the goal can be
      -- `∀ y, let f := fun x => x + y; Continuous fun x => x + f x`
-- However it is still not complete solution. How should we deal with mix of let and forall?
      withReducible <| forallTelescopeReducing (← whnfR goalType) fun _ type => do
        unless (← getFunProp? type).isSome do
          let hint :=
            if let some n := type.getAppFn.constName?
            then s!" Maybe you forgot marking `{n}` with `@[fun_prop]`."
            else ""
          throwError "`{← ppExpr type}` is not a `fun_prop` goal!{hint}"

      let cfg ← elabFunPropConfig cfg

      let disch ← show MetaM (Expr → MetaM (Option Expr)) from do
        match d with
        | none => pure emptyDischarge
        | some d =>
          match d with
          | `(discharger| (discharger:=$tac)) => pure <| tacticToDischarge (← `(tactic| ($tac)))
          | _ => pure emptyDischarge

      let namesToUnfold : Array Name :=
        match names with
        | none => #[]
        | some ns => ns.getElems.map (fun n => n.getId)

      let namesToUnfold := namesToUnfold.append defaultNamesToUnfold

      let ctx : Context :=
        { config := cfg,
          disch := disch
          constToUnfold := .ofArray namesToUnfold _}
      let env ← getEnv
      let s := {
        morTheorems        := morTheoremsExt.getState env
        transitionTheorems := transitionTheoremsExt.getState env }
      let (r?, s) ← funProp goalType ctx |>.run s
      if let some r := r? then
        goal.assign r.proof
      else
        let mut msg := s!"`fun_prop` was unable to prove `{← Meta.ppExpr goalType}`\n\n"

        msg := msg ++ "Issues:"
        msg := s.msgLog.foldl (init := msg) (fun msg m => msg ++ "\n  " ++ m)

        throwError msg

  | _ => throwUnsupportedSyntax

/-- Command that prints all function properties attached to a function.

For example
```
#print_fun_prop_theorems HAdd.hAdd
```
might print out
```
Continuous
  continuous_add, args: [4,5], priority: 1000
  continuous_const_add, args: [5], priority: 1000
  continuous_add_const, args [4], priority: 1000
  ...
Differentiable
  Differentiable.add, args: [4,5], priority: 1000
  Differentiable.add_const, args: [4], priority: 1000
  Differentiable.const_add, args: [5], priority: 1000
  ...
```

You can also see only theorems about a concrete function property
```
#print_fun_prop_theorems HAdd.hAdd Continuous
```
-/
elab "#print_fun_prop_theorems " funIdent:ident funProp:(ident)? : command => do

  let funName ← ensureNonAmbiguous funIdent (← resolveGlobalConst funIdent)
  let funProp? ← funProp.mapM (fun stx => do
    ensureNonAmbiguous stx (← resolveGlobalConst stx))

  let theorems := (functionTheoremsExt.getState (← getEnv)).theorems.getD funName {}

  let logTheorems (funProp : Name) (thms : Array FunctionTheorem) : Command.CommandElabM Unit := do
    let mut msg : MessageData := ""
    msg := msg ++ m!"{← Meta.ppOrigin (.decl funProp)}"
    for thm in thms do
      msg := msg ++ m!"\n  {← Meta.ppOrigin (.decl thm.thmOrigin.name)}, \
                 args: {thm.mainArgs}, form: {thm.form}"
      pure ()
    logInfo msg

  match funProp? with
  | none =>
    for (funProp,thms) in theorems do
      logTheorems funProp thms
  | some funProp =>
    logTheorems funProp (theorems.getD funProp #[])

end Meta.FunProp

end Mathlib
```
[/INST]


[System] You are Leanstral, a Lean 4 proof assistant. Complete the task below using the provided Lean source context. The target mathlib commit is 5e932f97dd25535344f80f9dd8da3aab83df0fe6 (module Mathlib.AlgebraicTopology.SimplexCategory.ToMkOne).

[User]
The following Lean code fails to compile. Fix it.

```lean
lemma σ_comp_toMk₁_of_lt {n : ℕ} (i : Fin (n + 2)) (j : Fin (n + 1)) (h : j.castSucc < i) :
    σ j ≫ toMk₁ i = toMk₁ i.succ
:= by .someMissingNamespaceXYZ
refine ConcreteCategory.hom_ext _ _ (fun k ↦ ?_)
  change toMk₁ i (j.predAbove k) = _
  by_cases! hk : i < k
  · grind [Fin.predAbove_of_castSucc_lt, toMk₁_of_le_castSucc]
  · dsimp
    rw [toMk₁_of_castSucc_lt i.succ k (by simpa), toMk₁_of_castSucc_lt]
    by_cases hk' : j.castSucc < k
    · rwa [Fin.predAbove_of_castSucc_lt _ _ hk', Fin.castSucc_pred_lt_iff]
    · simp only [not_lt] at hk'
      rw [Fin.predAbove_of_le_castSucc _ _ hk']
      exact lt_of_le_of_lt (by simpa) h
```

## Compiler diagnostic
```
[CompilationUnit(i_begin=0, i_end=106, messages=[Message(data='expected token', pos=Position(line=2, column=8), pos_end=None, severity=<Severity.ERROR: 3>, kind=None), Message(data="Function expected at\n  σ\nbut this term has type\n  ?m.1\n\nNote: Expected a function because this term is being applied to the argument\n  j\n\nHint: The identifier `σ` is unknown, and Lean's `autoImplicit` option causes an unknown identifier to be treated as an implicitly bound variable with an unknown type. However, the unknown type cannot be a function, and a function is what Lean expects here. This is often the result of a typo or a missing `import` or `open` statement.", pos=Position(line=2, column=4), pos_end=Position(line=2, column=7), severity=<Severity.ERROR: 3>, kind=None)], invocations=None, goal_state=None, goal_src_boundaries=None, new_constants=None), CompilationUnit(i_begin=106, i_end=664, messages=[], invocations=None, goal_state=None, goal_src_boundaries=None, new_constants=None)]
```

Return a corrected, compiling version of the code.

<s>[SYSTEM_PROMPT]You are Leanstral, a Lean 4 proof assistant. Complete the task below using the provided Lean source context. The target mathlib commit is 5e932f97dd25535344f80f9dd8da3aab83df0fe6 (module Mathlib.AlgebraicTopology.SimplexCategory.ToMkOne).[/SYSTEM_PROMPT][MODEL_SETTINGS]{"reasoning_effort": "none"}[/MODEL_SETTINGS][INST]The following Lean code fails to compile. Fix it.

```lean
lemma σ_comp_toMk₁_of_lt {n : ℕ} (i : Fin (n + 2)) (j : Fin (n + 1)) (h : j.castSucc < i) :
    σ j ≫ toMk₁ i = toMk₁ i.succ
:= by .someMissingNamespaceXYZ
refine ConcreteCategory.hom_ext _ _ (fun k ↦ ?_)
  change toMk₁ i (j.predAbove k) = _
  by_cases! hk : i < k
  · grind [Fin.predAbove_of_castSucc_lt, toMk₁_of_le_castSucc]
  · dsimp
    rw [toMk₁_of_castSucc_lt i.succ k (by simpa), toMk₁_of_castSucc_lt]
    by_cases hk' : j.castSucc < k
    · rwa [Fin.predAbove_of_castSucc_lt _ _ hk', Fin.castSucc_pred_lt_iff]
    · simp only [not_lt] at hk'
      rw [Fin.predAbove_of_le_castSucc _ _ hk']
      exact lt_of_le_of_lt (by simpa) h
```

## Compiler diagnostic
```
[CompilationUnit(i_begin=0, i_end=106, messages=[Message(data='expected token', pos=Position(line=2, column=8), pos_end=None, severity=<Severity.ERROR: 3>, kind=None), Message(data="Function expected at\n  σ\nbut this term has type\n  ?m.1\n\nNote: Expected a function because this term is being applied to the argument\n  j\n\nHint: The identifier `σ` is unknown, and Lean's `autoImplicit` option causes an unknown identifier to be treated as an implicitly bound variable with an unknown type. However, the unknown type cannot be a function, and a function is what Lean expects here. This is often the result of a typo or a missing `import` or `open` statement.", pos=Position(line=2, column=4), pos_end=Position(line=2, column=7), severity=<Severity.ERROR: 3>, kind=None)], invocations=None, goal_state=None, goal_src_boundaries=None, new_constants=None), CompilationUnit(i_begin=106, i_end=664, messages=[], invocations=None, goal_state=None, goal_src_boundaries=None, new_constants=None)]
```

Return a corrected, compiling version of the code.[/INST]


[System] You are Leanstral, a Lean 4 proof assistant. Complete the task below using the provided Lean source context. The target mathlib commit is 5e932f97dd25535344f80f9dd8da3aab83df0fe6 (module Mathlib.Condensed.Light.InternallyProjective).

[User]
The following proof-state transcript was extracted from a real mathlib proof. Use it to answer the question below.

```text
Proof-state transcript for `Mathlib.Condensed.Light.InternallyProjective.free_lightProfinite_internallyProjective_iff_tensor_condition'`:
State 0: ⊢ lemma free_lightProfinite_internallyProjective_iff_tensor_condition' (P : LightProfinite.{u}) :
    InternallyProjective ((free R).obj P.toCondensed) ↔
      ∀ {A B : LightCondMod R} (e : A ⟶ B) [Epi e], (∀ (S : LightProfinite)
        (g : (free R).obj ((S ⊗ P).toCondensed) ⟶ B), ∃ (S' : LightProfinite)
          (π : S' ⟶ S) (_ : Function.Surjective π) (g' : (free R).obj (S' ⊗ P).toCondensed ⟶ A),
            ((free R).map (lightProfiniteToLightCondSet.map (π ▷ P))) ≫ g = g' ≫ e)
Tactic: rw [free_internallyProjective_iff_tensor_condition']
State 1: ⊢ <goal after rw [free_internallyProjective_iff_tensor_condition']>
Tactic: refine ⟨fun h A B e he S g ↦ ?_, fun h A B e he S g ↦ ?_⟩  · specialize h e S ((free R).map (μIso lightProfiniteToLightCondSet _ _).hom ≫ g)
State 2: ⊢ <goal after refine ⟨fun h A B e he S g ↦ ?_, fun h A B e he S g ↦ ?_⟩>
Tactic: obtain ⟨S', π, hπ, g', hh⟩ := h
State 3: ⊢ <goal after obtain ⟨S', π, hπ, g', hh⟩ := h>
Tactic: refine ⟨S', π, hπ, (free R).map (μIso  lightProfiniteToLightCondSet _ _).inv ≫ g', ?_⟩
State 4: ⊢ <goal after refine ⟨S', π, hπ, (free R).map (μIso>
Tactic: rw [assoc, ← hh]
State 5: ⊢ <goal after rw [assoc, ← hh]>
Tactic: simp [-map_comp, ← map_comp_assoc]  · specialize h e S ((free R).map (μIso lightProfiniteToLightCondSet _ _).inv ≫ g)
State 6: ⊢ <goal after simp [-map_comp, ← map_comp_assoc]>
Tactic: obtain ⟨S', π, hπ, g', hh⟩ := h
State 7: ⊢ <goal after obtain ⟨S', π, hπ, g', hh⟩ := h>
Tactic: refine ⟨S', π, hπ, (free R).map  (μIso lightProfiniteToLightCondSet _ _).hom ≫ g', ?_⟩
State 8: ⊢ <goal after refine ⟨S', π, hπ, (free R).map>
Tactic: rw [assoc, ← hh]
State 9: ⊢ <goal after rw [assoc, ← hh]>
Tactic: simp [-map_comp, ← map_comp_assoc, ← μ_natural_left_assoc]  end LightCondensed
```

Question: what is the next tactic after the last recorded state, and why?

<s>[SYSTEM_PROMPT]You are Leanstral, a Lean 4 proof assistant. Complete the task below using the provided Lean source context. The target mathlib commit is 5e932f97dd25535344f80f9dd8da3aab83df0fe6 (module Mathlib.Condensed.Light.InternallyProjective).[/SYSTEM_PROMPT][MODEL_SETTINGS]{"reasoning_effort": "none"}[/MODEL_SETTINGS][INST]The following proof-state transcript was extracted from a real mathlib proof. Use it to answer the question below.

```text
Proof-state transcript for `Mathlib.Condensed.Light.InternallyProjective.free_lightProfinite_internallyProjective_iff_tensor_condition'`:
State 0: ⊢ lemma free_lightProfinite_internallyProjective_iff_tensor_condition' (P : LightProfinite.{u}) :
    InternallyProjective ((free R).obj P.toCondensed) ↔
      ∀ {A B : LightCondMod R} (e : A ⟶ B) [Epi e], (∀ (S : LightProfinite)
        (g : (free R).obj ((S ⊗ P).toCondensed) ⟶ B), ∃ (S' : LightProfinite)
          (π : S' ⟶ S) (_ : Function.Surjective π) (g' : (free R).obj (S' ⊗ P).toCondensed ⟶ A),
            ((free R).map (lightProfiniteToLightCondSet.map (π ▷ P))) ≫ g = g' ≫ e)
Tactic: rw [free_internallyProjective_iff_tensor_condition']
State 1: ⊢ <goal after rw [free_internallyProjective_iff_tensor_condition']>
Tactic: refine ⟨fun h A B e he S g ↦ ?_, fun h A B e he S g ↦ ?_⟩  · specialize h e S ((free R).map (μIso lightProfiniteToLightCondSet _ _).hom ≫ g)
State 2: ⊢ <goal after refine ⟨fun h A B e he S g ↦ ?_, fun h A B e he S g ↦ ?_⟩>
Tactic: obtain ⟨S', π, hπ, g', hh⟩ := h
State 3: ⊢ <goal after obtain ⟨S', π, hπ, g', hh⟩ := h>
Tactic: refine ⟨S', π, hπ, (free R).map (μIso  lightProfiniteToLightCondSet _ _).inv ≫ g', ?_⟩
State 4: ⊢ <goal after refine ⟨S', π, hπ, (free R).map (μIso>
Tactic: rw [assoc, ← hh]
State 5: ⊢ <goal after rw [assoc, ← hh]>
Tactic: simp [-map_comp, ← map_comp_assoc]  · specialize h e S ((free R).map (μIso lightProfiniteToLightCondSet _ _).inv ≫ g)
State 6: ⊢ <goal after simp [-map_comp, ← map_comp_assoc]>
Tactic: obtain ⟨S', π, hπ, g', hh⟩ := h
State 7: ⊢ <goal after obtain ⟨S', π, hπ, g', hh⟩ := h>
Tactic: refine ⟨S', π, hπ, (free R).map  (μIso lightProfiniteToLightCondSet _ _).hom ≫ g', ?_⟩
State 8: ⊢ <goal after refine ⟨S', π, hπ, (free R).map>
Tactic: rw [assoc, ← hh]
State 9: ⊢ <goal after rw [assoc, ← hh]>
Tactic: simp [-map_comp, ← map_comp_assoc, ← μ_natural_left_assoc]  end LightCondensed
```

Question: what is the next tactic after the last recorded state, and why?[/INST]


Instruction: Edit `trPosNum_natEnd` so it still compiles and the proof is simplified where possible. Return the full updated `trPosNum_natEnd` block.

[System] You are Leanstral, a Lean 4 proof assistant. Complete the task below using the provided Lean source context. The target mathlib commit is 5e932f97dd25535344f80f9dd8da3aab83df0fe6 (module Mathlib.Computability.TuringMachine.ToPartrec).

[User]
You are editing a file in `Mathlib/Computability/TuringMachine/ToPartrec.lean` (mathlib commit 5e932f97dd25535344f80f9dd8da3aab83df0fe6).

## Current excerpt (around `Mathlib.Computability.TuringMachine.ToPartrec.trPosNum_natEnd`, L670-L676)
```lean
theorem trPosNum_natEnd : ∀ (n), ∀ x ∈ trPosNum n, natEnd x = false
  | PosNum.one, _, List.Mem.head _ => rfl
  | PosNum.bit0 _, _, List.Mem.head _ => rfl
  | PosNum.bit0 n, _, List.Mem.tail _ h => trPosNum_natEnd n _ h
  | PosNum.bit1 _, _, List.Mem.head _ => rfl
  | PosNum.bit1 n, _, List.Mem.tail _ h => trPosNum_natEnd n _ h
```

## Edit instruction
Edit `trPosNum_natEnd` so it still compiles and the proof is simplified where possible. Return the full updated `trPosNum_natEnd` block.

<s>[SYSTEM_PROMPT]You are Leanstral, a Lean 4 proof assistant. Complete the task below using the provided Lean source context. The target mathlib commit is 5e932f97dd25535344f80f9dd8da3aab83df0fe6 (module Mathlib.Computability.TuringMachine.ToPartrec).[/SYSTEM_PROMPT][MODEL_SETTINGS]{"reasoning_effort": "none"}[/MODEL_SETTINGS][INST]You are editing a file in `Mathlib/Computability/TuringMachine/ToPartrec.lean` (mathlib commit 5e932f97dd25535344f80f9dd8da3aab83df0fe6).

## Current excerpt (around `Mathlib.Computability.TuringMachine.ToPartrec.trPosNum_natEnd`, L670-L676)
```lean
theorem trPosNum_natEnd : ∀ (n), ∀ x ∈ trPosNum n, natEnd x = false
  | PosNum.one, _, List.Mem.head _ => rfl
  | PosNum.bit0 _, _, List.Mem.head _ => rfl
  | PosNum.bit0 n, _, List.Mem.tail _ h => trPosNum_natEnd n _ h
  | PosNum.bit1 _, _, List.Mem.head _ => rfl
  | PosNum.bit1 n, _, List.Mem.tail _ h => trPosNum_natEnd n _ h
```

## Edit instruction
Edit `trPosNum_natEnd` so it still compiles and the proof is simplified where possible. Return the full updated `trPosNum_natEnd` block.
[/INST]