Add files using upload-large-folder tool
Browse files- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Tactic/Do/ProofMode/LeftRight.lean +38 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Tactic/Do/ProofMode/MGoal.lean +154 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Tactic/Do/ProofMode/Pure.lean +50 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Tactic/Do/ProofMode/Refine.lean +80 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Tactic/Do/ProofMode/Revert.lean +37 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Tactic/Do/ProofMode/Specialize.lean +176 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Tactic/Omega/Core.lean +575 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Tactic/Omega/Frontend.lean +712 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Std/Data/ExtTreeMap/Lemmas.lean +0 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Std/Data/ExtTreeSet/Basic.lean +523 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Std/Data/ExtTreeSet/Lemmas.lean +1539 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Std/Data/HashMap/AdditionalOperations.lean +50 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Std/Data/HashMap/Basic.lean +325 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/Util/Store.lean +52 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/Util/StoreInsts.lean +50 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/Util/Sugar.lean +15 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/Util/Task.lean +26 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/Util/Version.lean +226 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/lake/tests/8448/A.lean +2 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/lake/tests/8448/B.lean +5 -0
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Tactic/Do/ProofMode/LeftRight.lean
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2022 Lars König. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Lars König, Mario Carneiro, Sebastian Graf
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Std.Tactic.Do.Syntax
|
| 8 |
+
import Lean.Elab.Tactic.Do.ProofMode.MGoal
|
| 9 |
+
|
| 10 |
+
namespace Lean.Elab.Tactic.Do.ProofMode
|
| 11 |
+
open Std.Do
|
| 12 |
+
open Lean Elab Tactic Meta
|
| 13 |
+
|
| 14 |
+
def mLeftRightCore (right : Bool) (mvar : MVarId) : MetaM MVarId := do
|
| 15 |
+
let g ← instantiateMVars <| ← mvar.getType
|
| 16 |
+
let some goal := parseMGoal? g | throwError "not in proof mode"
|
| 17 |
+
|
| 18 |
+
let mkApp3 (.const ``SPred.or []) σs L R := goal.target | throwError "target is not SPred.or"
|
| 19 |
+
|
| 20 |
+
let (thm, keep) := if right then (``SPred.or_intro_r', R) else (``SPred.or_intro_l', L)
|
| 21 |
+
|
| 22 |
+
let newGoal ← mkFreshExprSyntheticOpaqueMVar {goal with target := keep}.toExpr
|
| 23 |
+
mvar.assign (mkApp5 (mkConst thm) σs goal.hyps L R newGoal)
|
| 24 |
+
return newGoal.mvarId!
|
| 25 |
+
|
| 26 |
+
@[builtin_tactic Lean.Parser.Tactic.mleft]
|
| 27 |
+
def elabMLeft : Tactic | _ => do
|
| 28 |
+
let mvar ← getMainGoal
|
| 29 |
+
mvar.withContext do
|
| 30 |
+
let newGoal ← mLeftRightCore (right := false) mvar
|
| 31 |
+
replaceMainGoal [newGoal]
|
| 32 |
+
|
| 33 |
+
@[builtin_tactic Lean.Parser.Tactic.mright]
|
| 34 |
+
def elabMRight : Tactic | _ => do
|
| 35 |
+
let mvar ← getMainGoal
|
| 36 |
+
mvar.withContext do
|
| 37 |
+
let newGoal ← mLeftRightCore (right := true) mvar
|
| 38 |
+
replaceMainGoal [newGoal]
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Tactic/Do/ProofMode/MGoal.lean
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2022 Lars König. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Lars König, Mario Carneiro, Sebastian Graf
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Std.Do.SPred.DerivedLaws
|
| 8 |
+
import Std.Tactic.Do.ProofMode
|
| 9 |
+
import Lean.Meta
|
| 10 |
+
|
| 11 |
+
namespace Lean.Elab.Tactic.Do.ProofMode
|
| 12 |
+
|
| 13 |
+
open Lean Elab Meta
|
| 14 |
+
open Std.Do Std.Tactic.Do
|
| 15 |
+
|
| 16 |
+
@[match_pattern] def nameAnnotation := `name
|
| 17 |
+
@[match_pattern] def uniqAnnotation := `uniq
|
| 18 |
+
|
| 19 |
+
structure Hyp where
|
| 20 |
+
name : Name
|
| 21 |
+
uniq : Name -- for display purposes only
|
| 22 |
+
p : Expr
|
| 23 |
+
|
| 24 |
+
def parseHyp? : Expr → Option Hyp
|
| 25 |
+
| .mdata ⟨[(nameAnnotation, .ofName name), (uniqAnnotation, .ofName uniq)]⟩ p =>
|
| 26 |
+
some ⟨name, uniq, p⟩ -- NB: mdatas are transparent to SubExpr; hence no pos.push
|
| 27 |
+
| _ => none
|
| 28 |
+
|
| 29 |
+
def Hyp.toExpr (hyp : Hyp) : Expr :=
|
| 30 |
+
.mdata ⟨[(nameAnnotation, .ofName hyp.name), (uniqAnnotation, .ofName hyp.uniq)]⟩ hyp.p
|
| 31 |
+
|
| 32 |
+
-- set_option pp.all true in
|
| 33 |
+
-- #check ⌜True⌝
|
| 34 |
+
def emptyHyp (σs : Expr) : Expr := -- ⌜True⌝ standing in for an empty conjunction of hypotheses
|
| 35 |
+
mkApp3 (mkConst ``SVal.curry) (.sort .zero) σs <| mkLambda `escape .default (mkApp (mkConst ``SVal.StateTuple) σs) (mkConst ``True)
|
| 36 |
+
def parseEmptyHyp? : Expr → Option Expr
|
| 37 |
+
| mkApp3 (.const ``SVal.curry _) (.sort .zero) σs (.lam _ _ (.const ``True _) _) => some σs
|
| 38 |
+
| _ => none
|
| 39 |
+
|
| 40 |
+
def pushLeftConjunct (pos : SubExpr.Pos) : SubExpr.Pos :=
|
| 41 |
+
pos.pushNaryArg 3 1
|
| 42 |
+
|
| 43 |
+
def pushRightConjunct (pos : SubExpr.Pos) : SubExpr.Pos :=
|
| 44 |
+
pos.pushNaryArg 3 2
|
| 45 |
+
|
| 46 |
+
/-- Combine two hypotheses into a conjunction.
|
| 47 |
+
Precondition: Neither `lhs` nor `rhs` is empty (`parseEmptyHyp?`). -/
|
| 48 |
+
def mkAnd! (σs lhs rhs : Expr) : Expr :=
|
| 49 |
+
mkApp3 (mkConst ``SPred.and) σs lhs rhs
|
| 50 |
+
|
| 51 |
+
/-- Smart constructor that cancels away empty hypotheses,
|
| 52 |
+
along with a proof that `lhs ∧ rhs ⊣⊢ₛ result`. -/
|
| 53 |
+
def mkAnd (σs lhs rhs : Expr) : Expr × Expr :=
|
| 54 |
+
if let some _ := parseEmptyHyp? lhs then
|
| 55 |
+
(rhs, mkApp2 (mkConst ``SPred.true_and) σs rhs)
|
| 56 |
+
else if let some _ := parseEmptyHyp? rhs then
|
| 57 |
+
(lhs, mkApp2 (mkConst ``SPred.and_true) σs lhs)
|
| 58 |
+
else
|
| 59 |
+
let result := mkAnd! σs lhs rhs
|
| 60 |
+
(result, mkApp2 (mkConst ``SPred.bientails.refl) σs result)
|
| 61 |
+
|
| 62 |
+
def σs.mkType : Expr := mkApp (mkConst ``List [.succ .zero]) (mkSort (.succ .zero))
|
| 63 |
+
def σs.mkNil : Expr := mkApp (mkConst ``List.nil [.succ .zero]) (mkSort (.succ .zero))
|
| 64 |
+
|
| 65 |
+
def parseAnd? (e : Expr) : Option (Expr × Expr × Expr) :=
|
| 66 |
+
e.app3? ``SPred.and <|> (σs.mkNil, ·) <$> e.app2? ``And
|
| 67 |
+
|
| 68 |
+
structure MGoal where
|
| 69 |
+
σs : Expr -- Q(List Type)
|
| 70 |
+
hyps : Expr -- A conjunction of hypotheses in `SPred σs`, each carrying a name and uniq as metadata (`parseHyp?`)
|
| 71 |
+
target : Expr -- Q(SPred $σs)
|
| 72 |
+
deriving Inhabited
|
| 73 |
+
|
| 74 |
+
def parseMGoal? (expr : Expr) : Option MGoal := do
|
| 75 |
+
let some (σs, hyps, target) := expr.consumeMData.app3? ``MGoalEntails | none
|
| 76 |
+
some { σs, hyps, target }
|
| 77 |
+
|
| 78 |
+
open Tactic in
|
| 79 |
+
def ensureMGoal : TacticM (MVarId × MGoal) := do
|
| 80 |
+
let mvar ← getMainGoal
|
| 81 |
+
let goal ← instantiateMVars <| (← mvar.getType)
|
| 82 |
+
if let some goal := parseMGoal? goal then
|
| 83 |
+
return (mvar, goal)
|
| 84 |
+
else
|
| 85 |
+
throwError "Not in proof mode"
|
| 86 |
+
|
| 87 |
+
def MGoal.strip (goal : MGoal) : Expr := -- omits the .mdata wrapper
|
| 88 |
+
mkApp3 (mkConst ``SPred.entails) goal.σs goal.hyps goal.target
|
| 89 |
+
|
| 90 |
+
/-- Roundtrips with `parseMGoal?`. -/
|
| 91 |
+
def MGoal.toExpr (goal : MGoal) : Expr :=
|
| 92 |
+
mkApp3 (mkConst ``MGoalEntails) goal.σs goal.hyps goal.target
|
| 93 |
+
|
| 94 |
+
partial def MGoal.findHyp? (goal : MGoal) (name : Name) : Option (SubExpr.Pos × Hyp) := go goal.hyps SubExpr.Pos.root
|
| 95 |
+
where
|
| 96 |
+
go (e : Expr) (p : SubExpr.Pos) : Option (SubExpr.Pos × Hyp) := do
|
| 97 |
+
if let some hyp := parseHyp? e then
|
| 98 |
+
if hyp.name = name then
|
| 99 |
+
return (p, hyp)
|
| 100 |
+
else
|
| 101 |
+
none
|
| 102 |
+
else if let some (_, lhs, rhs) := parseAnd? e then
|
| 103 |
+
-- NB: Need to prefer rhs over lhs, like the goal view (Lean.Elab.Tactic.Do.ProofMode.Delab).
|
| 104 |
+
go rhs (pushLeftConjunct p) <|> go lhs (pushRightConjunct p)
|
| 105 |
+
else if let some _ := parseEmptyHyp? e then
|
| 106 |
+
none
|
| 107 |
+
else
|
| 108 |
+
panic! "MGoal.findHyp?: hypothesis without proper metadata: {e}"
|
| 109 |
+
|
| 110 |
+
def MGoal.checkProof (goal : MGoal) (prf : Expr) (suppressWarning : Bool := false) : MetaM Unit := do
|
| 111 |
+
check prf
|
| 112 |
+
let prf_type ← inferType prf
|
| 113 |
+
unless ← isDefEq goal.toExpr prf_type do
|
| 114 |
+
throwError "MGoal.checkProof: the proof and its supposed type did not match.\ngoal: {goal.toExpr}\nproof: {prf_type}"
|
| 115 |
+
unless suppressWarning do
|
| 116 |
+
logWarning m!"stray MGoal.checkProof {prf_type} {goal.toExpr}"
|
| 117 |
+
|
| 118 |
+
def getFreshHypName : TSyntax ``binderIdent → CoreM (Name × Syntax)
|
| 119 |
+
| `(binderIdent| $name:ident) => pure (name.getId, name)
|
| 120 |
+
| stx => return (← mkFreshUserName `h, stx)
|
| 121 |
+
|
| 122 |
+
partial def betaRevPreservingHypNames (σs' e : Expr) (args : Array Expr) : Expr :=
|
| 123 |
+
if let some _σs := parseEmptyHyp? e then
|
| 124 |
+
emptyHyp σs'
|
| 125 |
+
else if let some hyp := parseHyp? e then
|
| 126 |
+
{ hyp with p := hyp.p.betaRev args }.toExpr
|
| 127 |
+
else if let some (_σs, lhs, rhs) := parseAnd? e then
|
| 128 |
+
-- _σs = σ :: σs'
|
| 129 |
+
mkAnd! σs' (betaRevPreservingHypNames σs' lhs args) (betaRevPreservingHypNames σs' rhs args)
|
| 130 |
+
else
|
| 131 |
+
e.betaRev args
|
| 132 |
+
|
| 133 |
+
def betaPreservingHypNames (σs' e : Expr) (args : Array Expr) : Expr :=
|
| 134 |
+
betaRevPreservingHypNames σs' e args.reverse
|
| 135 |
+
|
| 136 |
+
def dropStateList (σs : Expr) (n : Nat) : MetaM Expr := do
|
| 137 |
+
let mut σs := σs
|
| 138 |
+
for _ in [:n] do
|
| 139 |
+
let some (_type, _σ, σs') := (← whnfR σs).app3? ``List.cons | throwError "Ambient state list not a cons {σs}"
|
| 140 |
+
σs := σs'
|
| 141 |
+
return σs
|
| 142 |
+
|
| 143 |
+
def addLocalVarInfo (stx : Syntax) (lctx : LocalContext)
|
| 144 |
+
(expr : Expr) (expectedType? : Option Expr) (isBinder := false) : MetaM Unit := do
|
| 145 |
+
Elab.withInfoContext' (pure ())
|
| 146 |
+
(fun _ =>
|
| 147 |
+
return .inl <| .ofTermInfo
|
| 148 |
+
{ elaborator := .anonymous, lctx, expr, stx, expectedType?, isBinder })
|
| 149 |
+
(return .ofPartialTermInfo { elaborator := .anonymous, lctx, stx, expectedType? })
|
| 150 |
+
|
| 151 |
+
def addHypInfo (stx : Syntax) (σs : Expr) (hyp : Hyp) (isBinder := false) : MetaM Unit := do
|
| 152 |
+
let lctx ← getLCtx
|
| 153 |
+
let ty := mkApp2 (mkConst ``MGoalHypMarker) σs hyp.p
|
| 154 |
+
addLocalVarInfo stx (lctx.mkLocalDecl ⟨hyp.uniq⟩ hyp.name ty) (.fvar ⟨hyp.uniq⟩) ty isBinder
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Tactic/Do/ProofMode/Pure.lean
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2022 Lars König. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Lars König, Mario Carneiro, Sebastian Graf
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Std.Tactic.Do.Syntax
|
| 8 |
+
import Lean.Elab.Tactic.Do.ProofMode.MGoal
|
| 9 |
+
import Lean.Elab.Tactic.Do.ProofMode.Focus
|
| 10 |
+
|
| 11 |
+
namespace Lean.Elab.Tactic.Do.ProofMode
|
| 12 |
+
open Std.Do SPred.Tactic
|
| 13 |
+
open Lean Elab Tactic Meta
|
| 14 |
+
|
| 15 |
+
-- NB: We do not use MVarId.intro because that would mean we require all callers to supply an MVarId.
|
| 16 |
+
-- This function only knows about the hypothesis H=⌜φ⌝ to destruct.
|
| 17 |
+
-- It will provide a proof for Q ∧ H ⊢ₛ T
|
| 18 |
+
-- if `k` produces a proof for Q ⊢ₛ T that may range over a pure proof h : φ.
|
| 19 |
+
-- It calls `k` with the φ in H = ⌜φ⌝ and a proof `h : φ` thereof.
|
| 20 |
+
def mPureCore (σs : Expr) (hyp : Expr) (name : TSyntax ``binderIdent)
|
| 21 |
+
(k : Expr /-φ:Prop-/ → Expr /-h:φ-/ → MetaM (α × MGoal × Expr)) : MetaM (α × MGoal × Expr) := do
|
| 22 |
+
let φ ← mkFreshExprMVar (mkSort .zero)
|
| 23 |
+
let inst ← synthInstance (mkApp3 (mkConst ``IsPure) σs hyp φ)
|
| 24 |
+
let (name, ref) ← getFreshHypName name
|
| 25 |
+
withLocalDeclD name φ fun h => do
|
| 26 |
+
addLocalVarInfo ref (← getLCtx) h φ
|
| 27 |
+
let (a, goal, prf /- : goal.toExpr -/) ← k φ h
|
| 28 |
+
let prf ← mkLambdaFVars #[h] prf
|
| 29 |
+
let prf := mkApp7 (mkConst ``Pure.thm) σs goal.hyps hyp goal.target φ inst prf
|
| 30 |
+
let goal := { goal with hyps := mkAnd! σs goal.hyps hyp }
|
| 31 |
+
return (a, goal, prf)
|
| 32 |
+
|
| 33 |
+
@[builtin_tactic Lean.Parser.Tactic.mpure]
|
| 34 |
+
def elabMPure : Tactic
|
| 35 |
+
| `(tactic| mpure $hyp) => do
|
| 36 |
+
let mvar ← getMainGoal
|
| 37 |
+
mvar.withContext do
|
| 38 |
+
let g ← instantiateMVars <| ← mvar.getType
|
| 39 |
+
let some goal := parseMGoal? g | throwError "not in proof mode"
|
| 40 |
+
let res ← goal.focusHypWithInfo hyp
|
| 41 |
+
let (m, _new_goal, prf) ← mPureCore goal.σs res.focusHyp (← `(binderIdent| $hyp:ident)) fun _ _ => do
|
| 42 |
+
let goal := res.restGoal goal
|
| 43 |
+
let m ← mkFreshExprSyntheticOpaqueMVar goal.toExpr
|
| 44 |
+
return (m, goal, m)
|
| 45 |
+
let prf := res.rewriteHyps goal prf
|
| 46 |
+
mvar.assign prf
|
| 47 |
+
replaceMainGoal [m.mvarId!]
|
| 48 |
+
| _ => throwUnsupportedSyntax
|
| 49 |
+
|
| 50 |
+
macro "mpure_intro" : tactic => `(tactic| apply Pure.intro)
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Tactic/Do/ProofMode/Refine.lean
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2022 Lars König. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Lars König, Mario Carneiro, Sebastian Graf
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Std.Tactic.Do.Syntax
|
| 8 |
+
import Lean.Elab.Tactic.Do.ProofMode.Focus
|
| 9 |
+
import Lean.Elab.Tactic.Do.ProofMode.Assumption
|
| 10 |
+
import Lean.Elab.Tactic.Do.ProofMode.Exact
|
| 11 |
+
|
| 12 |
+
namespace Lean.Elab.Tactic.Do.ProofMode
|
| 13 |
+
open Std.Do SPred.Tactic Lean.Parser.Tactic
|
| 14 |
+
open Lean Elab Tactic Meta
|
| 15 |
+
|
| 16 |
+
def patAsTerm (pat : MRefinePat) (expected : Option Expr := none) : OptionT TacticM Expr := do
|
| 17 |
+
match pat with
|
| 18 |
+
| .pure t => elabTerm t expected
|
| 19 |
+
| .one name =>
|
| 20 |
+
if let `(binderIdent| $name:ident) := name then
|
| 21 |
+
elabTerm (← `($name)) expected
|
| 22 |
+
else failure
|
| 23 |
+
| _ => failure
|
| 24 |
+
|
| 25 |
+
partial def mRefineCore (goal : MGoal) (pat : MRefinePat) (k : MGoal → TSyntax ``binderIdent → TacticM Expr) : TacticM Expr := do
|
| 26 |
+
match pat with
|
| 27 |
+
| .stateful name => liftMetaM do
|
| 28 |
+
match name with
|
| 29 |
+
| `(binderIdent| $name:ident) => do
|
| 30 |
+
let some prf ← goal.exact name | throwError "unknown hypothesis '{repr name}'"
|
| 31 |
+
return prf
|
| 32 |
+
| _ => do
|
| 33 |
+
let some prf ← goal.assumption | throwError "could not solve {goal.target} by assumption"
|
| 34 |
+
return prf
|
| 35 |
+
| .pure t => do
|
| 36 |
+
goal.exactPure t
|
| 37 |
+
| .one name =>
|
| 38 |
+
if let `(binderIdent| $_:ident) := name then
|
| 39 |
+
mRefineCore goal (.pure ⟨name.raw⟩) k <|> mRefineCore goal (.stateful name) k
|
| 40 |
+
else
|
| 41 |
+
mRefineCore goal (.stateful name) k
|
| 42 |
+
| .hole name => k goal name
|
| 43 |
+
| .tuple [] => throwUnsupportedSyntax
|
| 44 |
+
| .tuple [p] => mRefineCore goal p k
|
| 45 |
+
| .tuple (p::ps) => do
|
| 46 |
+
let T ← whnfR goal.target
|
| 47 |
+
if let some (σs, T₁, T₂) := parseAnd? T.consumeMData then
|
| 48 |
+
let prf₁ ← mRefineCore { goal with target := T₁ } p k
|
| 49 |
+
let prf₂ ← mRefineCore { goal with target := T₂ } (.tuple ps) k
|
| 50 |
+
return mkApp6 (mkConst ``SPred.and_intro) σs goal.hyps T₁ T₂ prf₁ prf₂
|
| 51 |
+
else if let some (α, σs, ψ) := T.app3? ``SPred.exists then
|
| 52 |
+
let some witness ← patAsTerm p (some α) | throwError "pattern does not elaborate to a term to instantiate ψ"
|
| 53 |
+
let prf ← mRefineCore { goal with target := ψ.betaRev #[witness] } (.tuple ps) k
|
| 54 |
+
let u ← getLevel α
|
| 55 |
+
return mkApp6 (mkConst ``SPred.exists_intro' [u]) α σs goal.hyps ψ witness prf
|
| 56 |
+
else throwError "Neither a conjunction nor an existential quantifier {goal.target}"
|
| 57 |
+
|
| 58 |
+
@[builtin_tactic Lean.Parser.Tactic.mrefine]
|
| 59 |
+
def elabMRefine : Tactic
|
| 60 |
+
| `(tactic| mrefine $pat:mrefinePat) => do
|
| 61 |
+
let pat ← liftMacroM <| MRefinePat.parse pat
|
| 62 |
+
let (mvar, goal) ← mStartMVar (← getMainGoal)
|
| 63 |
+
mvar.withContext do
|
| 64 |
+
|
| 65 |
+
let goals ← IO.mkRef #[]
|
| 66 |
+
let prf ← mRefineCore goal pat fun goal name => do
|
| 67 |
+
let m ← mkFreshExprSyntheticOpaqueMVar goal.toExpr name.raw.getId
|
| 68 |
+
goals.modify (·.push m.mvarId!)
|
| 69 |
+
return m
|
| 70 |
+
mvar.assign prf
|
| 71 |
+
replaceMainGoal (← goals.get).toList
|
| 72 |
+
| _ => throwUnsupportedSyntax
|
| 73 |
+
|
| 74 |
+
@[builtin_tactic Lean.Parser.Tactic.mexists]
|
| 75 |
+
def elabMExists : Tactic
|
| 76 |
+
| `(tactic| mexists $args,*) => do
|
| 77 |
+
let pats ← args.getElems.mapM fun t => `(mrefinePat| ⌜$t⌝)
|
| 78 |
+
let pat ← pats.foldrM (fun pat acc => `(mrefinePat| ⟨$pat, $acc⟩)) (← `(mrefinePat| ?_))
|
| 79 |
+
evalTactic (← `(tactic| (mrefine $pat; try massumption)))
|
| 80 |
+
| _ => throwUnsupportedSyntax
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Tactic/Do/ProofMode/Revert.lean
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2022 Lars König. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Lars König, Mario Carneiro, Sebastian Graf
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Std.Tactic.Do.Syntax
|
| 8 |
+
import Lean.Elab.Tactic.Do.ProofMode.Focus
|
| 9 |
+
import Lean.Elab.Tactic.Do.ProofMode.Basic
|
| 10 |
+
|
| 11 |
+
namespace Lean.Elab.Tactic.Do.ProofMode
|
| 12 |
+
open Std.Do SPred.Tactic
|
| 13 |
+
open Lean Elab Tactic Meta
|
| 14 |
+
|
| 15 |
+
partial def mRevertStep (goal : MGoal) (ref : TSyntax `ident) (k : MGoal → MetaM Expr) : MetaM Expr := do
|
| 16 |
+
let res ← goal.focusHypWithInfo ref
|
| 17 |
+
let P := goal.hyps
|
| 18 |
+
let Q := res.restHyps
|
| 19 |
+
let H := res.focusHyp
|
| 20 |
+
let T := goal.target
|
| 21 |
+
let prf ← k { goal with hyps := Q, target := mkApp3 (mkConst ``SPred.imp) goal.σs H T }
|
| 22 |
+
let prf := mkApp7 (mkConst ``Revert.revert) goal.σs P Q H T res.proof prf
|
| 23 |
+
return prf
|
| 24 |
+
|
| 25 |
+
@[builtin_tactic Lean.Parser.Tactic.mrevert]
|
| 26 |
+
def elabMRevert : Tactic
|
| 27 |
+
| `(tactic| mrevert $h) => do
|
| 28 |
+
let (mvar, goal) ← mStartMVar (← getMainGoal)
|
| 29 |
+
mvar.withContext do
|
| 30 |
+
|
| 31 |
+
let goals ← IO.mkRef []
|
| 32 |
+
mvar.assign (← mRevertStep goal h fun newGoal => do
|
| 33 |
+
let m ← mkFreshExprSyntheticOpaqueMVar newGoal.toExpr
|
| 34 |
+
goals.modify (m.mvarId! :: ·)
|
| 35 |
+
return m)
|
| 36 |
+
replaceMainGoal (← goals.get)
|
| 37 |
+
| _ => throwUnsupportedSyntax
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Tactic/Do/ProofMode/Specialize.lean
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2022 Lars König. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Lars König, Mario Carneiro, Sebastian Graf
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Std.Tactic.Do.Syntax
|
| 8 |
+
import Lean.Elab.Tactic.Do.ProofMode.MGoal
|
| 9 |
+
import Lean.Elab.Tactic.Do.ProofMode.Focus
|
| 10 |
+
import Lean.Elab.Tactic.Do.ProofMode.Basic
|
| 11 |
+
import Lean.Elab.Tactic.Do.ProofMode.Pure
|
| 12 |
+
|
| 13 |
+
namespace Lean.Elab.Tactic.Do.ProofMode
|
| 14 |
+
open Std.Do SPred.Tactic
|
| 15 |
+
open Lean Elab Tactic Meta
|
| 16 |
+
|
| 17 |
+
initialize registerTraceClass `Meta.Tactic.Do.specialize
|
| 18 |
+
|
| 19 |
+
def mSpecializeImpStateful (σs : Expr) (P : Expr) (QR : Expr) (arg : TSyntax `term) : OptionT TacticM (Expr × Expr) := do
|
| 20 |
+
guard (arg.raw.isIdent)
|
| 21 |
+
let some argRes := focusHyp σs (mkAnd! σs P QR) arg.raw.getId | failure
|
| 22 |
+
let some hyp := parseHyp? argRes.focusHyp | failure
|
| 23 |
+
addHypInfo arg σs hyp
|
| 24 |
+
OptionT.mk do -- no OptionT failure after this point
|
| 25 |
+
-- The goal is P ∧ (Q → R)
|
| 26 |
+
-- argRes.proof : P ∧ (Q → R) ⊣⊢ₛ P' ∧ Q
|
| 27 |
+
-- we want to return (R, (proof : P ∧ (Q → R) ⊢ₛ P ∧ R))
|
| 28 |
+
let some specHyp := parseHyp? QR | panic! "Precondition of specializeImpStateful violated"
|
| 29 |
+
let P' := argRes.restHyps
|
| 30 |
+
let Q := argRes.focusHyp
|
| 31 |
+
let hrefocus := argRes.proof -- P ∧ (Q → R) ⊣⊢ₛ P' ∧ Q
|
| 32 |
+
let mkApp3 (.const ``SPred.imp []) σs Q' R := specHyp.p | throwError "Expected implication {QR}"
|
| 33 |
+
let proof := mkApp6 (mkConst ``Specialize.imp_stateful) σs P P' Q R hrefocus
|
| 34 |
+
-- check proof
|
| 35 |
+
trace[Meta.Tactic.Do.specialize] "Statefully specialize {specHyp.p} with {Q}. New Goal: {mkAnd! σs P R}"
|
| 36 |
+
unless ← isDefEq Q Q' do
|
| 37 |
+
throwError "failed to specialize {specHyp.p} with {Q}"
|
| 38 |
+
|
| 39 |
+
return ({ specHyp with p := R }.toExpr, proof)
|
| 40 |
+
|
| 41 |
+
def mSpecializeImpPure (_σs : Expr) (P : Expr) (QR : Expr) (arg : TSyntax `term) : OptionT TacticM (Expr × Expr) := do
|
| 42 |
+
let some specHyp := parseHyp? QR | panic! "Precondition of specializeImpPure violated"
|
| 43 |
+
let mkApp3 (.const ``SPred.imp []) σs Q R := specHyp.p | failure
|
| 44 |
+
let mut φ ← mkFreshExprMVar (mkSort .zero)
|
| 45 |
+
let mut (hφ, mvarIds) ← try
|
| 46 |
+
elabTermWithHoles arg.raw φ `specialize (allowNaturalHoles := true)
|
| 47 |
+
catch _ => failure
|
| 48 |
+
-- We might have hφ : φ and Q = ⌜φ⌝. In this case, convert hφ to a proof of ⊢ₛ ⌜φ⌝,
|
| 49 |
+
-- so that we can infer an instance of `PropAsSPredTautology`.
|
| 50 |
+
-- NB: PropAsSPredTautology φ ⌜φ⌝ is unfortunately impossible because ⊢ₛ ⌜φ⌝ does not imply φ.
|
| 51 |
+
-- Hence this additional (lossy) conversion.
|
| 52 |
+
if let some inst ← synthInstance? (mkApp3 (mkConst ``IsPure) σs Q φ) then
|
| 53 |
+
hφ := mkApp5 (mkConst ``Specialize.pure_taut) σs φ Q inst hφ
|
| 54 |
+
φ := mkApp2 (mkConst ``tautological) σs Q
|
| 55 |
+
|
| 56 |
+
let some inst ← synthInstance? (mkApp3 (mkConst ``PropAsSPredTautology) φ σs Q)
|
| 57 |
+
| failure
|
| 58 |
+
|
| 59 |
+
OptionT.mk do -- no OptionT failure after this point
|
| 60 |
+
-- The goal is P ∧ (Q → R)
|
| 61 |
+
-- we want to return (R, (proof : P ∧ (Q → R) ⊢ₛ P ∧ R))
|
| 62 |
+
pushGoals mvarIds
|
| 63 |
+
let proof := mkApp7 (mkConst ``Specialize.imp_pure) σs φ P Q R inst hφ
|
| 64 |
+
-- check proof
|
| 65 |
+
trace[Meta.Tactic.Do.specialize] "Purely specialize {specHyp.p} with {Q}. New Goal: {mkAnd! σs P R}"
|
| 66 |
+
-- logInfo m!"proof: {← inferType proof}"
|
| 67 |
+
return ({ specHyp with p := R }.toExpr, proof)
|
| 68 |
+
|
| 69 |
+
def mSpecializeForall (_σs : Expr) (P : Expr) (Ψ : Expr) (arg : TSyntax `term) : OptionT TacticM (Expr × Expr) := do
|
| 70 |
+
let some specHyp := parseHyp? Ψ | panic! "Precondition of specializeForall violated"
|
| 71 |
+
let mkApp3 (.const ``SPred.forall [u]) α σs αR := specHyp.p | failure
|
| 72 |
+
let (a, mvarIds) ← try
|
| 73 |
+
elabTermWithHoles arg.raw α `specialize (allowNaturalHoles := true)
|
| 74 |
+
catch _ => failure
|
| 75 |
+
OptionT.mk do -- no OptionT failure after this point
|
| 76 |
+
pushGoals mvarIds
|
| 77 |
+
let proof := mkApp5 (mkConst ``Specialize.forall [u]) σs α P αR a
|
| 78 |
+
let R := αR.beta #[a]
|
| 79 |
+
-- check proof
|
| 80 |
+
trace[Meta.Tactic.Do.specialize] "Instantiate {specHyp.p} with {a}. New Goal: {mkAnd! σs P R}"
|
| 81 |
+
return ({ specHyp with p := R }.toExpr, proof)
|
| 82 |
+
|
| 83 |
+
@[builtin_tactic Lean.Parser.Tactic.mspecialize]
|
| 84 |
+
def elabMSpecialize : Tactic
|
| 85 |
+
| `(tactic| mspecialize $hyp $args*) => do
|
| 86 |
+
let (mvar, goal) ← mStartMVar (← getMainGoal)
|
| 87 |
+
mvar.withContext do
|
| 88 |
+
|
| 89 |
+
-- Want to prove goal P ⊢ T, where hyp occurs in P.
|
| 90 |
+
-- So we
|
| 91 |
+
-- 1. focus on hyp (referred to as H): P ⊣⊢ₛ P' ∧ H. Prove P' ∧ H ⊢ₛ T
|
| 92 |
+
-- 2. Produce a (transitive chain of) proofs
|
| 93 |
+
-- P' ∧ H ⊢ P' ∧ H₁ ⊢ₛ P' ∧ H₂ ⊢ₛ ...
|
| 94 |
+
-- One for each arg; end up with goal P' ∧ H' ⊢ₛ T
|
| 95 |
+
-- 3. Recombine with mkAnd (NB: P' might be empty), compose with P' ∧ H' ⊣⊢ₛ mkAnd P' H'.
|
| 96 |
+
-- 4. Make a new MVar for goal `mkAnd P' H' ⊢ T` and assign the transitive chain.
|
| 97 |
+
let some specFocus := goal.focusHyp hyp.getId | throwError "unknown identifier '{hyp}'"
|
| 98 |
+
let σs := goal.σs
|
| 99 |
+
let P := specFocus.restHyps
|
| 100 |
+
let mut H := specFocus.focusHyp
|
| 101 |
+
let some hyp' := parseHyp? H | panic! "Invariant of specialize violated"
|
| 102 |
+
addHypInfo hyp σs hyp'
|
| 103 |
+
-- invariant: proof (_ : { goal with hyps := mkAnd! σs P H }.toExpr) fills the mvar
|
| 104 |
+
let mut proof : Expr → Expr :=
|
| 105 |
+
mkApp7 (mkConst ``Specialize.focus) σs goal.hyps P H goal.target specFocus.proof
|
| 106 |
+
|
| 107 |
+
for arg in args do
|
| 108 |
+
let res? ← OptionT.run
|
| 109 |
+
(mSpecializeImpStateful σs P H arg
|
| 110 |
+
<|> mSpecializeImpPure σs P H arg
|
| 111 |
+
<|> mSpecializeForall σs P H arg)
|
| 112 |
+
match res? with
|
| 113 |
+
| some (H', H2H') =>
|
| 114 |
+
-- logInfo m!"H: {H}, proof: {← inferType H2H'}"
|
| 115 |
+
proof := fun hgoal => proof (mkApp6 (mkConst ``SPred.entails.trans) σs (mkAnd! σs P H) (mkAnd! σs P H') goal.target H2H' hgoal)
|
| 116 |
+
H := H'
|
| 117 |
+
| none =>
|
| 118 |
+
throwError "Could not specialize {H} with {arg}"
|
| 119 |
+
|
| 120 |
+
let newMVar ← mkFreshExprSyntheticOpaqueMVar { goal with hyps := mkAnd! σs P H }.toExpr
|
| 121 |
+
mvar.assign (proof newMVar)
|
| 122 |
+
replaceMainGoal [newMVar.mvarId!]
|
| 123 |
+
|
| 124 |
+
| _ => throwUnsupportedSyntax
|
| 125 |
+
|
| 126 |
+
@[builtin_tactic Lean.Parser.Tactic.mspecializePure]
|
| 127 |
+
def elabMspecializePure : Tactic
|
| 128 |
+
| `(tactic| mspecialize_pure $head $args* => $hyp) => do
|
| 129 |
+
-- "mspecialize_pure" >> term >> many (ppSpace >> checkColGt "irrelevant" >> termParser (eval_prec max)) >> "as" >> ident
|
| 130 |
+
let (mvar, goal) ← mStartMVar (← getMainGoal)
|
| 131 |
+
mvar.withContext do
|
| 132 |
+
|
| 133 |
+
-- Want to prove goal P ⊢ₛ T. `head` is a pure proof of type `φ` that turns into `⊢ₛ H` via `start_entails`.
|
| 134 |
+
-- So we
|
| 135 |
+
-- 1. Introduce `head` via `PropAsEntails` as stateful hypothesis named `hyp`, P ∧ (hyp : H) ⊢ₛ T
|
| 136 |
+
-- 2. (from here on it's the same as `mspecialize`.)
|
| 137 |
+
-- Produce a (transitive chain of) proofs
|
| 138 |
+
-- P ∧ H ⊢ P ∧ H₁ ⊢ₛ P ∧ H₂ ⊢ₛ ...
|
| 139 |
+
-- One for each arg; end up with goal P ∧ H' ⊢ₛ T
|
| 140 |
+
-- 3. Recombine with mkAnd (NB: P' might be empty), compose with P' ∧ H' ⊣⊢ₛ mkAnd P' H'.
|
| 141 |
+
-- 4. Make a new MVar for goal `mkAnd P' H' ⊢ T` and assign the transitive chain.
|
| 142 |
+
let σs := goal.σs
|
| 143 |
+
let P := goal.hyps
|
| 144 |
+
let T := goal.target
|
| 145 |
+
let hφ ← elabTerm head none
|
| 146 |
+
let φ ← inferType hφ
|
| 147 |
+
let H ← mkFreshExprMVar (mkApp (mkConst ``SPred) σs)
|
| 148 |
+
let inst ← synthInstance (mkApp3 (mkConst ``PropAsSPredTautology) φ σs H)
|
| 149 |
+
let uniq ← mkFreshId
|
| 150 |
+
let mut H := (Hyp.mk hyp.getId uniq (← instantiateMVars H)).toExpr
|
| 151 |
+
|
| 152 |
+
let goal : MGoal := { goal with hyps := mkAnd! σs P H }
|
| 153 |
+
-- invariant: proof (_ : { goal with hyps := mkAnd! σs P H }.toExpr) fills the mvar
|
| 154 |
+
let mut proof : Expr → Expr :=
|
| 155 |
+
mkApp8 (mkConst ``Specialize.pure_start) σs φ H P T inst hφ
|
| 156 |
+
|
| 157 |
+
for arg in args do
|
| 158 |
+
let res? ← OptionT.run
|
| 159 |
+
(mSpecializeImpStateful σs P H ⟨arg⟩
|
| 160 |
+
<|> mSpecializeImpPure σs P H ⟨arg⟩
|
| 161 |
+
<|> mSpecializeForall σs P H ⟨arg⟩)
|
| 162 |
+
match res? with
|
| 163 |
+
| some (H', H2H') =>
|
| 164 |
+
-- logInfo m!"H: {H}, proof: {← inferType H2H'}"
|
| 165 |
+
proof := fun hgoal => proof (mkApp6 (mkConst ``SPred.entails.trans) σs (mkAnd! σs P H) (mkAnd! σs P H') goal.target H2H' hgoal)
|
| 166 |
+
H := H'
|
| 167 |
+
| none =>
|
| 168 |
+
throwError "Could not specialize {H} with {arg}"
|
| 169 |
+
|
| 170 |
+
let some hyp' := parseHyp? H | panic! "Invariant of specialize_pure violated"
|
| 171 |
+
addHypInfo hyp σs hyp'
|
| 172 |
+
|
| 173 |
+
let newMVar ← mkFreshExprSyntheticOpaqueMVar { goal with hyps := mkAnd! σs P H }.toExpr
|
| 174 |
+
mvar.assign (proof newMVar)
|
| 175 |
+
replaceMainGoal [newMVar.mvarId!]
|
| 176 |
+
| _ => throwUnsupportedSyntax
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Tactic/Omega/Core.lean
ADDED
|
@@ -0,0 +1,575 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2023 Lean FRO, LLC. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Kim Morrison
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Init.Omega.Constraint
|
| 8 |
+
import Lean.Elab.Tactic.Omega.OmegaM
|
| 9 |
+
import Lean.Elab.Tactic.Omega.MinNatAbs
|
| 10 |
+
|
| 11 |
+
namespace Lean.Elab.Tactic.Omega
|
| 12 |
+
|
| 13 |
+
initialize Lean.registerTraceClass `omega
|
| 14 |
+
|
| 15 |
+
open Lean (Expr)
|
| 16 |
+
open Lean Meta Omega
|
| 17 |
+
|
| 18 |
+
open Lean in
|
| 19 |
+
instance : ToExpr LinearCombo where
|
| 20 |
+
toExpr lc :=
|
| 21 |
+
(Expr.const ``LinearCombo.mk []).app (toExpr lc.const) |>.app (toExpr lc.coeffs)
|
| 22 |
+
toTypeExpr := .const ``LinearCombo []
|
| 23 |
+
|
| 24 |
+
open Lean in
|
| 25 |
+
instance : ToExpr Constraint where
|
| 26 |
+
toExpr s :=
|
| 27 |
+
(Expr.const ``Constraint.mk []).app (toExpr s.lowerBound) |>.app (toExpr s.upperBound)
|
| 28 |
+
toTypeExpr := .const ``Constraint []
|
| 29 |
+
|
| 30 |
+
/--
|
| 31 |
+
A delayed proof that a constraint is satisfied at the atoms.
|
| 32 |
+
-/
|
| 33 |
+
abbrev Proof : Type := OmegaM Expr
|
| 34 |
+
|
| 35 |
+
/--
|
| 36 |
+
Our internal representation of an argument "justifying" that a constraint holds on some coefficients.
|
| 37 |
+
We'll use this to construct the proof term once a contradiction is found.
|
| 38 |
+
-/
|
| 39 |
+
inductive Justification : Constraint → Coeffs → Type
|
| 40 |
+
/--
|
| 41 |
+
`Problem.assumptions[i]` generates a proof that `s.sat' coeffs atoms`
|
| 42 |
+
-/
|
| 43 |
+
| assumption (s : Constraint) (x : Coeffs) (i : Nat) : Justification s x
|
| 44 |
+
/-- The result of `tidy` on another `Justification`. -/
|
| 45 |
+
| tidy (j : Justification s c) : Justification (tidyConstraint s c) (tidyCoeffs s c)
|
| 46 |
+
/-- The result of `combine` on two `Justifications`. -/
|
| 47 |
+
| combine {s t c} (j : Justification s c) (k : Justification t c) : Justification (s.combine t) c
|
| 48 |
+
/-- A linear `combo` of two `Justifications`. -/
|
| 49 |
+
| combo {s t x y} (a : Int) (j : Justification s x) (b : Int) (k : Justification t y) :
|
| 50 |
+
Justification (Constraint.combo a s b t) (Coeffs.combo a x b y)
|
| 51 |
+
/--
|
| 52 |
+
The justification for the constraint constructed using "balanced mod" while
|
| 53 |
+
eliminating an equality.
|
| 54 |
+
-/
|
| 55 |
+
| bmod (m : Nat) (r : Int) (i : Nat) {x} (j : Justification (.exact r) x) :
|
| 56 |
+
Justification (.exact (Int.bmod r m)) (bmod_coeffs m i x)
|
| 57 |
+
|
| 58 |
+
/-- Wrapping for `Justification.tidy` when `tidy?` is nonempty. -/
|
| 59 |
+
nonrec def Justification.tidy? (j : Justification s c) : Option (Σ s' c', Justification s' c') :=
|
| 60 |
+
match tidy? (s, c) with
|
| 61 |
+
| some _ => some ⟨_, _, tidy j⟩
|
| 62 |
+
| none => none
|
| 63 |
+
|
| 64 |
+
namespace Justification
|
| 65 |
+
|
| 66 |
+
private def bullet (s : String) := "• " ++ s.replace "\n" "\n "
|
| 67 |
+
|
| 68 |
+
/-- Print a `Justification` as an indented tree structure. -/
|
| 69 |
+
def toString : Justification s x → String
|
| 70 |
+
| assumption _ _ i => s!"{x} ∈ {s}: assumption {i}"
|
| 71 |
+
| @tidy s' x' j =>
|
| 72 |
+
if s == s' && x == x' then j.toString else s!"{x} ∈ {s}: tidying up:\n" ++ bullet j.toString
|
| 73 |
+
| combine j k =>
|
| 74 |
+
s!"{x} ∈ {s}: combination of:\n" ++ bullet j.toString ++ "\n" ++ bullet k.toString
|
| 75 |
+
| combo a j b k =>
|
| 76 |
+
s!"{x} ∈ {s}: {a} * x + {b} * y combo of:\n" ++ bullet j.toString ++ "\n" ++ bullet k.toString
|
| 77 |
+
| bmod m _ i j =>
|
| 78 |
+
s!"{x} ∈ {s}: bmod with m={m} and i={i} of:\n" ++ bullet j.toString
|
| 79 |
+
|
| 80 |
+
instance : ToString (Justification s x) where toString := toString
|
| 81 |
+
|
| 82 |
+
open Lean
|
| 83 |
+
|
| 84 |
+
/-- Construct the proof term associated to a `tidy` step. -/
|
| 85 |
+
def tidyProof (s : Constraint) (x : Coeffs) (v : Expr) (prf : Expr) : Expr :=
|
| 86 |
+
mkApp4 (.const ``tidy_sat []) (toExpr s) (toExpr x) v prf
|
| 87 |
+
|
| 88 |
+
/-- Construct the proof term associated to a `combine` step. -/
|
| 89 |
+
def combineProof (s t : Constraint) (x : Coeffs) (v : Expr) (ps pt : Expr) : Expr :=
|
| 90 |
+
mkApp6 (.const ``Constraint.combine_sat' []) (toExpr s) (toExpr t) (toExpr x) v ps pt
|
| 91 |
+
|
| 92 |
+
/-- Construct the proof term associated to a `combo` step. -/
|
| 93 |
+
def comboProof (s t : Constraint) (a : Int) (x : Coeffs) (b : Int) (y : Coeffs)
|
| 94 |
+
(v : Expr) (px py : Expr) : Expr :=
|
| 95 |
+
mkApp9 (.const ``combo_sat' []) (toExpr s) (toExpr t) (toExpr a) (toExpr x) (toExpr b) (toExpr y)
|
| 96 |
+
v px py
|
| 97 |
+
|
| 98 |
+
/-- Construct the proof term associated to a `bmod` step. -/
|
| 99 |
+
def bmodProof (m : Nat) (r : Int) (i : Nat) (x : Coeffs) (v : Expr) (w : Expr) : MetaM Expr := do
|
| 100 |
+
let m := toExpr m
|
| 101 |
+
let r := toExpr r
|
| 102 |
+
let i := toExpr i
|
| 103 |
+
let x := toExpr x
|
| 104 |
+
let h ← mkDecideProof (mkApp4 (.const ``LE.le [.zero]) (.const ``Nat []) (.const ``instLENat [])
|
| 105 |
+
(.app (.const ``Coeffs.length []) x) i)
|
| 106 |
+
let lhs := mkApp2 (.const ``Coeffs.get []) v i
|
| 107 |
+
let rhs := mkApp3 (.const ``bmod_div_term []) m x v
|
| 108 |
+
let p ← mkEqReflWithExpectedType lhs rhs
|
| 109 |
+
return mkApp8 (.const ``bmod_sat []) m r i x v h p w
|
| 110 |
+
|
| 111 |
+
-- TODO could we increase sharing in the proof term here?
|
| 112 |
+
|
| 113 |
+
/-- Constructs a proof that `s.sat' c v = true` -/
|
| 114 |
+
def proof (v : Expr) (assumptions : Array Proof) : Justification s c → Proof
|
| 115 |
+
| assumption s c i => assumptions[i]!
|
| 116 |
+
| @tidy s c j => return tidyProof s c v (← proof v assumptions j)
|
| 117 |
+
| @combine s t c j k =>
|
| 118 |
+
return combineProof s t c v (← proof v assumptions j) (← proof v assumptions k)
|
| 119 |
+
| @combo s t x y a j b k =>
|
| 120 |
+
return comboProof s t a x b y v (← proof v assumptions j) (← proof v assumptions k)
|
| 121 |
+
| @bmod m r i x j => do bmodProof m r i x v (← proof v assumptions j)
|
| 122 |
+
|
| 123 |
+
end Justification
|
| 124 |
+
|
| 125 |
+
/-- A `Justification` bundled together with its parameters. -/
|
| 126 |
+
structure Fact where
|
| 127 |
+
/-- The coefficients of a constraint. -/
|
| 128 |
+
coeffs : Coeffs
|
| 129 |
+
/-- The constraint. -/
|
| 130 |
+
constraint : Constraint
|
| 131 |
+
/-- The justification of a derived fact. -/
|
| 132 |
+
justification : Justification constraint coeffs
|
| 133 |
+
|
| 134 |
+
namespace Fact
|
| 135 |
+
|
| 136 |
+
instance : ToString Fact where
|
| 137 |
+
toString f := toString f.justification
|
| 138 |
+
|
| 139 |
+
/-- `tidy`, implemented on `Fact`. -/
|
| 140 |
+
def tidy (f : Fact) : Fact :=
|
| 141 |
+
match f.justification.tidy? with
|
| 142 |
+
| some ⟨constraint, coeffs, justification⟩ => { coeffs, constraint, justification }
|
| 143 |
+
| none => f
|
| 144 |
+
|
| 145 |
+
/-- `combo`, implemented on `Fact`. -/
|
| 146 |
+
def combo (a : Int) (f : Fact) (b : Int) (g : Fact) : Fact :=
|
| 147 |
+
{ coeffs := .combo a f.coeffs b g.coeffs
|
| 148 |
+
constraint := .combo a f.constraint b g.constraint
|
| 149 |
+
justification := .combo a f.justification b g.justification }
|
| 150 |
+
|
| 151 |
+
end Fact
|
| 152 |
+
|
| 153 |
+
/--
|
| 154 |
+
A `omega` problem.
|
| 155 |
+
|
| 156 |
+
This is a hybrid structure:
|
| 157 |
+
it contains both `Expr` objects giving proofs of the "ground" assumptions
|
| 158 |
+
(or rather continuations which will produce the proofs when needed)
|
| 159 |
+
and internal representations of the linear constraints that we manipulate in the algorithm.
|
| 160 |
+
|
| 161 |
+
While the algorithm is running we do not synthesize any new `Expr` proofs: proof extraction happens
|
| 162 |
+
only once we've found a contradiction.
|
| 163 |
+
-/
|
| 164 |
+
structure Problem where
|
| 165 |
+
/-- The ground assumptions that the algorithm starts from. -/
|
| 166 |
+
assumptions : Array Proof := ∅
|
| 167 |
+
/-- The number of variables in the problem. -/
|
| 168 |
+
numVars : Nat := 0
|
| 169 |
+
/-- The current constraints, indexed by their coefficients. -/
|
| 170 |
+
constraints : Std.HashMap Coeffs Fact := ∅
|
| 171 |
+
/--
|
| 172 |
+
The coefficients for which `constraints` contains an exact constraint (i.e. an equality).
|
| 173 |
+
-/
|
| 174 |
+
equalities : Std.HashSet Coeffs := ∅
|
| 175 |
+
/--
|
| 176 |
+
Equations that have already been used to eliminate variables,
|
| 177 |
+
along with the variable which was removed, and its coefficient (either `1` or `-1`).
|
| 178 |
+
The earlier elements are more recent,
|
| 179 |
+
so if these are being reapplied it is essential to use `List.foldr`.
|
| 180 |
+
-/
|
| 181 |
+
eliminations : List (Fact × Nat × Int) := []
|
| 182 |
+
/-- Whether the problem is possible. -/
|
| 183 |
+
possible : Bool := true
|
| 184 |
+
/-- If the problem is impossible, then `proveFalse?` will contain a proof of `False`. -/
|
| 185 |
+
proveFalse? : Option Proof := none
|
| 186 |
+
/-- Invariant between `possible` and `proveFalse?`. -/
|
| 187 |
+
proveFalse?_spec : possible || proveFalse?.isSome := by rfl
|
| 188 |
+
/--
|
| 189 |
+
If we have found a contradiction,
|
| 190 |
+
`explanation?` will contain a human readable account of the deriviation.
|
| 191 |
+
-/
|
| 192 |
+
explanation? : Thunk String := ""
|
| 193 |
+
|
| 194 |
+
namespace Problem
|
| 195 |
+
|
| 196 |
+
/-- Check if a problem has no constraints. -/
|
| 197 |
+
def isEmpty (p : Problem) : Bool := p.constraints.isEmpty
|
| 198 |
+
|
| 199 |
+
instance : ToString Problem where
|
| 200 |
+
toString p :=
|
| 201 |
+
if p.possible then
|
| 202 |
+
if p.isEmpty then
|
| 203 |
+
"trivial"
|
| 204 |
+
else
|
| 205 |
+
"\n".intercalate <|
|
| 206 |
+
(p.constraints.toList.map fun ⟨coeffs, ⟨_, cst, _⟩⟩ => s!"{coeffs} ∈ {cst}")
|
| 207 |
+
else
|
| 208 |
+
"impossible"
|
| 209 |
+
|
| 210 |
+
open Lean in
|
| 211 |
+
/--
|
| 212 |
+
Takes a proof that `s.sat' x v` for some `s` such that `s.isImpossible`,
|
| 213 |
+
and constructs a proof of `False`.
|
| 214 |
+
-/
|
| 215 |
+
def proveFalse {s x} (j : Justification s x) (assumptions : Array Proof) : Proof := do
|
| 216 |
+
let v := ← atomsCoeffs
|
| 217 |
+
let prf ← j.proof v assumptions
|
| 218 |
+
let x := toExpr x
|
| 219 |
+
let s := toExpr s
|
| 220 |
+
let impossible ←
|
| 221 |
+
mkDecideProof (← mkEq (mkApp (.const ``Constraint.isImpossible []) s) (.const ``true []))
|
| 222 |
+
return mkApp5 (.const ``Constraint.not_sat'_of_isImpossible []) s impossible x v prf
|
| 223 |
+
|
| 224 |
+
/--
|
| 225 |
+
Insert a constraint into the problem,
|
| 226 |
+
without checking if there is already a constraint for these coefficients.
|
| 227 |
+
-/
|
| 228 |
+
def insertConstraint (p : Problem) : Fact → Problem
|
| 229 |
+
| f@⟨x, s, j⟩ =>
|
| 230 |
+
if s.isImpossible then
|
| 231 |
+
{ p with
|
| 232 |
+
possible := false
|
| 233 |
+
proveFalse? := some (proveFalse j p.assumptions)
|
| 234 |
+
explanation? := Thunk.mk fun _ => j.toString
|
| 235 |
+
proveFalse?_spec := rfl }
|
| 236 |
+
else
|
| 237 |
+
{ p with
|
| 238 |
+
numVars := max p.numVars x.length
|
| 239 |
+
constraints := p.constraints.insert x f
|
| 240 |
+
proveFalse?_spec := p.proveFalse?_spec
|
| 241 |
+
equalities :=
|
| 242 |
+
if f.constraint.isExact then
|
| 243 |
+
p.equalities.insert x
|
| 244 |
+
else
|
| 245 |
+
p.equalities }
|
| 246 |
+
|
| 247 |
+
/--
|
| 248 |
+
Add a constraint into the problem,
|
| 249 |
+
combining it with any existing constraints for the same coefficients.
|
| 250 |
+
-/
|
| 251 |
+
def addConstraint (p : Problem) : Fact → Problem
|
| 252 |
+
| f@⟨x, s, j⟩ =>
|
| 253 |
+
if p.possible then
|
| 254 |
+
match p.constraints[x]? with
|
| 255 |
+
| none =>
|
| 256 |
+
match s with
|
| 257 |
+
| .trivial => p
|
| 258 |
+
| _ => p.insertConstraint f
|
| 259 |
+
| some ⟨x', t, k⟩ =>
|
| 260 |
+
if h : x = x' then
|
| 261 |
+
let r := s.combine t
|
| 262 |
+
if r = t then
|
| 263 |
+
-- No need to overwrite the existing fact
|
| 264 |
+
-- with the same fact with a more complicated justification
|
| 265 |
+
p
|
| 266 |
+
else
|
| 267 |
+
if r = s then
|
| 268 |
+
-- The new constraint is strictly stronger, no need to combine with the old one:
|
| 269 |
+
p.insertConstraint ⟨x, s, j⟩
|
| 270 |
+
else
|
| 271 |
+
p.insertConstraint ⟨x, s.combine t, j.combine (h ▸ k)⟩
|
| 272 |
+
else
|
| 273 |
+
p -- unreachable
|
| 274 |
+
else
|
| 275 |
+
p
|
| 276 |
+
|
| 277 |
+
/--
|
| 278 |
+
Walk through the equalities, finding either the first equality with minimal coefficient `±1`,
|
| 279 |
+
or otherwise the equality with minimal `(r.minNatAbs, r.maxNatAbs)` (ordered lexicographically).
|
| 280 |
+
|
| 281 |
+
Returns the coefficients of the equality, along with the value of `minNatAbs`.
|
| 282 |
+
|
| 283 |
+
Although we don't need to run a termination proof here, it's nevertheless important that we use this
|
| 284 |
+
ordering so the algorithm terminates in practice!
|
| 285 |
+
-/
|
| 286 |
+
def selectEquality (p : Problem) : Option (Coeffs × Nat) :=
|
| 287 |
+
p.equalities.fold (init := none) fun
|
| 288 |
+
| none, c => (c, c.minNatAbs)
|
| 289 |
+
| some (r, m), c =>
|
| 290 |
+
if 2 ≤ m then
|
| 291 |
+
let m' := c.minNatAbs
|
| 292 |
+
if (m' < m || m' = m && c.maxNatAbs < r.maxNatAbs) then
|
| 293 |
+
(c, m')
|
| 294 |
+
else
|
| 295 |
+
(r, m)
|
| 296 |
+
else
|
| 297 |
+
(r, m)
|
| 298 |
+
|
| 299 |
+
/--
|
| 300 |
+
If we have already solved some equalities, apply those to some new `Fact`.
|
| 301 |
+
-/
|
| 302 |
+
def replayEliminations (p : Problem) (f : Fact) : Fact :=
|
| 303 |
+
p.eliminations.foldr (init := f) fun (f, i, s) g =>
|
| 304 |
+
match Coeffs.get g.coeffs i with
|
| 305 |
+
| 0 => g
|
| 306 |
+
| y => Fact.combo (-1 * s * y) f 1 g
|
| 307 |
+
|
| 308 |
+
/--
|
| 309 |
+
Solve an "easy" equality, i.e. one with a coefficient that is `±1`.
|
| 310 |
+
|
| 311 |
+
After solving, the variable will have been eliminated from all constraints.
|
| 312 |
+
-/
|
| 313 |
+
def solveEasyEquality (p : Problem) (c : Coeffs) : Problem :=
|
| 314 |
+
let i := c.findIdx? (·.natAbs = 1) |>.getD 0 -- findIdx? is always some
|
| 315 |
+
let sign := c.get i |> Int.sign
|
| 316 |
+
match p.constraints[c]? with
|
| 317 |
+
| some f =>
|
| 318 |
+
let init :=
|
| 319 |
+
{ assumptions := p.assumptions
|
| 320 |
+
eliminations := (f, i, sign) :: p.eliminations }
|
| 321 |
+
p.constraints.fold (init := init) fun p' coeffs g =>
|
| 322 |
+
match Coeffs.get coeffs i with
|
| 323 |
+
| 0 =>
|
| 324 |
+
p'.addConstraint g
|
| 325 |
+
| ci =>
|
| 326 |
+
let k := -1 * sign * ci
|
| 327 |
+
p'.addConstraint (Fact.combo k f 1 g).tidy
|
| 328 |
+
| _ => p -- unreachable
|
| 329 |
+
|
| 330 |
+
open Lean in
|
| 331 |
+
/--
|
| 332 |
+
We deal with a hard equality by introducing a new easy equality.
|
| 333 |
+
|
| 334 |
+
After solving the easy equality,
|
| 335 |
+
the minimum lexicographic value of `(c.minNatAbs, c.maxNatAbs)` will have been reduced.
|
| 336 |
+
-/
|
| 337 |
+
def dealWithHardEquality (p : Problem) (c : Coeffs) : OmegaM Problem :=
|
| 338 |
+
match p.constraints[c]? with
|
| 339 |
+
| some ⟨_, ⟨some r, some r'⟩, j⟩ => do
|
| 340 |
+
let m := c.minNatAbs + 1
|
| 341 |
+
-- We have to store the valid value of the newly introduced variable in the atoms.
|
| 342 |
+
let x := mkApp3 (.const ``bmod_div_term []) (toExpr m) (toExpr c) (← atomsCoeffs)
|
| 343 |
+
let (i, facts?) ← lookup x
|
| 344 |
+
if hr : r' = r then
|
| 345 |
+
match facts? with
|
| 346 |
+
| none => throwError "When solving hard equality, new atom had been seen before!"
|
| 347 |
+
| some facts => if ! facts.isEmpty then
|
| 348 |
+
throwError "When solving hard equality, there were unexpected new facts!"
|
| 349 |
+
return p.addConstraint { coeffs := _, constraint := _, justification := (hr ▸ j).bmod m r i }
|
| 350 |
+
else
|
| 351 |
+
throwError "Invalid constraint, expected an equation." -- unreachable
|
| 352 |
+
| _ =>
|
| 353 |
+
return p -- unreachable
|
| 354 |
+
|
| 355 |
+
/--
|
| 356 |
+
Solve an equality, by deciding whether it is easy (has a `±1` coefficient) or hard,
|
| 357 |
+
and delegating to the appropriate function.
|
| 358 |
+
-/
|
| 359 |
+
def solveEquality (p : Problem) (c : Coeffs) (m : Nat) : OmegaM Problem :=
|
| 360 |
+
if m = 1 then
|
| 361 |
+
return p.solveEasyEquality c
|
| 362 |
+
else
|
| 363 |
+
p.dealWithHardEquality c
|
| 364 |
+
|
| 365 |
+
/-- Recursively solve all equalities. -/
|
| 366 |
+
partial def solveEqualities (p : Problem) : OmegaM Problem :=
|
| 367 |
+
if p.possible then
|
| 368 |
+
match p.selectEquality with
|
| 369 |
+
| some (c, m) => do (← p.solveEquality c m).solveEqualities
|
| 370 |
+
| none => return p
|
| 371 |
+
else return p
|
| 372 |
+
|
| 373 |
+
open Constraint
|
| 374 |
+
|
| 375 |
+
open Lean in
|
| 376 |
+
/-- Constructing the proof term for `addInequality`. -/
|
| 377 |
+
def addInequality_proof (c : Int) (x : Coeffs) (p : Proof) : Proof := do
|
| 378 |
+
return mkApp4 (.const ``addInequality_sat []) (toExpr c) (toExpr x) (← atomsCoeffs) (← p)
|
| 379 |
+
|
| 380 |
+
open Lean in
|
| 381 |
+
/-- Constructing the proof term for `addEquality`. -/
|
| 382 |
+
def addEquality_proof (c : Int) (x : Coeffs) (p : Proof) : Proof := do
|
| 383 |
+
return mkApp4 (.const ``addEquality_sat []) (toExpr c) (toExpr x) (← atomsCoeffs) (← p)
|
| 384 |
+
|
| 385 |
+
/--
|
| 386 |
+
Helper function for adding an inequality of the form `const + Coeffs.dot coeffs atoms ≥ 0`
|
| 387 |
+
to a `Problem`.
|
| 388 |
+
|
| 389 |
+
(This is only used while initializing a `Problem`. During elimination we use `addConstraint`.)
|
| 390 |
+
-/
|
| 391 |
+
-- We are given `prf? : const + Coeffs.dot coeffs atoms ≥ 0`,
|
| 392 |
+
-- and need to transform this to `Coeffs.dot coeffs atoms ≥ -const`.
|
| 393 |
+
def addInequality (p : Problem) (const : Int) (coeffs : Coeffs) (prf? : Option Proof) : Problem :=
|
| 394 |
+
let prf := prf?.getD (do mkSorry (← mkFreshExprMVar none) false)
|
| 395 |
+
let i := p.assumptions.size
|
| 396 |
+
let p' := { p with assumptions := p.assumptions.push (addInequality_proof const coeffs prf) }
|
| 397 |
+
let f : Fact :=
|
| 398 |
+
{ coeffs
|
| 399 |
+
constraint := { lowerBound := some (-const), upperBound := none }
|
| 400 |
+
justification := .assumption _ _ i }
|
| 401 |
+
let f := p.replayEliminations f
|
| 402 |
+
let f := f.tidy
|
| 403 |
+
p'.addConstraint f
|
| 404 |
+
|
| 405 |
+
/--
|
| 406 |
+
Helper function for adding an equality of the form `const + Coeffs.dot coeffs atoms = 0`
|
| 407 |
+
to a `Problem`.
|
| 408 |
+
|
| 409 |
+
(This is only used while initializing a `Problem`. During elimination we use `addConstraint`.)
|
| 410 |
+
-/
|
| 411 |
+
def addEquality (p : Problem) (const : Int) (coeffs : Coeffs) (prf? : Option Proof) : Problem :=
|
| 412 |
+
let prf := prf?.getD (do mkSorry (← mkFreshExprMVar none) false)
|
| 413 |
+
let i := p.assumptions.size
|
| 414 |
+
let p' := { p with assumptions := p.assumptions.push (addEquality_proof const coeffs prf) }
|
| 415 |
+
let f : Fact :=
|
| 416 |
+
{ coeffs
|
| 417 |
+
constraint := { lowerBound := some (-const), upperBound := some (-const) }
|
| 418 |
+
justification := .assumption _ _ i }
|
| 419 |
+
let f := p.replayEliminations f
|
| 420 |
+
let f := f.tidy
|
| 421 |
+
p'.addConstraint f
|
| 422 |
+
|
| 423 |
+
/-- Folding `addInequality` over a list. -/
|
| 424 |
+
def addInequalities (p : Problem) (ineqs : List (Int × Coeffs × Option Proof)) : Problem :=
|
| 425 |
+
ineqs.foldl (init := p) fun p ⟨const, coeffs, prf?⟩ => p.addInequality const coeffs prf?
|
| 426 |
+
|
| 427 |
+
/-- Folding `addEquality` over a list. -/
|
| 428 |
+
def addEqualities (p : Problem) (eqs : List (Int × Coeffs × Option Proof)) : Problem :=
|
| 429 |
+
eqs.foldl (init := p) fun p ⟨const, coeffs, prf?⟩ => p.addEquality const coeffs prf?
|
| 430 |
+
|
| 431 |
+
/-- Representation of the data required to run Fourier-Motzkin elimination on a variable. -/
|
| 432 |
+
structure FourierMotzkinData where
|
| 433 |
+
/-- Which variable is being eliminated. -/
|
| 434 |
+
var : Nat
|
| 435 |
+
/-- The "irrelevant" facts which do not involve the target variable. -/
|
| 436 |
+
irrelevant : List Fact := []
|
| 437 |
+
/--
|
| 438 |
+
The facts which give a lower bound on the target variable,
|
| 439 |
+
and the coefficient of the target variable in each.
|
| 440 |
+
-/
|
| 441 |
+
lowerBounds : List (Fact × Int) := []
|
| 442 |
+
/--
|
| 443 |
+
The facts which give an upper bound on the target variable,
|
| 444 |
+
and the coefficient of the target variable in each.
|
| 445 |
+
-/
|
| 446 |
+
upperBounds : List (Fact × Int) := []
|
| 447 |
+
/--
|
| 448 |
+
Whether the elimination would be exact, because all of the lower bound coefficients are `±1`.
|
| 449 |
+
-/
|
| 450 |
+
lowerExact : Bool := true
|
| 451 |
+
/--
|
| 452 |
+
Whether the elimination would be exact, because all of the upper bound coefficients are `±1`.
|
| 453 |
+
-/
|
| 454 |
+
upperExact : Bool := true
|
| 455 |
+
deriving Inhabited
|
| 456 |
+
|
| 457 |
+
instance : ToString FourierMotzkinData where
|
| 458 |
+
toString d :=
|
| 459 |
+
let irrelevant := d.irrelevant.map fun ⟨x, s, _⟩ => s!"{x} ∈ {s}"
|
| 460 |
+
let lowerBounds := d.lowerBounds.map fun ⟨⟨x, s, _⟩, _⟩ => s!"{x} ∈ {s}"
|
| 461 |
+
let upperBounds := d.upperBounds.map fun ⟨⟨x, s, _⟩, _⟩ => s!"{x} ∈ {s}"
|
| 462 |
+
s!"Fourier-Motzkin elimination data for variable {d.var}\n"
|
| 463 |
+
++ s!"• irrelevant: {irrelevant}\n"
|
| 464 |
+
++ s!"• lowerBounds: {lowerBounds}\n"
|
| 465 |
+
++ s!"• upperBounds: {upperBounds}"
|
| 466 |
+
|
| 467 |
+
/-- Is a Fourier-Motzkin elimination empty (i.e. there are no relevant constraints). -/
|
| 468 |
+
def FourierMotzkinData.isEmpty (d : FourierMotzkinData) : Bool :=
|
| 469 |
+
d.lowerBounds.isEmpty && d.upperBounds.isEmpty
|
| 470 |
+
/-- The number of new constraints that would be introduced by Fourier-Motzkin elimination. -/
|
| 471 |
+
def FourierMotzkinData.size (d : FourierMotzkinData) : Nat :=
|
| 472 |
+
d.lowerBounds.length * d.upperBounds.length
|
| 473 |
+
/-- Is the Fourier-Motzkin elimination known to be exact? -/
|
| 474 |
+
def FourierMotzkinData.exact (d : FourierMotzkinData) : Bool := d.lowerExact || d.upperExact
|
| 475 |
+
|
| 476 |
+
/-- Prepare the Fourier-Motzkin elimination data for each variable. -/
|
| 477 |
+
-- TODO we could short-circuit here, if we find one with `size = 0`.
|
| 478 |
+
def fourierMotzkinData (p : Problem) : Array FourierMotzkinData := Id.run do
|
| 479 |
+
let n := p.numVars
|
| 480 |
+
let mut data : Array FourierMotzkinData :=
|
| 481 |
+
(List.range p.numVars).foldl (fun a i => a.push { var := i}) #[]
|
| 482 |
+
for (_, f@⟨xs, s, _⟩) in p.constraints do
|
| 483 |
+
for i in [0:n] do
|
| 484 |
+
let x := Coeffs.get xs i
|
| 485 |
+
data := data.modify i fun d =>
|
| 486 |
+
if x = 0 then
|
| 487 |
+
{ d with irrelevant := f :: d.irrelevant }
|
| 488 |
+
else Id.run do
|
| 489 |
+
let s' := s.scale x
|
| 490 |
+
let mut d' := d
|
| 491 |
+
if s'.lowerBound.isSome then
|
| 492 |
+
d' := { d' with
|
| 493 |
+
lowerBounds := (f, x) :: d'.lowerBounds
|
| 494 |
+
lowerExact := d'.lowerExact && x.natAbs = 1 }
|
| 495 |
+
if s'.upperBound.isSome then
|
| 496 |
+
d' := { d' with
|
| 497 |
+
upperBounds := (f, x) :: d'.upperBounds
|
| 498 |
+
upperExact := d'.upperExact && x.natAbs = 1 }
|
| 499 |
+
return d'
|
| 500 |
+
return data
|
| 501 |
+
|
| 502 |
+
/--
|
| 503 |
+
Decides which variable to run Fourier-Motzkin elimination on, and returns the associated data.
|
| 504 |
+
|
| 505 |
+
We prefer eliminations which introduce no new inequalities, or otherwise exact eliminations,
|
| 506 |
+
and break ties by the number of new inequalities introduced.
|
| 507 |
+
-/
|
| 508 |
+
def fourierMotzkinSelect (data : Array FourierMotzkinData) : MetaM FourierMotzkinData := do
|
| 509 |
+
let data := data.filter fun d => ¬ d.isEmpty
|
| 510 |
+
trace[omega] "Selecting variable to eliminate from (idx, size, exact) triples:\n\
|
| 511 |
+
{data.map fun d => (d.var, d.size, d.exact)}"
|
| 512 |
+
let mut bestIdx := 0
|
| 513 |
+
let mut bestSize := data[0]!.size
|
| 514 |
+
let mut bestExact := data[0]!.exact
|
| 515 |
+
if bestSize = 0 then
|
| 516 |
+
trace[omega] "Selected variable {data[0]!.var}."
|
| 517 |
+
return data[0]!
|
| 518 |
+
for h : i in [1:data.size] do
|
| 519 |
+
let exact := data[i].exact
|
| 520 |
+
let size := data[i].size
|
| 521 |
+
if size = 0 || !bestExact && exact || (bestExact == exact) && size < bestSize then
|
| 522 |
+
if size = 0 then
|
| 523 |
+
trace[omega] "Selected variable {data[i].var}"
|
| 524 |
+
return data[i]
|
| 525 |
+
bestIdx := i
|
| 526 |
+
bestExact := exact
|
| 527 |
+
bestSize := size
|
| 528 |
+
trace[omega] "Selected variable {data[bestIdx]!.var}."
|
| 529 |
+
return data[bestIdx]!
|
| 530 |
+
|
| 531 |
+
/--
|
| 532 |
+
Run Fourier-Motzkin elimination on one variable.
|
| 533 |
+
-/
|
| 534 |
+
-- This is only in MetaM to enable tracing.
|
| 535 |
+
def fourierMotzkin (p : Problem) : MetaM Problem := do
|
| 536 |
+
let data := p.fourierMotzkinData
|
| 537 |
+
-- Now perform the elimination.
|
| 538 |
+
let ⟨_, irrelevant, lower, upper, _, _⟩ ← fourierMotzkinSelect data
|
| 539 |
+
let mut r : Problem := { assumptions := p.assumptions, eliminations := p.eliminations }
|
| 540 |
+
for f in irrelevant do
|
| 541 |
+
r := r.insertConstraint f
|
| 542 |
+
for ⟨f, b⟩ in lower do
|
| 543 |
+
for ⟨g, a⟩ in upper do
|
| 544 |
+
r := r.addConstraint (Fact.combo a f (-b) g).tidy
|
| 545 |
+
return r
|
| 546 |
+
|
| 547 |
+
mutual
|
| 548 |
+
|
| 549 |
+
/--
|
| 550 |
+
Run the `omega` algorithm (for now without dark and grey shadows!) on a problem.
|
| 551 |
+
-/
|
| 552 |
+
partial def runOmega (p : Problem) : OmegaM Problem := do
|
| 553 |
+
trace[omega] "Running omega on:\n{p}"
|
| 554 |
+
if p.possible then
|
| 555 |
+
let p' ← p.solveEqualities
|
| 556 |
+
elimination p'
|
| 557 |
+
else
|
| 558 |
+
return p
|
| 559 |
+
|
| 560 |
+
/-- As for `runOmega`, but assuming the first round of solving equalities has already happened. -/
|
| 561 |
+
partial def elimination (p : Problem) : OmegaM Problem :=
|
| 562 |
+
if p.possible then
|
| 563 |
+
if p.isEmpty then
|
| 564 |
+
return p
|
| 565 |
+
else do
|
| 566 |
+
trace[omega] "Running Fourier-Motzkin elimination on:\n{p}"
|
| 567 |
+
runOmega (← p.fourierMotzkin)
|
| 568 |
+
else
|
| 569 |
+
return p
|
| 570 |
+
|
| 571 |
+
end
|
| 572 |
+
|
| 573 |
+
end Problem
|
| 574 |
+
|
| 575 |
+
end Lean.Elab.Tactic.Omega
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Tactic/Omega/Frontend.lean
ADDED
|
@@ -0,0 +1,712 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2023 Lean FRO, LLC. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Kim Morrison
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Elab.Tactic.Omega.Core
|
| 8 |
+
import Lean.Elab.Tactic.FalseOrByContra
|
| 9 |
+
import Lean.Elab.Tactic.Config
|
| 10 |
+
import Lean.Elab.MutualDef
|
| 11 |
+
import Lean.Meta.Closure
|
| 12 |
+
|
| 13 |
+
/-!
|
| 14 |
+
# Frontend to the `omega` tactic.
|
| 15 |
+
|
| 16 |
+
See `Lean.Elab.Tactic.Omega` for an overview of the tactic.
|
| 17 |
+
-/
|
| 18 |
+
|
| 19 |
+
open Lean Meta Omega
|
| 20 |
+
|
| 21 |
+
namespace Lean.Elab.Tactic.Omega
|
| 22 |
+
|
| 23 |
+
/--
|
| 24 |
+
Allow elaboration of `OmegaConfig` arguments to tactics.
|
| 25 |
+
-/
|
| 26 |
+
declare_config_elab elabOmegaConfig Lean.Meta.Omega.OmegaConfig
|
| 27 |
+
|
| 28 |
+
/-- Match on the two defeq expressions for successor: `n+1`, `n.succ`. -/
|
| 29 |
+
def succ? (e : Expr) : Option Expr :=
|
| 30 |
+
match e.getAppFnArgs with
|
| 31 |
+
| (``Nat.succ, #[n]) => some n
|
| 32 |
+
| (``HAdd.hAdd, #[_, _, _, _, a, b]) => do
|
| 33 |
+
if b == toExpr (1 : Nat) then some a else none
|
| 34 |
+
| _ => none
|
| 35 |
+
|
| 36 |
+
/--
|
| 37 |
+
A partially processed `omega` context.
|
| 38 |
+
|
| 39 |
+
We have:
|
| 40 |
+
* a `Problem` representing the integer linear constraints extracted so far, and their proofs
|
| 41 |
+
* the unprocessed `facts : List Expr` taken from the local context,
|
| 42 |
+
* the unprocessed `disjunctions : List Expr`,
|
| 43 |
+
which will only be split one at a time if we can't otherwise find a contradiction.
|
| 44 |
+
|
| 45 |
+
We begin with `facts := ← getLocalHyps` and `problem := .trivial`,
|
| 46 |
+
and progressively process the facts.
|
| 47 |
+
|
| 48 |
+
As we process the facts, we may generate additional facts
|
| 49 |
+
(e.g. about coercions and integer divisions).
|
| 50 |
+
To avoid duplicates, we maintain a `HashSet` of previously processed facts.
|
| 51 |
+
-/
|
| 52 |
+
structure MetaProblem where
|
| 53 |
+
/-- An integer linear arithmetic problem. -/
|
| 54 |
+
problem : Problem := {}
|
| 55 |
+
/-- Pending facts which have not been processed yet. -/
|
| 56 |
+
facts : List Expr := []
|
| 57 |
+
/--
|
| 58 |
+
Pending disjunctions, which we will case split one at a time if we can't get a contradiction.
|
| 59 |
+
-/
|
| 60 |
+
disjunctions : List Expr := []
|
| 61 |
+
/-- Facts which have already been processed; we keep these to avoid duplicates. -/
|
| 62 |
+
processedFacts : Std.HashSet Expr := ∅
|
| 63 |
+
|
| 64 |
+
/-- Construct the `rfl` proof that `lc.eval atoms = e`. -/
|
| 65 |
+
def mkEvalRflProof (e : Expr) (lc : LinearCombo) : OmegaM Expr := do
|
| 66 |
+
mkEqReflWithExpectedType e (mkApp2 (.const ``LinearCombo.eval []) (toExpr lc) (← atomsCoeffs))
|
| 67 |
+
|
| 68 |
+
/-- If `e : Expr` is the `n`-th atom, construct the proof that
|
| 69 |
+
`e = (coordinate n).eval atoms`. -/
|
| 70 |
+
def mkCoordinateEvalAtomsEq (e : Expr) (n : Nat) : OmegaM Expr := do
|
| 71 |
+
if n < 10 then
|
| 72 |
+
let atoms ← atoms
|
| 73 |
+
let tail ← mkListLit (.const ``Int []) atoms[n<...*].toArray.toList
|
| 74 |
+
let lem := .str ``LinearCombo s!"coordinate_eval_{n}"
|
| 75 |
+
mkEqSymm (mkAppN (.const lem []) (atoms[*...=n].toArray.push tail))
|
| 76 |
+
else
|
| 77 |
+
let atoms ← atomsCoeffs
|
| 78 |
+
let n := toExpr n
|
| 79 |
+
-- Construct the `rfl` proof that `e = (atoms.get? n).getD 0`
|
| 80 |
+
let eq ← mkEqReflWithExpectedType e (mkApp2 (.const ``Coeffs.get []) atoms n)
|
| 81 |
+
mkEqTrans eq (← mkEqSymm (mkApp2 (.const ``LinearCombo.coordinate_eval []) n atoms))
|
| 82 |
+
|
| 83 |
+
/-- Construct the linear combination (and its associated proof and new facts) for an atom. -/
|
| 84 |
+
def mkAtomLinearCombo (e : Expr) : OmegaM (LinearCombo × OmegaM Expr × Std.HashSet Expr) := do
|
| 85 |
+
let (n, facts) ← lookup e
|
| 86 |
+
return ⟨LinearCombo.coordinate n, mkCoordinateEvalAtomsEq e n, facts.getD ∅⟩
|
| 87 |
+
|
| 88 |
+
mutual
|
| 89 |
+
|
| 90 |
+
/--
|
| 91 |
+
Wrapper for `asLinearComboImpl`,
|
| 92 |
+
using a cache for previously visited expressions.
|
| 93 |
+
|
| 94 |
+
Gives a small (10%) speedup in testing.
|
| 95 |
+
I tried using a pointer based cache,
|
| 96 |
+
but there was never enough subexpression sharing to make it effective.
|
| 97 |
+
-/
|
| 98 |
+
partial def asLinearCombo (e : Expr) : OmegaM (LinearCombo × OmegaM Expr × Std.HashSet Expr) := do
|
| 99 |
+
let cache ← get
|
| 100 |
+
match cache.get? e with
|
| 101 |
+
| some (lc, prf) =>
|
| 102 |
+
trace[omega] "Found in cache: {e}"
|
| 103 |
+
return (lc, prf, ∅)
|
| 104 |
+
| none =>
|
| 105 |
+
let (lc, proof, r) ← asLinearComboImpl e
|
| 106 |
+
modifyThe Cache fun cache => (cache.insert e (lc, proof.run' cache))
|
| 107 |
+
pure (lc, proof, r)
|
| 108 |
+
|
| 109 |
+
/--
|
| 110 |
+
Translates an expression into a `LinearCombo`.
|
| 111 |
+
Also returns:
|
| 112 |
+
* a proof that this linear combo evaluated at the atoms is equal to the original expression
|
| 113 |
+
* a list of new facts which should be recorded:
|
| 114 |
+
* for each new atom `a` of the form `((x : Nat) : Int)`, the fact that `0 ≤ a`
|
| 115 |
+
* for each new atom `a` of the form `x / k`, for `k` a positive numeral, the facts that
|
| 116 |
+
`k * a ≤ x < (k + 1) * a`
|
| 117 |
+
* for each new atom of the form `((a - b : Nat) : Int)`, the fact:
|
| 118 |
+
`b ≤ a ∧ ((a - b : Nat) : Int) = a - b ∨ a < b ∧ ((a - b : Nat) : Int) = 0`
|
| 119 |
+
|
| 120 |
+
We also transform the expression as we descend into it:
|
| 121 |
+
* pushing coercions: `↑(x + y)`, `↑(x * y)`, `↑(x / k)`, `↑(x % k)`, `↑k`
|
| 122 |
+
* unfolding `emod`: `x % k` → `x - x / k`
|
| 123 |
+
-/
|
| 124 |
+
partial def asLinearComboImpl (e : Expr) : OmegaM (LinearCombo × OmegaM Expr × Std.HashSet Expr) := do
|
| 125 |
+
trace[omega] "processing {e}"
|
| 126 |
+
match groundInt? e with
|
| 127 |
+
| some i =>
|
| 128 |
+
let lc := {const := i}
|
| 129 |
+
return ⟨lc, mkEvalRflProof e lc, ∅⟩
|
| 130 |
+
| none =>
|
| 131 |
+
if e.isFVar then
|
| 132 |
+
if let some v ← e.fvarId!.getValue? then
|
| 133 |
+
rewrite e (← mkEqReflWithExpectedType e v)
|
| 134 |
+
else
|
| 135 |
+
mkAtomLinearCombo e
|
| 136 |
+
else match e.getAppFnArgs with
|
| 137 |
+
| (``HAdd.hAdd, #[_, _, _, _, e₁, e₂]) => do
|
| 138 |
+
let (l₁, prf₁, facts₁) ← asLinearCombo e₁
|
| 139 |
+
let (l₂, prf₂, facts₂) ← asLinearCombo e₂
|
| 140 |
+
let prf : OmegaM Expr := do
|
| 141 |
+
let add_eval :=
|
| 142 |
+
mkApp3 (.const ``LinearCombo.add_eval []) (toExpr l₁) (toExpr l₂) (← atomsCoeffs)
|
| 143 |
+
mkEqTrans
|
| 144 |
+
(← mkAppM ``Int.add_congr #[← prf₁, ← prf₂])
|
| 145 |
+
(← mkEqSymm add_eval)
|
| 146 |
+
pure (l₁ + l₂, prf, facts₁.union facts₂)
|
| 147 |
+
| (``HSub.hSub, #[_, _, _, _, e₁, e₂]) => do
|
| 148 |
+
let (l₁, prf₁, facts₁) ← asLinearCombo e₁
|
| 149 |
+
let (l₂, prf₂, facts₂) ← asLinearCombo e₂
|
| 150 |
+
let prf : OmegaM Expr := do
|
| 151 |
+
let sub_eval :=
|
| 152 |
+
mkApp3 (.const ``LinearCombo.sub_eval []) (toExpr l₁) (toExpr l₂) (← atomsCoeffs)
|
| 153 |
+
mkEqTrans
|
| 154 |
+
(← mkAppM ``Int.sub_congr #[← prf₁, ← prf₂])
|
| 155 |
+
(← mkEqSymm sub_eval)
|
| 156 |
+
pure (l₁ - l₂, prf, facts₁.union facts₂)
|
| 157 |
+
| (``Neg.neg, #[_, _, e']) => do
|
| 158 |
+
let (l, prf, facts) ← asLinearCombo e'
|
| 159 |
+
let prf' : OmegaM Expr := do
|
| 160 |
+
let neg_eval := mkApp2 (.const ``LinearCombo.neg_eval []) (toExpr l) (← atomsCoeffs)
|
| 161 |
+
mkEqTrans
|
| 162 |
+
(← mkAppM ``Int.neg_congr #[← prf])
|
| 163 |
+
(← mkEqSymm neg_eval)
|
| 164 |
+
pure (-l, prf', facts)
|
| 165 |
+
| (``HMul.hMul, #[_, _, _, _, x, y]) =>
|
| 166 |
+
-- If we decide not to expand out the multiplication,
|
| 167 |
+
-- we have to revert the `OmegaM` state so that any new facts about the factors
|
| 168 |
+
-- can still be reported when they are visited elsewhere.
|
| 169 |
+
let r? ← commitWhen do
|
| 170 |
+
let (xl, xprf, xfacts) ← asLinearCombo x
|
| 171 |
+
let (yl, yprf, yfacts) ← asLinearCombo y
|
| 172 |
+
if xl.coeffs.isZero ∨ yl.coeffs.isZero then
|
| 173 |
+
let prf : OmegaM Expr := do
|
| 174 |
+
let h ← mkDecideProof (mkApp2 (.const ``Or [])
|
| 175 |
+
(.app (.const ``Coeffs.isZero []) (toExpr xl.coeffs))
|
| 176 |
+
(.app (.const ``Coeffs.isZero []) (toExpr yl.coeffs)))
|
| 177 |
+
let mul_eval :=
|
| 178 |
+
mkApp4 (.const ``LinearCombo.mul_eval []) (toExpr xl) (toExpr yl) (← atomsCoeffs) h
|
| 179 |
+
mkEqTrans
|
| 180 |
+
(← mkAppM ``Int.mul_congr #[← xprf, ← yprf])
|
| 181 |
+
(← mkEqSymm mul_eval)
|
| 182 |
+
pure (some (LinearCombo.mul xl yl, prf, xfacts.union yfacts), true)
|
| 183 |
+
else
|
| 184 |
+
pure (none, false)
|
| 185 |
+
match r? with
|
| 186 |
+
| some r => pure r
|
| 187 |
+
| none => mkAtomLinearCombo e
|
| 188 |
+
| (``HMod.hMod, #[_, _, _, _, n, k]) =>
|
| 189 |
+
match groundNat? k with
|
| 190 |
+
| some k' => do
|
| 191 |
+
let k' := toExpr (k' : Int)
|
| 192 |
+
rewrite (← mkAppM ``HMod.hMod #[n, k']) (mkApp2 (.const ``Int.emod_def []) n k')
|
| 193 |
+
| none => mkAtomLinearCombo e
|
| 194 |
+
| (``HDiv.hDiv, #[_, _, _, _, x, z]) =>
|
| 195 |
+
match groundInt? z with
|
| 196 |
+
| some 0 => rewrite e (mkApp (.const ``Int.ediv_zero []) x)
|
| 197 |
+
| some i => do
|
| 198 |
+
let e' ← mkAppM ``HDiv.hDiv #[x, toExpr i]
|
| 199 |
+
if i < 0 then
|
| 200 |
+
rewrite e' (mkApp2 (.const ``Int.ediv_neg []) x (toExpr (-i)))
|
| 201 |
+
else
|
| 202 |
+
mkAtomLinearCombo e'
|
| 203 |
+
| _ => mkAtomLinearCombo e
|
| 204 |
+
| (``Min.min, #[_, _, a, b]) =>
|
| 205 |
+
if (← cfg).splitMinMax then
|
| 206 |
+
rewrite e (mkApp2 (.const ``Int.min_def []) a b)
|
| 207 |
+
else
|
| 208 |
+
mkAtomLinearCombo e
|
| 209 |
+
| (``Max.max, #[_, _, a, b]) =>
|
| 210 |
+
if (← cfg).splitMinMax then
|
| 211 |
+
rewrite e (mkApp2 (.const ``Int.max_def []) a b)
|
| 212 |
+
else
|
| 213 |
+
mkAtomLinearCombo e
|
| 214 |
+
| (``HPow.hPow, #[_, _, _, _, b, k]) =>
|
| 215 |
+
match succ? k with /- match for `e+1` and `e.succ` -/
|
| 216 |
+
| none => mkAtomLinearCombo e
|
| 217 |
+
| some k' =>
|
| 218 |
+
match groundInt? b with
|
| 219 |
+
| some _ => rewrite e (mkApp2 (.const ``Int.pow_succ []) b k')
|
| 220 |
+
| none => mkAtomLinearCombo e
|
| 221 |
+
| (``Nat.cast, #[α, i, n]) =>
|
| 222 |
+
match_expr α with
|
| 223 |
+
| Int => handleNatCast e i n
|
| 224 |
+
| _ => mkAtomLinearCombo e
|
| 225 |
+
| (``Prod.fst, #[α, β, p]) => match p with
|
| 226 |
+
| .app (.app (.app (.app (.const ``Prod.mk [u, v]) _) _) x) y =>
|
| 227 |
+
rewrite e (mkApp4 (.const ``Prod.fst_mk [u, v]) α x β y)
|
| 228 |
+
| _ => mkAtomLinearCombo e
|
| 229 |
+
| (``Prod.snd, #[α, β, p]) => match p with
|
| 230 |
+
| .app (.app (.app (.app (.const ``Prod.mk [u, v]) _) _) x) y =>
|
| 231 |
+
rewrite e (mkApp4 (.const ``Prod.snd_mk [u, v]) α x β y)
|
| 232 |
+
| _ => mkAtomLinearCombo e
|
| 233 |
+
| (``Int.negSucc, #[n]) => rewrite e (mkApp (.const ``Int.negSucc_eq []) n)
|
| 234 |
+
| _ => mkAtomLinearCombo e
|
| 235 |
+
where
|
| 236 |
+
/--
|
| 237 |
+
Apply a rewrite rule to an expression, and interpret the result as a `LinearCombo`.
|
| 238 |
+
(We're not rewriting any subexpressions here, just the top level, for efficiency.)
|
| 239 |
+
-/
|
| 240 |
+
rewrite (lhs rw : Expr) : OmegaM (LinearCombo × OmegaM Expr × Std.HashSet Expr) := do
|
| 241 |
+
trace[omega] "rewriting {lhs} via {rw} : {← inferType rw}"
|
| 242 |
+
match (← inferType rw).eq? with
|
| 243 |
+
| some (_, _lhs', rhs) =>
|
| 244 |
+
let (lc, prf, facts) ← asLinearCombo rhs
|
| 245 |
+
let prf' : OmegaM Expr := do mkEqTrans rw (← prf)
|
| 246 |
+
pure (lc, prf', facts)
|
| 247 |
+
| none => panic! "Invalid rewrite rule in 'asLinearCombo'"
|
| 248 |
+
handleNatCast (e i n : Expr) : OmegaM (LinearCombo × OmegaM Expr × Std.HashSet Expr) := do
|
| 249 |
+
match n with
|
| 250 |
+
| .fvar h =>
|
| 251 |
+
if let some v ← h.getValue? then
|
| 252 |
+
rewrite e (← mkEqReflWithExpectedType e
|
| 253 |
+
(mkApp3 (.const ``Nat.cast [0]) (.const ``Int []) i v))
|
| 254 |
+
else
|
| 255 |
+
mkAtomLinearCombo e
|
| 256 |
+
| _ => match n.getAppFnArgs with
|
| 257 |
+
| (``Nat.succ, #[n]) => rewrite e (.app (.const ``Int.ofNat_succ []) n)
|
| 258 |
+
| (``HAdd.hAdd, #[_, _, _, _, a, b]) => rewrite e (mkApp2 (.const ``Int.natCast_add []) a b)
|
| 259 |
+
| (``HMul.hMul, #[_, _, _, _, a, b]) =>
|
| 260 |
+
let (lc, prf, r) ← rewrite e (mkApp2 (.const ``Int.ofNat_mul []) a b)
|
| 261 |
+
-- Add the fact that the multiplication is non-negative.
|
| 262 |
+
pure (lc, prf, r.insert (mkApp2 (.const ``Int.ofNat_mul_nonneg []) a b))
|
| 263 |
+
| (``HDiv.hDiv, #[_, _, _, _, a, b]) => rewrite e (mkApp2 (.const ``Int.natCast_ediv []) a b)
|
| 264 |
+
| (``OfNat.ofNat, #[_, n, _]) => rewrite e (.app (.const ``Int.natCast_ofNat []) n)
|
| 265 |
+
| (``HMod.hMod, #[_, _, _, _, a, b]) => rewrite e (mkApp2 (.const ``Int.natCast_emod []) a b)
|
| 266 |
+
| (``HSub.hSub, #[_, _, _, _, mkApp6 (.const ``HSub.hSub _) _ _ _ _ a b, c]) =>
|
| 267 |
+
rewrite e (mkApp3 (.const ``Int.ofNat_sub_sub []) a b c)
|
| 268 |
+
| (``HPow.hPow, #[_, _, _, _, a, b]) =>
|
| 269 |
+
match groundNat? a with
|
| 270 |
+
| some _ => rewrite e (mkApp2 (.const ``Int.ofNat_pow []) a b)
|
| 271 |
+
| none => mkAtomLinearCombo e
|
| 272 |
+
| (``Prod.fst, #[_, β, p]) => match p with
|
| 273 |
+
| .app (.app (.app (.app (.const ``Prod.mk [0, v]) _) _) x) y =>
|
| 274 |
+
rewrite e (mkApp3 (.const ``Int.ofNat_fst_mk [v]) β x y)
|
| 275 |
+
| _ => mkAtomLinearCombo e
|
| 276 |
+
| (``Prod.snd, #[α, _, p]) => match p with
|
| 277 |
+
| .app (.app (.app (.app (.const ``Prod.mk [u, 0]) _) _) x) y =>
|
| 278 |
+
rewrite e (mkApp3 (.const ``Int.ofNat_snd_mk [u]) α x y)
|
| 279 |
+
| _ => mkAtomLinearCombo e
|
| 280 |
+
| (``Min.min, #[_, _, a, b]) => rewrite e (mkApp2 (.const ``Int.ofNat_min []) a b)
|
| 281 |
+
| (``Max.max, #[_, _, a, b]) => rewrite e (mkApp2 (.const ``Int.ofNat_max []) a b)
|
| 282 |
+
| (``Int.toNat, #[n]) => rewrite e (mkApp (.const ``Int.toNat_eq_max []) n)
|
| 283 |
+
| (``HShiftLeft.hShiftLeft, #[_, _, _, _, a, b]) =>
|
| 284 |
+
rewrite e (mkApp2 (.const ``Int.ofNat_shiftLeft_eq []) a b)
|
| 285 |
+
| (``HShiftRight.hShiftRight, #[_, _, _, _, a, b]) =>
|
| 286 |
+
rewrite e (mkApp2 (.const ``Int.ofNat_shiftRight_eq_div_pow []) a b)
|
| 287 |
+
| (``Int.natAbs, #[n]) =>
|
| 288 |
+
if (← cfg).splitNatAbs then
|
| 289 |
+
rewrite e (mkApp (.const ``Int.ofNat_natAbs []) n)
|
| 290 |
+
else
|
| 291 |
+
mkAtomLinearCombo e
|
| 292 |
+
| (``Fin.val, #[n, x]) =>
|
| 293 |
+
handleFinVal e i n x
|
| 294 |
+
| _ => mkAtomLinearCombo e
|
| 295 |
+
handleFinVal (e i n x : Expr) : OmegaM (LinearCombo × OmegaM Expr × Std.HashSet Expr) := do
|
| 296 |
+
match x with
|
| 297 |
+
| .fvar h =>
|
| 298 |
+
if let some v ← h.getValue? then
|
| 299 |
+
rewrite e (← mkEqReflWithExpectedType e
|
| 300 |
+
(mkApp3 (.const ``Nat.cast [0]) (.const ``Int []) i (mkApp2 (.const ``Fin.val []) n v)))
|
| 301 |
+
else
|
| 302 |
+
mkAtomLinearCombo e
|
| 303 |
+
| _ => match x.getAppFnArgs, n.nat? with
|
| 304 |
+
| (``HAdd.hAdd, #[_, _, _, _, a, b]), _ =>
|
| 305 |
+
rewrite e (mkApp3 (.const ``Fin.ofNat_val_add []) n a b)
|
| 306 |
+
| (``HMul.hMul, #[_, _, _, _, a, b]), _ =>
|
| 307 |
+
rewrite e (mkApp3 (.const ``Fin.ofNat_val_mul []) n a b)
|
| 308 |
+
| (``HSub.hSub, #[_, _, _, _, a, b]), some _ =>
|
| 309 |
+
-- Only do this rewrite if `n` is a numeral.
|
| 310 |
+
rewrite e (mkApp3 (.const ``Fin.ofNat_val_sub []) n a b)
|
| 311 |
+
| (``OfNat.ofNat, #[_, y, _]), some m =>
|
| 312 |
+
-- Only do this rewrite if `n` is a nonzero numeral.
|
| 313 |
+
if m = 0 then
|
| 314 |
+
mkAtomLinearCombo e
|
| 315 |
+
else
|
| 316 |
+
match y with
|
| 317 |
+
| .lit (.natVal y) =>
|
| 318 |
+
rewrite e (mkApp4 (.const ``Fin.ofNat_val_natCast [])
|
| 319 |
+
(toExpr (m - 1)) (toExpr y) (.lit (.natVal (y % m))) (← mkEqRefl (toExpr (y % m))))
|
| 320 |
+
| _ =>
|
| 321 |
+
-- This shouldn't happen, we obtained `y` from `OfNat.ofNat`
|
| 322 |
+
mkAtomLinearCombo e
|
| 323 |
+
| _, _ => mkAtomLinearCombo e
|
| 324 |
+
|
| 325 |
+
end
|
| 326 |
+
namespace MetaProblem
|
| 327 |
+
|
| 328 |
+
/-- The trivial `MetaProblem`, with no facts to process and a trivial `Problem`. -/
|
| 329 |
+
def trivial : MetaProblem where
|
| 330 |
+
problem := {}
|
| 331 |
+
|
| 332 |
+
instance : Inhabited MetaProblem := ⟨trivial⟩
|
| 333 |
+
|
| 334 |
+
/--
|
| 335 |
+
Add an integer equality to the `Problem`.
|
| 336 |
+
|
| 337 |
+
We solve equalities as they are discovered, as this often results in an earlier contradiction.
|
| 338 |
+
-/
|
| 339 |
+
def addIntEquality (p : MetaProblem) (h x : Expr) : OmegaM MetaProblem := do
|
| 340 |
+
let (lc, prf, facts) ← asLinearCombo x
|
| 341 |
+
let newFacts : Std.HashSet Expr := facts.fold (init := ∅) fun s e =>
|
| 342 |
+
if p.processedFacts.contains e then s else s.insert e
|
| 343 |
+
trace[omega] "Adding proof of {lc} = 0"
|
| 344 |
+
pure <|
|
| 345 |
+
{ p with
|
| 346 |
+
facts := newFacts.toList ++ p.facts
|
| 347 |
+
problem := ← (p.problem.addEquality lc.const lc.coeffs
|
| 348 |
+
(some do mkEqTrans (← mkEqSymm (← prf)) h)) |>.solveEqualities }
|
| 349 |
+
|
| 350 |
+
/--
|
| 351 |
+
Add an integer inequality to the `Problem`.
|
| 352 |
+
|
| 353 |
+
We solve equalities as they are discovered, as this often results in an earlier contradiction.
|
| 354 |
+
-/
|
| 355 |
+
def addIntInequality (p : MetaProblem) (h y : Expr) : OmegaM MetaProblem := do
|
| 356 |
+
let (lc, prf, facts) ← asLinearCombo y
|
| 357 |
+
let newFacts : Std.HashSet Expr := facts.fold (init := ∅) fun s e =>
|
| 358 |
+
if p.processedFacts.contains e then s else s.insert e
|
| 359 |
+
trace[omega] "Adding proof of {lc} ≥ 0"
|
| 360 |
+
pure <|
|
| 361 |
+
{ p with
|
| 362 |
+
facts := newFacts.toList ++ p.facts
|
| 363 |
+
problem := ← (p.problem.addInequality lc.const lc.coeffs
|
| 364 |
+
(some do mkAppM ``le_of_le_of_eq #[h, (← prf)])) |>.solveEqualities }
|
| 365 |
+
|
| 366 |
+
/-- Given a fact `h` with type `¬ P`, return a more useful fact obtained by pushing the negation. -/
|
| 367 |
+
def pushNot (h P : Expr) : MetaM (Option Expr) := do
|
| 368 |
+
let P ← whnfR P
|
| 369 |
+
trace[omega] "pushing negation: {P}"
|
| 370 |
+
match P with
|
| 371 |
+
| .forallE _ t b _ =>
|
| 372 |
+
if (← isProp t) && (← isProp b) then
|
| 373 |
+
return some (mkApp4 (.const ``Decidable.and_not_of_not_imp []) t b
|
| 374 |
+
(.app (.const ``Classical.propDecidable []) t) h)
|
| 375 |
+
else
|
| 376 |
+
return none
|
| 377 |
+
| .app _ _ =>
|
| 378 |
+
match_expr P with
|
| 379 |
+
| LT.lt α _ x y => match_expr α with
|
| 380 |
+
| Nat => return some (mkApp3 (.const ``Nat.le_of_not_lt []) x y h)
|
| 381 |
+
| Int => return some (mkApp3 (.const ``Int.le_of_not_lt []) x y h)
|
| 382 |
+
| Fin n => return some (mkApp4 (.const ``Fin.le_of_not_lt []) n x y h)
|
| 383 |
+
| _ => return none
|
| 384 |
+
| LE.le α _ x y => match_expr α with
|
| 385 |
+
| Nat => return some (mkApp3 (.const ``Nat.lt_of_not_le []) x y h)
|
| 386 |
+
| Int => return some (mkApp3 (.const ``Int.lt_of_not_le []) x y h)
|
| 387 |
+
| Fin n => return some (mkApp4 (.const ``Fin.lt_of_not_le []) n x y h)
|
| 388 |
+
| _ => return none
|
| 389 |
+
| Eq α x y => match_expr α with
|
| 390 |
+
| Nat => return some (mkApp3 (.const ``Nat.lt_or_gt_of_ne []) x y h)
|
| 391 |
+
| Int => return some (mkApp3 (.const ``Int.lt_or_gt_of_ne []) x y h)
|
| 392 |
+
| Fin n => return some (mkApp4 (.const ``Fin.lt_or_gt_of_ne []) n x y h)
|
| 393 |
+
| _ => return none
|
| 394 |
+
| Dvd.dvd α _ k x => match_expr α with
|
| 395 |
+
| Nat => return some (mkApp3 (.const ``Nat.emod_pos_of_not_dvd []) k x h)
|
| 396 |
+
| Int =>
|
| 397 |
+
-- This introduces a disjunction that could be avoided by checking `k ≠ 0`.
|
| 398 |
+
return some (mkApp3 (.const ``Int.emod_pos_of_not_dvd []) k x h)
|
| 399 |
+
| _ => return none
|
| 400 |
+
| Prod.Lex _ _ _ _ _ _ => return some (← mkAppM ``Prod.of_not_lex #[h])
|
| 401 |
+
| Not P =>
|
| 402 |
+
return some (mkApp3 (.const ``Decidable.of_not_not []) P
|
| 403 |
+
(.app (.const ``Classical.propDecidable []) P) h)
|
| 404 |
+
| And P Q =>
|
| 405 |
+
return some (mkApp5 (.const ``Decidable.or_not_not_of_not_and []) P Q
|
| 406 |
+
(.app (.const ``Classical.propDecidable []) P)
|
| 407 |
+
(.app (.const ``Classical.propDecidable []) Q) h)
|
| 408 |
+
| Or P Q =>
|
| 409 |
+
return some (mkApp3 (.const ``and_not_not_of_not_or []) P Q h)
|
| 410 |
+
| Iff P Q =>
|
| 411 |
+
return some (mkApp5 (.const ``Decidable.and_not_or_not_and_of_not_iff []) P Q
|
| 412 |
+
(.app (.const ``Classical.propDecidable []) P)
|
| 413 |
+
(.app (.const ``Classical.propDecidable []) Q) h)
|
| 414 |
+
| _ => return none
|
| 415 |
+
| _ => return none
|
| 416 |
+
|
| 417 |
+
/--
|
| 418 |
+
Parse an `Expr` and extract facts, also returning the number of new facts found.
|
| 419 |
+
-/
|
| 420 |
+
partial def addFact (p : MetaProblem) (h : Expr) : OmegaM (MetaProblem × Nat) := do
|
| 421 |
+
if ! p.problem.possible then
|
| 422 |
+
return (p, 0)
|
| 423 |
+
else
|
| 424 |
+
let t ← instantiateMVars (← whnfR (← inferType h))
|
| 425 |
+
trace[omega] "adding fact: {t}"
|
| 426 |
+
match t with
|
| 427 |
+
| .forallE _ x y _ =>
|
| 428 |
+
if ← pure t.isArrow <&&> isProp x <&&> isProp y then
|
| 429 |
+
p.addFact (mkApp4 (.const ``Decidable.not_or_of_imp []) x y
|
| 430 |
+
(.app (.const ``Classical.propDecidable []) x) h)
|
| 431 |
+
else
|
| 432 |
+
trace[omega] "rejecting forall: it's not an arrow, or not propositional"
|
| 433 |
+
return (p, 0)
|
| 434 |
+
| .app _ _ =>
|
| 435 |
+
match_expr t with
|
| 436 |
+
| Eq α x y =>
|
| 437 |
+
match_expr α with
|
| 438 |
+
| Int =>
|
| 439 |
+
match y.int? with
|
| 440 |
+
| some 0 => pure (← p.addIntEquality h x, 1)
|
| 441 |
+
| _ => p.addFact (mkApp3 (.const ``Int.sub_eq_zero_of_eq []) x y h)
|
| 442 |
+
| Nat => p.addFact (mkApp3 (.const ``Int.ofNat_congr []) x y h)
|
| 443 |
+
| Fin n => p.addFact (mkApp4 (.const ``Fin.val_congr []) n x y h)
|
| 444 |
+
| _ => pure (p, 0)
|
| 445 |
+
| LE.le α _ x y =>
|
| 446 |
+
match_expr α with
|
| 447 |
+
| Int =>
|
| 448 |
+
match x.int? with
|
| 449 |
+
| some 0 => pure (← p.addIntInequality h y, 1)
|
| 450 |
+
| _ => p.addFact (mkApp3 (.const ``Int.sub_nonneg_of_le []) y x h)
|
| 451 |
+
| Nat => p.addFact (mkApp3 (.const ``Int.ofNat_le_of_le []) x y h)
|
| 452 |
+
| Fin n => p.addFact (mkApp4 (.const ``Fin.val_le_of_le []) n x y h)
|
| 453 |
+
| _ => pure (p, 0)
|
| 454 |
+
| LT.lt α _ x y =>
|
| 455 |
+
match_expr α with
|
| 456 |
+
| Int => p.addFact (mkApp3 (.const ``Int.add_one_le_of_lt []) x y h)
|
| 457 |
+
| Nat => p.addFact (mkApp3 (.const ``Int.ofNat_lt_of_lt []) x y h)
|
| 458 |
+
| Fin n => p.addFact (mkApp4 (.const ``Fin.val_add_one_le_of_lt []) n x y h)
|
| 459 |
+
| _ => pure (p, 0)
|
| 460 |
+
| GT.gt α _ x y =>
|
| 461 |
+
match_expr α with
|
| 462 |
+
| Int => p.addFact (mkApp3 (.const ``Int.lt_of_gt []) x y h)
|
| 463 |
+
| Nat => p.addFact (mkApp3 (.const ``Nat.lt_of_gt []) x y h)
|
| 464 |
+
| Fin n => p.addFact (mkApp4 (.const ``Fin.val_add_one_le_of_gt []) n x y h)
|
| 465 |
+
| _ => pure (p, 0)
|
| 466 |
+
| GE.ge α _ x y =>
|
| 467 |
+
match_expr α with
|
| 468 |
+
| Int => p.addFact (mkApp3 (.const ``Int.le_of_ge []) x y h)
|
| 469 |
+
| Nat => p.addFact (mkApp3 (.const ``Nat.le_of_ge []) x y h)
|
| 470 |
+
| Fin n => p.addFact (mkApp4 (.const ``Fin.val_le_of_ge []) n x y h)
|
| 471 |
+
| _ => pure (p, 0)
|
| 472 |
+
| Ne α x y =>
|
| 473 |
+
match_expr α with
|
| 474 |
+
| Int => p.addFact (mkApp3 (.const ``Int.lt_or_gt_of_ne []) x y h)
|
| 475 |
+
| Nat => p.addFact (mkApp3 (.const ``Nat.lt_or_gt_of_ne []) x y h)
|
| 476 |
+
| _ => pure (p, 0)
|
| 477 |
+
| Dvd.dvd α _ k x =>
|
| 478 |
+
match_expr α with
|
| 479 |
+
| Int => p.addFact (mkApp3 (.const ``Int.emod_eq_zero_of_dvd []) k x h)
|
| 480 |
+
| Nat => p.addFact (mkApp3 (.const ``Nat.mod_eq_zero_of_dvd []) k x h)
|
| 481 |
+
| _ => pure (p, 0)
|
| 482 |
+
| Not P => match ← pushNot h P with
|
| 483 |
+
| none => return (p, 0)
|
| 484 |
+
| some h' => p.addFact h'
|
| 485 |
+
| Prod.Lex _ _ _ _ _ _ => p.addFact (← mkAppM ``Prod.of_lex #[h])
|
| 486 |
+
| And t₁ t₂ => do
|
| 487 |
+
let (p₁, n₁) ← p.addFact (mkApp3 (.const ``And.left []) t₁ t₂ h)
|
| 488 |
+
let (p₂, n₂) ← p₁.addFact (mkApp3 (.const ``And.right []) t₁ t₂ h)
|
| 489 |
+
return (p₂, n₁ + n₂)
|
| 490 |
+
| Exists α P =>
|
| 491 |
+
p.addFact (mkApp3 (.const ``Exists.choose_spec [← getLevel α]) α P h)
|
| 492 |
+
| Subtype α P =>
|
| 493 |
+
p.addFact (mkApp3 (.const ``Subtype.property [← getLevel α]) α P h)
|
| 494 |
+
| Iff P₁ P₂ =>
|
| 495 |
+
p.addFact (mkApp4 (.const ``Decidable.and_or_not_and_not_of_iff [])
|
| 496 |
+
P₁ P₂ (.app (.const ``Classical.propDecidable []) P₂) h)
|
| 497 |
+
| Or _ _ =>
|
| 498 |
+
if (← cfg).splitDisjunctions then
|
| 499 |
+
return ({ p with disjunctions := p.disjunctions.insert h }, 1)
|
| 500 |
+
else
|
| 501 |
+
return (p, 0)
|
| 502 |
+
| _ => pure (p, 0)
|
| 503 |
+
| _ => pure (p, 0)
|
| 504 |
+
|
| 505 |
+
/--
|
| 506 |
+
Process all the facts in a `MetaProblem`, returning the new problem, and the number of new facts.
|
| 507 |
+
|
| 508 |
+
This is partial because new facts may be generated along the way.
|
| 509 |
+
-/
|
| 510 |
+
partial def processFacts (p : MetaProblem) : OmegaM (MetaProblem × Nat) := do
|
| 511 |
+
match p.facts with
|
| 512 |
+
| [] => pure (p, 0)
|
| 513 |
+
| h :: t =>
|
| 514 |
+
if p.processedFacts.contains h then
|
| 515 |
+
processFacts { p with facts := t }
|
| 516 |
+
else
|
| 517 |
+
let (p₁, n₁) ← MetaProblem.addFact { p with
|
| 518 |
+
facts := t
|
| 519 |
+
processedFacts := p.processedFacts.insert h } h
|
| 520 |
+
let (p₂, n₂) ← p₁.processFacts
|
| 521 |
+
return (p₂, n₁ + n₂)
|
| 522 |
+
|
| 523 |
+
end MetaProblem
|
| 524 |
+
|
| 525 |
+
/--
|
| 526 |
+
Helpful error message when omega cannot find a solution
|
| 527 |
+
-/
|
| 528 |
+
def formatErrorMessage (p : Problem) : OmegaM MessageData := do
|
| 529 |
+
if p.possible then
|
| 530 |
+
if p.isEmpty then
|
| 531 |
+
return m!"No usable constraints found. You may need to unfold definitions so `omega` can see \
|
| 532 |
+
linear arithmetic facts about `Nat` and `Int`, which may also involve multiplication, \
|
| 533 |
+
division, and modular remainder by constants."
|
| 534 |
+
else
|
| 535 |
+
let as ← atoms
|
| 536 |
+
return .ofLazyM (es := as) do
|
| 537 |
+
let mask ← mentioned as p.constraints
|
| 538 |
+
let names ← varNames mask
|
| 539 |
+
return m!"a possible counterexample may satisfy the constraints\n" ++
|
| 540 |
+
m!"{prettyConstraints names p.constraints}\nwhere\n{prettyAtoms names as mask}"
|
| 541 |
+
else
|
| 542 |
+
-- formatErrorMessage should not be used in this case
|
| 543 |
+
return "it is trivially solvable"
|
| 544 |
+
where
|
| 545 |
+
varNameOf (i : Nat) : String :=
|
| 546 |
+
let c : Char := .ofNat ('a'.toNat + (i % 26))
|
| 547 |
+
let suffix := if i < 26 then "" else s!"_{i / 26}"
|
| 548 |
+
s!"{c}{suffix}"
|
| 549 |
+
|
| 550 |
+
inScope (s : String) : MetaM Bool := do
|
| 551 |
+
let n := .mkSimple s
|
| 552 |
+
if (← resolveGlobalName n).isEmpty then
|
| 553 |
+
if ((← getLCtx).findFromUserName? n).isNone then
|
| 554 |
+
return false
|
| 555 |
+
return true
|
| 556 |
+
|
| 557 |
+
-- Assign ascending names a, b, c, …, z, a1 … to all atoms mentioned according to the mask
|
| 558 |
+
-- but avoid names in the local or global scope
|
| 559 |
+
varNames (mask : Array Bool) : MetaM (Array String) := do
|
| 560 |
+
let mut names := #[]
|
| 561 |
+
let mut next := 0
|
| 562 |
+
for h : i in [:mask.size] do
|
| 563 |
+
if mask[i] then
|
| 564 |
+
while ← inScope (varNameOf next) do next := next + 1
|
| 565 |
+
names := names.push (varNameOf next)
|
| 566 |
+
next := next + 1
|
| 567 |
+
else
|
| 568 |
+
names := names.push "(masked)"
|
| 569 |
+
return names
|
| 570 |
+
|
| 571 |
+
-- We sort the constraints; otherwise the order is dependent on details of the hashing
|
| 572 |
+
-- and this can cause test suite output churn
|
| 573 |
+
prettyConstraints (names : Array String) (constraints : Std.HashMap Coeffs Fact) : String :=
|
| 574 |
+
constraints.toList
|
| 575 |
+
|>.toArray
|
| 576 |
+
|>.qsort (·.1 < ·.1)
|
| 577 |
+
|>.map (fun ⟨coeffs, ⟨_, cst, _⟩⟩ => " " ++ prettyConstraint (prettyCoeffs names coeffs) cst)
|
| 578 |
+
|>.toList
|
| 579 |
+
|> "\n".intercalate
|
| 580 |
+
|
| 581 |
+
prettyConstraint (e : String) : Constraint → String
|
| 582 |
+
| ⟨none, none⟩ => s!"{e} is unconstrained" -- should not happen in error messages
|
| 583 |
+
| ⟨none, some y⟩ => s!"{e} ≤ {y}"
|
| 584 |
+
| ⟨some x, none⟩ => s!"{e} ≥ {x}"
|
| 585 |
+
| ⟨some x, some y⟩ =>
|
| 586 |
+
if y < x then "∅" else -- should not happen in error messages
|
| 587 |
+
s!"{x} ≤ {e} ≤ {y}"
|
| 588 |
+
|
| 589 |
+
prettyCoeffs (names : Array String) (coeffs : Coeffs) : String :=
|
| 590 |
+
coeffs.toList.zipIdx
|
| 591 |
+
|>.filter (fun (c,_) => c ≠ 0)
|
| 592 |
+
|>.mapIdx (fun j (c,i) =>
|
| 593 |
+
(if j > 0 then if c > 0 then " + " else " - " else if c > 0 then "" else "- ") ++
|
| 594 |
+
(if Int.natAbs c = 1 then names[i]! else s!"{c.natAbs}*{names[i]!}"))
|
| 595 |
+
|> String.join
|
| 596 |
+
|
| 597 |
+
mentioned (atoms : Array Expr) (constraints : Std.HashMap Coeffs Fact) : MetaM (Array Bool) := do
|
| 598 |
+
let initMask := .replicate atoms.size false
|
| 599 |
+
return constraints.fold (init := initMask) fun mask coeffs _ =>
|
| 600 |
+
coeffs.zipIdx.foldl (init := mask) fun mask (c, i) =>
|
| 601 |
+
if c = 0 then mask else mask.set! i true
|
| 602 |
+
|
| 603 |
+
prettyAtoms (names : Array String) (atoms : Array Expr) (mask : Array Bool) : MessageData :=
|
| 604 |
+
(Array.zip names atoms).toList.zipIdx
|
| 605 |
+
|>.filter (fun (_, i) => mask.getD i false)
|
| 606 |
+
|>.map (fun ((n, a),_) => m!" {n} := {a}")
|
| 607 |
+
|> m!"\n".joinSep
|
| 608 |
+
|
| 609 |
+
mutual
|
| 610 |
+
|
| 611 |
+
/--
|
| 612 |
+
Split a disjunction in a `MetaProblem`, and if we find a new usable fact
|
| 613 |
+
call `omegaImpl` in both branches.
|
| 614 |
+
-/
|
| 615 |
+
partial def splitDisjunction (m : MetaProblem) : OmegaM Expr := do
|
| 616 |
+
match m.disjunctions with
|
| 617 |
+
| [] => throwError "omega could not prove the goal:\n{← formatErrorMessage m.problem}"
|
| 618 |
+
| h :: t => do
|
| 619 |
+
let hType ← whnfD (← inferType h)
|
| 620 |
+
trace[omega] "Case splitting on {hType}"
|
| 621 |
+
let_expr Or hType₁ hType₂ := hType | throwError "Unexpected disjunction {hType}"
|
| 622 |
+
let p?₁ ← withoutModifyingState do withLocalDeclD `h₁ hType₁ fun h₁ => do
|
| 623 |
+
withTraceNode `omega (msg := fun _ => do pure m!"Assuming fact:{indentExpr hType₁}") do
|
| 624 |
+
let m₁ := { m with facts := [h₁], disjunctions := t }
|
| 625 |
+
let (m₁, n) ← m₁.processFacts
|
| 626 |
+
if 0 < n then
|
| 627 |
+
let p₁ ← omegaImpl m₁
|
| 628 |
+
let p₁ ← mkLambdaFVars #[h₁] p₁
|
| 629 |
+
return some p₁
|
| 630 |
+
else
|
| 631 |
+
return none
|
| 632 |
+
if let some p₁ := p?₁ then
|
| 633 |
+
withLocalDeclD `h₂ hType₂ fun h₂ => do
|
| 634 |
+
withTraceNode `omega (msg := fun _ => do pure m!"Assuming fact:{indentExpr hType₂}") do
|
| 635 |
+
let m₂ := { m with facts := [h₂], disjunctions := t }
|
| 636 |
+
let p₂ ← omegaImpl m₂
|
| 637 |
+
let p₂ ← mkLambdaFVars #[h₂] p₂
|
| 638 |
+
return mkApp6 (mkConst ``Or.elim) hType₁ hType₂ (mkConst ``False) h p₁ p₂
|
| 639 |
+
else
|
| 640 |
+
trace[omega] "No new facts found."
|
| 641 |
+
splitDisjunction { m with disjunctions := t }
|
| 642 |
+
|
| 643 |
+
/-- Implementation of the `omega` algorithm, and handling disjunctions. -/
|
| 644 |
+
partial def omegaImpl (m : MetaProblem) : OmegaM Expr := do
|
| 645 |
+
let (m, _) ← m.processFacts
|
| 646 |
+
guard m.facts.isEmpty
|
| 647 |
+
let p := m.problem
|
| 648 |
+
trace[omega] "Extracted linear arithmetic problem:\nAtoms: {← atomsList}\n{p}"
|
| 649 |
+
let p' ← if p.possible then p.elimination else pure p
|
| 650 |
+
trace[omega] "After elimination:\nAtoms: {← atomsList}\n{p'}"
|
| 651 |
+
match p'.possible, p'.proveFalse?, p'.proveFalse?_spec with
|
| 652 |
+
| true, _, _ =>
|
| 653 |
+
splitDisjunction m
|
| 654 |
+
| false, .some prf, _ =>
|
| 655 |
+
trace[omega] "Justification:\n{p'.explanation?.get}"
|
| 656 |
+
let prf ← instantiateMVars (← prf)
|
| 657 |
+
trace[omega] "omega found a contradiction, proving {← inferType prf}"
|
| 658 |
+
return prf
|
| 659 |
+
|
| 660 |
+
end
|
| 661 |
+
|
| 662 |
+
/--
|
| 663 |
+
Given a collection of facts, try prove `False` using the omega algorithm,
|
| 664 |
+
and close the goal using that.
|
| 665 |
+
-/
|
| 666 |
+
def omega (facts : List Expr) (g : MVarId) (cfg : OmegaConfig := {}) : MetaM Unit :=
|
| 667 |
+
g.withContext do
|
| 668 |
+
let prf ← OmegaM.run (omegaImpl { facts }) cfg
|
| 669 |
+
g.assign prf
|
| 670 |
+
|
| 671 |
+
open Lean Elab Tactic Parser.Tactic
|
| 672 |
+
|
| 673 |
+
/-- The `omega` tactic, for resolving integer and natural linear arithmetic problems. -/
|
| 674 |
+
def omegaTactic (cfg : OmegaConfig) : TacticM Unit := do
|
| 675 |
+
liftMetaFinishingTactic fun g => do
|
| 676 |
+
if debug.terminalTacticsAsSorry.get (← getOptions) then
|
| 677 |
+
g.admit
|
| 678 |
+
else
|
| 679 |
+
let some g ← g.falseOrByContra | return ()
|
| 680 |
+
g.withContext do
|
| 681 |
+
let type ← g.getType
|
| 682 |
+
let g' ← mkFreshExprSyntheticOpaqueMVar type
|
| 683 |
+
let hyps := (← getLocalHyps).toList
|
| 684 |
+
trace[omega] "analyzing {hyps.length} hypotheses:\n{← hyps.mapM inferType}"
|
| 685 |
+
omega hyps g'.mvarId! cfg
|
| 686 |
+
-- Omega proofs are typically rather large, so hide them in a separate definition
|
| 687 |
+
let e ← mkAuxTheorem type (← instantiateMVarsProfiling g') (zetaDelta := true)
|
| 688 |
+
g.assign e
|
| 689 |
+
|
| 690 |
+
|
| 691 |
+
/-- The `omega` tactic, for resolving integer and natural linear arithmetic problems. This
|
| 692 |
+
`TacticM Unit` frontend with default configuration can be used as an Aesop rule, for example via
|
| 693 |
+
the tactic call `aesop (add 50% tactic Lean.Elab.Tactic.Omega.omegaDefault)`. -/
|
| 694 |
+
def omegaDefault : TacticM Unit := omegaTactic {}
|
| 695 |
+
|
| 696 |
+
@[builtin_tactic Lean.Parser.Tactic.omega]
|
| 697 |
+
def evalOmega : Tactic
|
| 698 |
+
| `(tactic| omega%$tk $cfg:optConfig) => do
|
| 699 |
+
-- Call `assumption` first, to avoid constructing unnecessary proofs.
|
| 700 |
+
withReducibleAndInstances (evalAssumption tk) <|> do
|
| 701 |
+
let cfg ← elabOmegaConfig cfg
|
| 702 |
+
omegaTactic cfg
|
| 703 |
+
| _ => throwUnsupportedSyntax
|
| 704 |
+
|
| 705 |
+
builtin_initialize bitvec_to_nat : SimpExtension ←
|
| 706 |
+
registerSimpAttr `bitvec_to_nat
|
| 707 |
+
"simp lemmas converting `BitVec` goals to `Nat` goals"
|
| 708 |
+
|
| 709 |
+
@[deprecated bitvec_to_nat (since := "2025-02-10")]
|
| 710 |
+
builtin_initialize bvOmegaSimpExtension : SimpExtension ←
|
| 711 |
+
registerSimpAttr `bv_toNat
|
| 712 |
+
"simp lemmas converting `BitVec` goals to `Nat` goals, for the `bv_omega` preprocessor"
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Std/Data/ExtTreeMap/Lemmas.lean
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Std/Data/ExtTreeSet/Basic.lean
ADDED
|
@@ -0,0 +1,523 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2025 Robin Arnez. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Robin Arnez, Markus Himmel, Paul Reichert
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Std.Data.ExtTreeMap.Basic
|
| 8 |
+
|
| 9 |
+
/-!
|
| 10 |
+
# Extensional tree sets
|
| 11 |
+
|
| 12 |
+
This file develops the type `Std.ExtTreeSet` of tree sets.
|
| 13 |
+
|
| 14 |
+
Lemmas about the operations on `Std.Data.ExtTreeSet` will be available in the
|
| 15 |
+
module `Std.Data.ExtTreeSet.Lemmas`.
|
| 16 |
+
|
| 17 |
+
See the module `Std.Data.TreeSet.Raw.Basic` for a variant of this type which is safe to use in
|
| 18 |
+
nested inductive types.
|
| 19 |
+
-/
|
| 20 |
+
|
| 21 |
+
set_option autoImplicit false
|
| 22 |
+
set_option linter.missingDocs true
|
| 23 |
+
|
| 24 |
+
universe u v w w₂
|
| 25 |
+
|
| 26 |
+
variable {α : Type u} {cmp : α → α → Ordering}
|
| 27 |
+
|
| 28 |
+
namespace Std
|
| 29 |
+
|
| 30 |
+
/--
|
| 31 |
+
Extensional tree sets.
|
| 32 |
+
|
| 33 |
+
A tree set stores elements of a certain type in a certain order. It depends on a comparator function
|
| 34 |
+
that defines an ordering on the keys and provides efficient order-dependent queries, such as
|
| 35 |
+
retrieval of the minimum or maximum.
|
| 36 |
+
|
| 37 |
+
To ensure that the operations behave as expected, the comparator function `cmp` should satisfy
|
| 38 |
+
certain laws that ensure a consistent ordering:
|
| 39 |
+
|
| 40 |
+
* If `a` is less than (or equal) to `b`, then `b` is greater than (or equal) to `a`
|
| 41 |
+
and vice versa (see the `OrientedCmp` typeclass).
|
| 42 |
+
* If `a` is less than or equal to `b` and `b` is, in turn, less than or equal to `c`, then `a`
|
| 43 |
+
is less than or equal to `c` (see the `TransCmp` typeclass).
|
| 44 |
+
|
| 45 |
+
Keys for which `cmp a b = Ordering.eq` are considered the same, i.e., there can be only one of them
|
| 46 |
+
be contained in a single tree set at the same time.
|
| 47 |
+
|
| 48 |
+
To avoid expensive copies, users should make sure that the tree set is used linearly.
|
| 49 |
+
|
| 50 |
+
Internally, the tree sets are represented as size-bounded trees, a type of self-balancing binary
|
| 51 |
+
search tree with efficient order statistic lookups.
|
| 52 |
+
|
| 53 |
+
In contrast to regular dependent tree maps, `Std.ExtTreeSet` offers several extensionality lemmas
|
| 54 |
+
and therefore has more lemmas about equality of tree sets. This doesn't affect the amount of
|
| 55 |
+
supported functions though: `Std.ExtTreeSet` supports all operations from `Std.TreeMap`.
|
| 56 |
+
|
| 57 |
+
In order to use most functions, a `TransCmp` instance is required. This is necessary to make sure
|
| 58 |
+
that the functions are congruent, i.e. equivalent tree sets as parameters produce equivalent return
|
| 59 |
+
values.
|
| 60 |
+
|
| 61 |
+
These tree sets contain a bundled well-formedness invariant, which means that they cannot
|
| 62 |
+
be used in nested inductive types. For these use cases, `Std.TreeSet.Raw` and
|
| 63 |
+
`Std.TreeSet.Raw.WF` unbundle the invariant from the tree set. When in doubt, prefer
|
| 64 |
+
`ExtTreeSet` over `TreeSet.Raw`.
|
| 65 |
+
-/
|
| 66 |
+
structure ExtTreeSet (α : Type u) (cmp : α → α → Ordering := by exact compare) where
|
| 67 |
+
/-- Internal implementation detail of the tree map. -/
|
| 68 |
+
inner : ExtTreeMap α Unit cmp
|
| 69 |
+
|
| 70 |
+
namespace ExtTreeSet
|
| 71 |
+
|
| 72 |
+
/--
|
| 73 |
+
Creates a new empty tree set. It is also possible and recommended to
|
| 74 |
+
use the empty collection notations `∅` and `{}` to create an empty tree set. `simp` replaces
|
| 75 |
+
`empty` with `∅`.
|
| 76 |
+
-/
|
| 77 |
+
@[inline]
|
| 78 |
+
def empty : ExtTreeSet α cmp :=
|
| 79 |
+
⟨ExtTreeMap.empty⟩
|
| 80 |
+
|
| 81 |
+
instance : EmptyCollection (ExtTreeSet α cmp) where
|
| 82 |
+
emptyCollection := empty
|
| 83 |
+
|
| 84 |
+
instance : Inhabited (ExtTreeSet α cmp) where
|
| 85 |
+
default := ∅
|
| 86 |
+
|
| 87 |
+
@[simp, grind =]
|
| 88 |
+
theorem empty_eq_emptyc : (empty : ExtTreeSet α cmp) = ∅ :=
|
| 89 |
+
rfl
|
| 90 |
+
|
| 91 |
+
/--
|
| 92 |
+
Inserts the given element into the set. If the tree set already contains an element that is
|
| 93 |
+
equal (with regard to `cmp`) to the given element, then the tree set is returned unchanged.
|
| 94 |
+
|
| 95 |
+
Note: this non-replacement behavior is true for `ExtTreeSet` and `ExtTreeSet.Raw`.
|
| 96 |
+
The `insert` function on `ExtTreeMap`, `ExtDTreeMap`, `ExtTreeMap.Raw` and `ExtDTreeMap.Raw` behaves
|
| 97 |
+
differently: it will overwrite an existing mapping.
|
| 98 |
+
-/
|
| 99 |
+
@[inline]
|
| 100 |
+
def insert [TransCmp cmp] (l : ExtTreeSet α cmp) (a : α) : ExtTreeSet α cmp :=
|
| 101 |
+
⟨l.inner.insertIfNew a ()⟩
|
| 102 |
+
|
| 103 |
+
instance [TransCmp cmp] : Singleton α (ExtTreeSet α cmp) where
|
| 104 |
+
singleton e := (∅ : ExtTreeSet α cmp).insert e
|
| 105 |
+
|
| 106 |
+
instance [TransCmp cmp] : Insert α (ExtTreeSet α cmp) where
|
| 107 |
+
insert e s := s.insert e
|
| 108 |
+
|
| 109 |
+
instance [TransCmp cmp] : LawfulSingleton α (ExtTreeSet α cmp) where
|
| 110 |
+
insert_empty_eq _ := rfl
|
| 111 |
+
|
| 112 |
+
/--
|
| 113 |
+
Checks whether an element is present in a set and inserts the element if it was not found.
|
| 114 |
+
If the tree set already contains an element that is equal (with regard to `cmp` to the given
|
| 115 |
+
element, then the tree set is returned unchanged.
|
| 116 |
+
|
| 117 |
+
Equivalent to (but potentially faster than) calling `contains` followed by `insert`.
|
| 118 |
+
-/
|
| 119 |
+
@[inline]
|
| 120 |
+
def containsThenInsert [TransCmp cmp] (t : ExtTreeSet α cmp) (a : α) : Bool × ExtTreeSet α cmp :=
|
| 121 |
+
let p := t.inner.containsThenInsertIfNew a ()
|
| 122 |
+
(p.1, ⟨p.2⟩)
|
| 123 |
+
|
| 124 |
+
/--
|
| 125 |
+
Returns `true` if `a`, or an element equal to `a` according to the comparator `cmp`, is contained
|
| 126 |
+
in the set. There is also a `Prop`-valued version of this: `a ∈ t` is equivalent to
|
| 127 |
+
`t.contains a = true`.
|
| 128 |
+
|
| 129 |
+
Observe that this is different behavior than for lists: for lists, `∈` uses `=` and `contains` uses
|
| 130 |
+
`==` for equality checks, while for tree sets, both use the given comparator `cmp`.
|
| 131 |
+
-/
|
| 132 |
+
@[inline]
|
| 133 |
+
def contains [TransCmp cmp] (l : ExtTreeSet α cmp) (a : α) : Bool :=
|
| 134 |
+
l.inner.contains a
|
| 135 |
+
|
| 136 |
+
instance [TransCmp cmp] : Membership α (ExtTreeSet α cmp) where
|
| 137 |
+
mem m a := m.contains a
|
| 138 |
+
|
| 139 |
+
instance [TransCmp cmp] {m : ExtTreeSet α cmp} {a : α} : Decidable (a ∈ m) :=
|
| 140 |
+
inferInstanceAs <| Decidable (m.contains a)
|
| 141 |
+
|
| 142 |
+
/-- Returns the number of mappings present in the map. -/
|
| 143 |
+
@[inline]
|
| 144 |
+
def size (t : ExtTreeSet α cmp) : Nat :=
|
| 145 |
+
t.inner.size
|
| 146 |
+
|
| 147 |
+
/-- Returns `true` if the tree set contains no mappings. -/
|
| 148 |
+
@[inline]
|
| 149 |
+
def isEmpty (t : ExtTreeSet α cmp) : Bool :=
|
| 150 |
+
t.inner.isEmpty
|
| 151 |
+
|
| 152 |
+
/-- Removes the given key if it exists. -/
|
| 153 |
+
@[inline]
|
| 154 |
+
def erase [TransCmp cmp] (t : ExtTreeSet α cmp) (a : α) : ExtTreeSet α cmp :=
|
| 155 |
+
⟨t.inner.erase a⟩
|
| 156 |
+
|
| 157 |
+
/--
|
| 158 |
+
Checks if given key is contained and returns the key if it is, otherwise `none`.
|
| 159 |
+
The result in the `some` case is guaranteed to be pointer equal to the key in the map.
|
| 160 |
+
-/
|
| 161 |
+
@[inline]
|
| 162 |
+
def get? [TransCmp cmp] (t : ExtTreeSet α cmp) (a : α) : Option α :=
|
| 163 |
+
t.inner.getKey? a
|
| 164 |
+
|
| 165 |
+
/--
|
| 166 |
+
Retrieves the key from the set that matches `a`. Ensures that such a key exists by requiring a proof
|
| 167 |
+
of `a ∈ m`. The result is guaranteed to be pointer equal to the key in the set.
|
| 168 |
+
-/
|
| 169 |
+
@[inline]
|
| 170 |
+
def get [TransCmp cmp] (t : ExtTreeSet α cmp) (a : α) (h : a ∈ t) : α :=
|
| 171 |
+
t.inner.getKey a h
|
| 172 |
+
|
| 173 |
+
/--
|
| 174 |
+
Checks if given key is contained and returns the key if it is, otherwise panics.
|
| 175 |
+
If no panic occurs the result is guaranteed to be pointer equal to the key in the set.
|
| 176 |
+
-/
|
| 177 |
+
@[inline]
|
| 178 |
+
def get! [TransCmp cmp] [Inhabited α] (t : ExtTreeSet α cmp) (a : α) : α :=
|
| 179 |
+
t.inner.getKey! a
|
| 180 |
+
|
| 181 |
+
/--
|
| 182 |
+
Checks if given key is contained and returns the key if it is, otherwise `fallback`.
|
| 183 |
+
If they key is contained the result is guaranteed to be pointer equal to the key in the set.
|
| 184 |
+
-/
|
| 185 |
+
@[inline]
|
| 186 |
+
def getD [TransCmp cmp] (t : ExtTreeSet α cmp) (a : α) (fallback : α) : α :=
|
| 187 |
+
t.inner.getKeyD a fallback
|
| 188 |
+
|
| 189 |
+
/--
|
| 190 |
+
Tries to retrieve the smallest element of the tree set, returning `none` if the set is empty.
|
| 191 |
+
-/
|
| 192 |
+
@[inline]
|
| 193 |
+
def min? [TransCmp cmp] (t : ExtTreeSet α cmp) : Option α :=
|
| 194 |
+
ExtTreeMap.minKey? t.inner
|
| 195 |
+
|
| 196 |
+
/--
|
| 197 |
+
Given a proof that the tree set is not empty, retrieves the smallest element.
|
| 198 |
+
-/
|
| 199 |
+
@[inline]
|
| 200 |
+
def min [TransCmp cmp] (t : ExtTreeSet α cmp) (h : t ≠ ∅) : α :=
|
| 201 |
+
ExtTreeMap.minKey t.inner (fun _ => nomatch t)
|
| 202 |
+
|
| 203 |
+
/--
|
| 204 |
+
Tries to retrieve the smallest element of the tree set, panicking if the set is empty.
|
| 205 |
+
-/
|
| 206 |
+
@[inline]
|
| 207 |
+
def min! [TransCmp cmp] [Inhabited α] (t : ExtTreeSet α cmp) : α :=
|
| 208 |
+
ExtTreeMap.minKey! t.inner
|
| 209 |
+
|
| 210 |
+
/--
|
| 211 |
+
Tries to retrieve the smallest element of the tree set, returning `fallback` if the tree set is empty.
|
| 212 |
+
-/
|
| 213 |
+
@[inline]
|
| 214 |
+
def minD [TransCmp cmp] (t : ExtTreeSet α cmp) (fallback : α) : α :=
|
| 215 |
+
ExtTreeMap.minKeyD t.inner fallback
|
| 216 |
+
|
| 217 |
+
/--
|
| 218 |
+
Tries to retrieve the largest element of the tree set, returning `none` if the set is empty.
|
| 219 |
+
-/
|
| 220 |
+
@[inline]
|
| 221 |
+
def max? [TransCmp cmp] (t : ExtTreeSet α cmp) : Option α :=
|
| 222 |
+
ExtTreeMap.maxKey? t.inner
|
| 223 |
+
|
| 224 |
+
/--
|
| 225 |
+
Given a proof that the tree set is not empty, retrieves the largest element.
|
| 226 |
+
-/
|
| 227 |
+
@[inline]
|
| 228 |
+
def max [TransCmp cmp] (t : ExtTreeSet α cmp) (h : t ≠ ∅) : α :=
|
| 229 |
+
ExtTreeMap.maxKey t.inner (fun _ => nomatch t)
|
| 230 |
+
|
| 231 |
+
/--
|
| 232 |
+
Tries to retrieve the largest element of the tree set, panicking if the set is empty.
|
| 233 |
+
-/
|
| 234 |
+
@[inline]
|
| 235 |
+
def max! [TransCmp cmp] [Inhabited α] (t : ExtTreeSet α cmp) : α :=
|
| 236 |
+
ExtTreeMap.maxKey! t.inner
|
| 237 |
+
|
| 238 |
+
/--
|
| 239 |
+
Tries to retrieve the largest element of the tree set, returning `fallback` if the tree set is empty.
|
| 240 |
+
-/
|
| 241 |
+
@[inline]
|
| 242 |
+
def maxD [TransCmp cmp] (t : ExtTreeSet α cmp) (fallback : α) : α :=
|
| 243 |
+
ExtTreeMap.maxKeyD t.inner fallback
|
| 244 |
+
|
| 245 |
+
/-- Returns the `n`-th smallest element, or `none` if `n` is at least `t.size`. -/
|
| 246 |
+
@[inline]
|
| 247 |
+
def atIdx? [TransCmp cmp] (t : ExtTreeSet α cmp) (n : Nat) : Option α :=
|
| 248 |
+
ExtTreeMap.keyAtIdx? t.inner n
|
| 249 |
+
|
| 250 |
+
/-- Returns the `n`-th smallest element. -/
|
| 251 |
+
@[inline]
|
| 252 |
+
def atIdx [TransCmp cmp] (t : ExtTreeSet α cmp) (n : Nat) (h : n < t.size) : α :=
|
| 253 |
+
ExtTreeMap.keyAtIdx t.inner n h
|
| 254 |
+
|
| 255 |
+
/-- Returns the `n`-th smallest element, or panics if `n` is at least `t.size`. -/
|
| 256 |
+
@[inline]
|
| 257 |
+
def atIdx! [TransCmp cmp] [Inhabited α] (t : ExtTreeSet α cmp) (n : Nat) : α :=
|
| 258 |
+
ExtTreeMap.keyAtIdx! t.inner n
|
| 259 |
+
|
| 260 |
+
/-- Returns the `n`-th smallest element, or `fallback` if `n` is at least `t.size`. -/
|
| 261 |
+
@[inline]
|
| 262 |
+
def atIdxD [TransCmp cmp] (t : ExtTreeSet α cmp) (n : Nat) (fallback : α) : α :=
|
| 263 |
+
ExtTreeMap.keyAtIdxD t.inner n fallback
|
| 264 |
+
|
| 265 |
+
/--
|
| 266 |
+
Tries to retrieve the smallest element that is greater than or equal to the
|
| 267 |
+
given element, returning `none` if no such element exists.
|
| 268 |
+
-/
|
| 269 |
+
@[inline]
|
| 270 |
+
def getGE? [TransCmp cmp] (t : ExtTreeSet α cmp) (k : α) : Option α :=
|
| 271 |
+
ExtTreeMap.getKeyGE? t.inner k
|
| 272 |
+
|
| 273 |
+
/--
|
| 274 |
+
Tries to retrieve the smallest element that is greater than the given element,
|
| 275 |
+
returning `none` if no such element exists.
|
| 276 |
+
-/
|
| 277 |
+
@[inline]
|
| 278 |
+
def getGT? [TransCmp cmp] (t : ExtTreeSet α cmp) (k : α) : Option α :=
|
| 279 |
+
ExtTreeMap.getKeyGT? t.inner k
|
| 280 |
+
|
| 281 |
+
/--
|
| 282 |
+
Tries to retrieve the largest element that is less than or equal to the
|
| 283 |
+
given element, returning `none` if no such element exists.
|
| 284 |
+
-/
|
| 285 |
+
@[inline]
|
| 286 |
+
def getLE? [TransCmp cmp] (t : ExtTreeSet α cmp) (k : α) : Option α :=
|
| 287 |
+
ExtTreeMap.getKeyLE? t.inner k
|
| 288 |
+
|
| 289 |
+
/--
|
| 290 |
+
Tries to retrieve the smallest element that is less than the given element,
|
| 291 |
+
returning `none` if no such element exists.
|
| 292 |
+
-/
|
| 293 |
+
@[inline]
|
| 294 |
+
def getLT? [TransCmp cmp] (t : ExtTreeSet α cmp) (k : α) : Option α :=
|
| 295 |
+
ExtTreeMap.getKeyLT? t.inner k
|
| 296 |
+
|
| 297 |
+
/--
|
| 298 |
+
Given a proof that such an element exists, retrieves the smallest element that is
|
| 299 |
+
greater than or equal to the given element.
|
| 300 |
+
-/
|
| 301 |
+
@[inline]
|
| 302 |
+
def getGE [TransCmp cmp] (t : ExtTreeSet α cmp) (k : α) (h : ∃ a ∈ t, (cmp a k).isGE) : α :=
|
| 303 |
+
ExtTreeMap.getKeyGE t.inner k h
|
| 304 |
+
|
| 305 |
+
/--
|
| 306 |
+
Given a proof that such an element exists, retrieves the smallest element that is
|
| 307 |
+
greater than the given element.
|
| 308 |
+
-/
|
| 309 |
+
@[inline]
|
| 310 |
+
def getGT [TransCmp cmp] (t : ExtTreeSet α cmp) (k : α) (h : ∃ a ∈ t, cmp a k = .gt) : α :=
|
| 311 |
+
ExtTreeMap.getKeyGT t.inner k h
|
| 312 |
+
|
| 313 |
+
/--
|
| 314 |
+
Given a proof that such an element exists, retrieves the largest element that is
|
| 315 |
+
less than or equal to the given element.
|
| 316 |
+
-/
|
| 317 |
+
@[inline]
|
| 318 |
+
def getLE [TransCmp cmp] (t : ExtTreeSet α cmp) (k : α) (h : ∃ a ∈ t, (cmp a k).isLE) : α :=
|
| 319 |
+
ExtTreeMap.getKeyLE t.inner k h
|
| 320 |
+
|
| 321 |
+
/--
|
| 322 |
+
Given a proof that such an element exists, retrieves the smallest element that is
|
| 323 |
+
less than the given element.
|
| 324 |
+
-/
|
| 325 |
+
@[inline]
|
| 326 |
+
def getLT [TransCmp cmp] (t : ExtTreeSet α cmp) (k : α) (h : ∃ a ∈ t, cmp a k = .lt) : α :=
|
| 327 |
+
ExtTreeMap.getKeyLT t.inner k h
|
| 328 |
+
|
| 329 |
+
/--
|
| 330 |
+
Tries to retrieve the smallest element that is greater than or equal to the
|
| 331 |
+
given element, panicking if no such element exists.
|
| 332 |
+
-/
|
| 333 |
+
@[inline]
|
| 334 |
+
def getGE! [TransCmp cmp] [Inhabited α] (t : ExtTreeSet α cmp) (k : α) : α :=
|
| 335 |
+
ExtTreeMap.getKeyGE! t.inner k
|
| 336 |
+
|
| 337 |
+
/--
|
| 338 |
+
Tries to retrieve the smallest element that is greater than the given element,
|
| 339 |
+
panicking if no such element exists.
|
| 340 |
+
-/
|
| 341 |
+
@[inline]
|
| 342 |
+
def getGT! [TransCmp cmp] [Inhabited α] (t : ExtTreeSet α cmp) (k : α) : α :=
|
| 343 |
+
ExtTreeMap.getKeyGT! t.inner k
|
| 344 |
+
|
| 345 |
+
/--
|
| 346 |
+
Tries to retrieve the largest element that is less than or equal to the
|
| 347 |
+
given element, panicking if no such element exists.
|
| 348 |
+
-/
|
| 349 |
+
@[inline]
|
| 350 |
+
def getLE! [TransCmp cmp] [Inhabited α] (t : ExtTreeSet α cmp) (k : α) : α :=
|
| 351 |
+
ExtTreeMap.getKeyLE! t.inner k
|
| 352 |
+
|
| 353 |
+
/--
|
| 354 |
+
Tries to retrieve the smallest element that is less than the given element,
|
| 355 |
+
panicking if no such element exists.
|
| 356 |
+
-/
|
| 357 |
+
@[inline]
|
| 358 |
+
def getLT! [TransCmp cmp] [Inhabited α] (t : ExtTreeSet α cmp) (k : α) : α :=
|
| 359 |
+
ExtTreeMap.getKeyLT! t.inner k
|
| 360 |
+
|
| 361 |
+
/--
|
| 362 |
+
Tries to retrieve the smallest element that is greater than or equal to the
|
| 363 |
+
given element, returning `fallback` if no such element exists.
|
| 364 |
+
-/
|
| 365 |
+
@[inline]
|
| 366 |
+
def getGED [TransCmp cmp] (t : ExtTreeSet α cmp) (k : α) (fallback : α) : α :=
|
| 367 |
+
ExtTreeMap.getKeyGED t.inner k fallback
|
| 368 |
+
|
| 369 |
+
/--
|
| 370 |
+
Tries to retrieve the smallest element that is greater than the given element,
|
| 371 |
+
returning `fallback` if no such element exists.
|
| 372 |
+
-/
|
| 373 |
+
@[inline]
|
| 374 |
+
def getGTD [TransCmp cmp] (t : ExtTreeSet α cmp) (k : α) (fallback : α) : α :=
|
| 375 |
+
ExtTreeMap.getKeyGTD t.inner k fallback
|
| 376 |
+
|
| 377 |
+
/--
|
| 378 |
+
Tries to retrieve the largest element that is less than or equal to the
|
| 379 |
+
given element, returning `fallback` if no such element exists.
|
| 380 |
+
-/
|
| 381 |
+
@[inline]
|
| 382 |
+
def getLED [TransCmp cmp] (t : ExtTreeSet α cmp) (k : α) (fallback : α) : α :=
|
| 383 |
+
ExtTreeMap.getKeyLED t.inner k fallback
|
| 384 |
+
|
| 385 |
+
/--
|
| 386 |
+
Tries to retrieve the smallest element that is less than the given element,
|
| 387 |
+
returning `fallback` if no such element exists.
|
| 388 |
+
-/
|
| 389 |
+
@[inline]
|
| 390 |
+
def getLTD [TransCmp cmp] (t : ExtTreeSet α cmp) (k : α) (fallback : α) : α :=
|
| 391 |
+
ExtTreeMap.getKeyLTD t.inner k fallback
|
| 392 |
+
|
| 393 |
+
variable {γ δ : Type w} {m : Type w → Type w₂} [Monad m] [LawfulMonad m]
|
| 394 |
+
|
| 395 |
+
/-- Removes all elements from the tree set for which the given function returns `false`. -/
|
| 396 |
+
@[inline]
|
| 397 |
+
def filter (f : α → Bool) (m : ExtTreeSet α cmp) : ExtTreeSet α cmp :=
|
| 398 |
+
⟨m.inner.filter fun a _ => f a⟩
|
| 399 |
+
|
| 400 |
+
/--
|
| 401 |
+
Monadically computes a value by folding the given function over the elements in the tree set in
|
| 402 |
+
ascending order.
|
| 403 |
+
-/
|
| 404 |
+
@[inline]
|
| 405 |
+
def foldlM [TransCmp cmp] (f : δ → (a : α) → m δ) (init : δ) (t : ExtTreeSet α cmp) : m δ :=
|
| 406 |
+
t.inner.foldlM (fun c a _ => f c a) init
|
| 407 |
+
|
| 408 |
+
/-- Folds the given function over the elements of the tree set in ascending order. -/
|
| 409 |
+
@[inline]
|
| 410 |
+
def foldl [TransCmp cmp] (f : δ → (a : α) → δ) (init : δ) (t : ExtTreeSet α cmp) : δ :=
|
| 411 |
+
t.inner.foldl (fun c a _ => f c a) init
|
| 412 |
+
|
| 413 |
+
/--
|
| 414 |
+
Monadically computes a value by folding the given function over the elements in the tree set in
|
| 415 |
+
descending order.
|
| 416 |
+
-/
|
| 417 |
+
@[inline]
|
| 418 |
+
def foldrM [TransCmp cmp] (f : (a : α) → δ → m δ) (init : δ) (t : ExtTreeSet α cmp) : m δ :=
|
| 419 |
+
t.inner.foldrM (fun a _ acc => f a acc) init
|
| 420 |
+
|
| 421 |
+
/-- Folds the given function over the elements of the tree set in descending order. -/
|
| 422 |
+
@[inline]
|
| 423 |
+
def foldr [TransCmp cmp] (f : (a : α) → δ → δ) (init : δ) (t : ExtTreeSet α cmp) : δ :=
|
| 424 |
+
t.inner.foldr (fun a _ acc => f a acc) init
|
| 425 |
+
|
| 426 |
+
/-- Partitions a tree set into two tree sets based on a predicate. -/
|
| 427 |
+
@[inline]
|
| 428 |
+
def partition [TransCmp cmp] (f : (a : α) → Bool) (t : ExtTreeSet α cmp) :
|
| 429 |
+
ExtTreeSet α cmp × ExtTreeSet α cmp :=
|
| 430 |
+
let p := t.inner.partition fun a _ => f a; (⟨p.1⟩, ⟨p.2⟩)
|
| 431 |
+
|
| 432 |
+
/-- Carries out a monadic action on each element in the tree set in ascending order. -/
|
| 433 |
+
@[inline]
|
| 434 |
+
def forM [TransCmp cmp] (f : α → m PUnit) (t : ExtTreeSet α cmp) : m PUnit :=
|
| 435 |
+
t.inner.forM (fun a _ => f a)
|
| 436 |
+
|
| 437 |
+
/--
|
| 438 |
+
Support for the `for` loop construct in `do` blocks. The iteration happens in ascending
|
| 439 |
+
order.
|
| 440 |
+
-/
|
| 441 |
+
@[inline]
|
| 442 |
+
def forIn [TransCmp cmp] (f : α → δ → m (ForInStep δ)) (init : δ) (t : ExtTreeSet α cmp) : m δ :=
|
| 443 |
+
t.inner.forIn (fun a _ c => f a c) init
|
| 444 |
+
|
| 445 |
+
/-
|
| 446 |
+
Note: We ignore the monad instance provided by `forM` / `forIn` and instead use the one from the
|
| 447 |
+
instance in order to get the `LawfulMonad m` assumption
|
| 448 |
+
-/
|
| 449 |
+
|
| 450 |
+
instance [TransCmp cmp] [inst : Monad m] [LawfulMonad m] : ForM m (ExtTreeSet α cmp) α where
|
| 451 |
+
forM t f := @forM _ _ _ inst _ _ f t
|
| 452 |
+
|
| 453 |
+
instance [TransCmp cmp] [inst : Monad m] [LawfulMonad m] : ForIn m (ExtTreeSet α cmp) α where
|
| 454 |
+
forIn m init f := @forIn _ _ _ _ inst _ _ f init m
|
| 455 |
+
|
| 456 |
+
/-- Check if all elements satisfy the predicate, short-circuiting if a predicate fails. -/
|
| 457 |
+
@[inline]
|
| 458 |
+
def any [TransCmp cmp] (t : ExtTreeSet α cmp) (p : α → Bool) : Bool :=
|
| 459 |
+
t.inner.any (fun a _ => p a)
|
| 460 |
+
|
| 461 |
+
/-- Check if any element satisfies the predicate, short-circuiting if a predicate succeeds. -/
|
| 462 |
+
@[inline]
|
| 463 |
+
def all [TransCmp cmp] (t : ExtTreeSet α cmp) (p : α → Bool) : Bool :=
|
| 464 |
+
t.inner.all (fun a _ => p a)
|
| 465 |
+
|
| 466 |
+
/-- Transforms the tree set into a list of elements in ascending order. -/
|
| 467 |
+
@[inline]
|
| 468 |
+
def toList [TransCmp cmp] (t : ExtTreeSet α cmp) : List α :=
|
| 469 |
+
t.inner.keys
|
| 470 |
+
|
| 471 |
+
/-- Transforms a list into a tree set. -/
|
| 472 |
+
def ofList (l : List α) (cmp : α → α → Ordering := by exact compare) : ExtTreeSet α cmp :=
|
| 473 |
+
⟨ExtTreeMap.unitOfList l cmp⟩
|
| 474 |
+
|
| 475 |
+
/-- Transforms the tree set into an array of elements in ascending order. -/
|
| 476 |
+
@[inline]
|
| 477 |
+
def toArray [TransCmp cmp] (t : ExtTreeSet α cmp) : Array α :=
|
| 478 |
+
t.inner.keysArray
|
| 479 |
+
|
| 480 |
+
/-- Transforms an array into a tree set. -/
|
| 481 |
+
def ofArray (a : Array α) (cmp : α → α → Ordering := by exact compare) : ExtTreeSet α cmp :=
|
| 482 |
+
⟨ExtTreeMap.unitOfArray a cmp⟩
|
| 483 |
+
|
| 484 |
+
/--
|
| 485 |
+
Returns a set that contains all mappings of `t₁` and `t₂.
|
| 486 |
+
|
| 487 |
+
This function ensures that `t₁` is used linearly.
|
| 488 |
+
Hence, as long as `t₁` is unshared, the performance characteristics follow the following imperative
|
| 489 |
+
description: Iterate over all mappings in `t₂`, inserting them into `t₁`.
|
| 490 |
+
|
| 491 |
+
Hence, the runtime of this method scales logarithmically in the size of `t₁` and linearly in the
|
| 492 |
+
size of `t₂` as long as `t₁` is unshared.
|
| 493 |
+
-/
|
| 494 |
+
@[inline]
|
| 495 |
+
def merge [TransCmp cmp] (t₁ t₂ : ExtTreeSet α cmp) : ExtTreeSet α cmp :=
|
| 496 |
+
⟨ExtTreeMap.mergeWith (fun _ _ _ => ()) t₁.inner t₂.inner⟩
|
| 497 |
+
|
| 498 |
+
/--
|
| 499 |
+
Inserts multiple elements into the tree set by iterating over the given collection and calling
|
| 500 |
+
`insert`. If the same element (with respect to `cmp`) appears multiple times, the first occurrence
|
| 501 |
+
takes precedence.
|
| 502 |
+
|
| 503 |
+
Note: this precedence behavior is true for `ExtTreeSet` and `ExtTreeSet.Raw`. The `insertMany` function on
|
| 504 |
+
`ExtTreeMap`, `ExtDTreeMap`, `ExtTreeMap.Raw` and `ExtDTreeMap.Raw` behaves differently: it will prefer the last
|
| 505 |
+
appearance.
|
| 506 |
+
-/
|
| 507 |
+
@[inline]
|
| 508 |
+
def insertMany [TransCmp cmp] {ρ} [ForIn Id ρ α] (t : ExtTreeSet α cmp) (l : ρ) : ExtTreeSet α cmp :=
|
| 509 |
+
⟨ExtTreeMap.insertManyIfNewUnit t.inner l⟩
|
| 510 |
+
|
| 511 |
+
/--
|
| 512 |
+
Erases multiple items from the tree set by iterating over the given collection and calling erase.
|
| 513 |
+
-/
|
| 514 |
+
@[inline]
|
| 515 |
+
def eraseMany [TransCmp cmp] {ρ} [ForIn Id ρ α] (t : ExtTreeSet α cmp) (l : ρ) : ExtTreeSet α cmp :=
|
| 516 |
+
⟨t.inner.eraseMany l⟩
|
| 517 |
+
|
| 518 |
+
instance [TransCmp cmp] [Repr α] : Repr (ExtTreeSet α cmp) where
|
| 519 |
+
reprPrec m prec := Repr.addAppParen ("Std.ExtTreeSet.ofList " ++ repr m.toList) prec
|
| 520 |
+
|
| 521 |
+
end ExtTreeSet
|
| 522 |
+
|
| 523 |
+
end Std
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Std/Data/ExtTreeSet/Lemmas.lean
ADDED
|
@@ -0,0 +1,1539 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2025 Robin Arnez. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Robin Arnez, Markus Himmel, Paul Reichert
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Std.Data.ExtTreeMap.Lemmas
|
| 8 |
+
import Std.Data.ExtTreeSet.Basic
|
| 9 |
+
|
| 10 |
+
/-!
|
| 11 |
+
# Tree set lemmas
|
| 12 |
+
|
| 13 |
+
This file contains lemmas about `Std.ExtTreeSet`.
|
| 14 |
+
-/
|
| 15 |
+
|
| 16 |
+
set_option linter.missingDocs true
|
| 17 |
+
set_option autoImplicit false
|
| 18 |
+
|
| 19 |
+
universe u v w w'
|
| 20 |
+
|
| 21 |
+
namespace Std.ExtTreeSet
|
| 22 |
+
|
| 23 |
+
variable {α : Type u} {cmp : α → α → Ordering} {t : ExtTreeSet α cmp}
|
| 24 |
+
|
| 25 |
+
private theorem ext {t t' : ExtTreeSet α cmp} : t.inner = t'.inner → t = t' := by
|
| 26 |
+
cases t; cases t'; rintro rfl; rfl
|
| 27 |
+
|
| 28 |
+
private theorem ext_iff {t t' : ExtTreeSet α cmp} : t = t' ↔ t.inner = t'.inner :=
|
| 29 |
+
⟨fun h => h ▸ rfl, ext⟩
|
| 30 |
+
|
| 31 |
+
@[simp]
|
| 32 |
+
theorem isEmpty_iff {t : ExtTreeSet α cmp} [TransCmp cmp] : t.isEmpty ↔ t = ∅ :=
|
| 33 |
+
ExtTreeMap.isEmpty_iff.trans ext_iff.symm
|
| 34 |
+
|
| 35 |
+
@[simp]
|
| 36 |
+
theorem isEmpty_eq_false_iff {t : ExtTreeSet α cmp} [TransCmp cmp] : t.isEmpty = false ↔ ¬t = ∅ :=
|
| 37 |
+
(Bool.not_eq_true _).symm.to_iff.trans (not_congr isEmpty_iff)
|
| 38 |
+
|
| 39 |
+
@[simp, grind =]
|
| 40 |
+
theorem isEmpty_empty : (∅ : ExtTreeSet α cmp).isEmpty :=
|
| 41 |
+
ExtTreeMap.isEmpty_empty
|
| 42 |
+
|
| 43 |
+
@[simp]
|
| 44 |
+
theorem empty_eq : ∅ = t ↔ t = ∅ := eq_comm
|
| 45 |
+
|
| 46 |
+
@[simp]
|
| 47 |
+
theorem insert_ne_empty [TransCmp cmp] {k : α} :
|
| 48 |
+
t.insert k ≠ ∅ :=
|
| 49 |
+
mt ext_iff.mp ExtTreeMap.insertIfNew_ne_empty
|
| 50 |
+
|
| 51 |
+
@[grind =]
|
| 52 |
+
theorem mem_iff_contains [TransCmp cmp] {k : α} : k ∈ t ↔ t.contains k :=
|
| 53 |
+
ExtTreeMap.mem_iff_contains
|
| 54 |
+
|
| 55 |
+
@[simp, grind _=_]
|
| 56 |
+
theorem contains_iff_mem [TransCmp cmp] {k : α} : t.contains k ↔ k ∈ t :=
|
| 57 |
+
ExtTreeMap.contains_iff_mem
|
| 58 |
+
|
| 59 |
+
theorem contains_congr [TransCmp cmp] {k k' : α} (hab : cmp k k' = .eq) :
|
| 60 |
+
t.contains k = t.contains k' :=
|
| 61 |
+
ExtTreeMap.contains_congr hab
|
| 62 |
+
|
| 63 |
+
theorem mem_congr [TransCmp cmp] {k k' : α} (hab : cmp k k' = .eq) : k ∈ t ↔ k' ∈ t :=
|
| 64 |
+
ExtTreeMap.mem_congr hab
|
| 65 |
+
|
| 66 |
+
@[simp, grind =]
|
| 67 |
+
theorem contains_empty [TransCmp cmp] {k : α} : (∅ : ExtTreeSet α cmp).contains k = false :=
|
| 68 |
+
rfl
|
| 69 |
+
|
| 70 |
+
@[simp]
|
| 71 |
+
theorem not_mem_empty [TransCmp cmp] {k : α} : k ∉ (∅ : ExtTreeSet α cmp) :=
|
| 72 |
+
Bool.false_ne_true
|
| 73 |
+
|
| 74 |
+
theorem eq_empty_iff_forall_contains [TransCmp cmp] : t = ∅ ↔ ∀ a, t.contains a = false :=
|
| 75 |
+
ext_iff.trans ExtTreeMap.eq_empty_iff_forall_contains
|
| 76 |
+
|
| 77 |
+
theorem eq_empty_iff_forall_not_mem [TransCmp cmp] : t = ∅ ↔ ∀ a, ¬a ∈ t :=
|
| 78 |
+
ext_iff.trans ExtTreeMap.eq_empty_iff_forall_not_mem
|
| 79 |
+
|
| 80 |
+
theorem ne_empty_of_mem [TransCmp cmp] {a : α} (h : a ∈ t) : t ≠ ∅ :=
|
| 81 |
+
(not_congr eq_empty_iff_forall_not_mem).mpr fun h' => h' a h
|
| 82 |
+
|
| 83 |
+
@[simp]
|
| 84 |
+
theorem insert_eq_insert [TransCmp cmp] {p : α} : Insert.insert p t = t.insert p :=
|
| 85 |
+
rfl
|
| 86 |
+
|
| 87 |
+
@[simp]
|
| 88 |
+
theorem singleton_eq_insert [TransCmp cmp] {p : α} :
|
| 89 |
+
Singleton.singleton p = (∅ : ExtTreeSet α cmp).insert p :=
|
| 90 |
+
rfl
|
| 91 |
+
|
| 92 |
+
@[simp, grind =]
|
| 93 |
+
theorem contains_insert [TransCmp cmp] {k a : α} :
|
| 94 |
+
(t.insert k).contains a = (cmp k a == .eq || t.contains a) :=
|
| 95 |
+
ExtTreeMap.contains_insertIfNew
|
| 96 |
+
|
| 97 |
+
@[simp, grind =]
|
| 98 |
+
theorem mem_insert [TransCmp cmp] {k a : α} :
|
| 99 |
+
a ∈ t.insert k ↔ cmp k a = .eq ∨ a ∈ t :=
|
| 100 |
+
ExtTreeMap.mem_insertIfNew
|
| 101 |
+
|
| 102 |
+
theorem contains_insert_self [TransCmp cmp] {k : α} :
|
| 103 |
+
(t.insert k).contains k :=
|
| 104 |
+
ExtTreeMap.contains_insertIfNew_self
|
| 105 |
+
|
| 106 |
+
theorem mem_insert_self [TransCmp cmp] {k : α} :
|
| 107 |
+
k ∈ t.insert k :=
|
| 108 |
+
ExtTreeMap.mem_insertIfNew_self
|
| 109 |
+
|
| 110 |
+
theorem contains_of_contains_insert [TransCmp cmp] {k a : α} :
|
| 111 |
+
(t.insert k).contains a → cmp k a ≠ .eq → t.contains a :=
|
| 112 |
+
ExtTreeMap.contains_of_contains_insertIfNew
|
| 113 |
+
|
| 114 |
+
theorem mem_of_mem_insert [TransCmp cmp] {k a : α} :
|
| 115 |
+
a ∈ t.insert k → cmp k a ≠ .eq → a ∈ t :=
|
| 116 |
+
ExtTreeMap.mem_of_mem_insertIfNew
|
| 117 |
+
|
| 118 |
+
/-- This is a restatement of `mem_of_mem_insert` that is written to exactly match the
|
| 119 |
+
proof obligation in the statement of `get_insert`. -/
|
| 120 |
+
theorem mem_of_mem_insert' [TransCmp cmp] {k a : α} :
|
| 121 |
+
a ∈ t.insert k → ¬ (cmp k a = .eq ∧ ¬ k ∈ t) → a ∈ t :=
|
| 122 |
+
ExtTreeMap.mem_of_mem_insertIfNew'
|
| 123 |
+
|
| 124 |
+
@[simp, grind =]
|
| 125 |
+
theorem size_empty : (∅ : ExtTreeSet α cmp).size = 0 :=
|
| 126 |
+
ExtTreeMap.size_empty
|
| 127 |
+
|
| 128 |
+
theorem isEmpty_eq_size_beq_zero : t.isEmpty = (t.size == 0) :=
|
| 129 |
+
ExtTreeMap.isEmpty_eq_size_beq_zero
|
| 130 |
+
|
| 131 |
+
theorem eq_empty_iff_size_eq_zero [TransCmp cmp] : t = ∅ ↔ t.size = 0 :=
|
| 132 |
+
ext_iff.trans ExtTreeMap.eq_empty_iff_size_eq_zero
|
| 133 |
+
|
| 134 |
+
@[grind =]
|
| 135 |
+
theorem size_insert [TransCmp cmp] {k : α} :
|
| 136 |
+
(t.insert k).size = if t.contains k then t.size else t.size + 1 :=
|
| 137 |
+
ExtTreeMap.size_insertIfNew
|
| 138 |
+
|
| 139 |
+
theorem size_le_size_insert [TransCmp cmp] {k : α} :
|
| 140 |
+
t.size ≤ (t.insert k).size :=
|
| 141 |
+
ExtTreeMap.size_le_size_insertIfNew
|
| 142 |
+
|
| 143 |
+
theorem size_insert_le [TransCmp cmp] {k : α} :
|
| 144 |
+
(t.insert k).size ≤ t.size + 1 :=
|
| 145 |
+
ExtTreeMap.size_insertIfNew_le
|
| 146 |
+
|
| 147 |
+
@[simp, grind =]
|
| 148 |
+
theorem erase_empty [TransCmp cmp] {k : α} : (∅ : ExtTreeSet α cmp).erase k = ∅ :=
|
| 149 |
+
ext ExtTreeMap.erase_empty
|
| 150 |
+
|
| 151 |
+
@[simp, grind =]
|
| 152 |
+
theorem erase_eq_empty_iff [TransCmp cmp] {k : α} :
|
| 153 |
+
t.erase k = ∅ ↔ t = ∅ ∨ (t.size = 1 ∧ k ∈ t) := by
|
| 154 |
+
simpa only [ext_iff] using ExtTreeMap.erase_eq_empty_iff
|
| 155 |
+
|
| 156 |
+
theorem eq_empty_iff_erase_eq_empty_and_not_mem [TransCmp cmp] (k : α) :
|
| 157 |
+
t = ∅ ↔ t.erase k = ∅ ∧ ¬k ∈ t := by
|
| 158 |
+
simpa only [ext_iff] using ExtTreeMap.eq_empty_iff_erase_eq_empty_and_not_mem k
|
| 159 |
+
|
| 160 |
+
theorem ne_empty_of_erase_ne_empty [TransCmp cmp] {k : α} (h : t.erase k ≠ ∅) : t ≠ ∅ := by
|
| 161 |
+
simp_all
|
| 162 |
+
|
| 163 |
+
@[simp, grind =]
|
| 164 |
+
theorem contains_erase [TransCmp cmp] {k a : α} :
|
| 165 |
+
(t.erase k).contains a = (cmp k a != .eq && t.contains a) :=
|
| 166 |
+
ExtTreeMap.contains_erase
|
| 167 |
+
|
| 168 |
+
@[simp, grind =]
|
| 169 |
+
theorem mem_erase [TransCmp cmp] {k a : α} :
|
| 170 |
+
a ∈ t.erase k ↔ cmp k a ≠ .eq ∧ a ∈ t :=
|
| 171 |
+
ExtTreeMap.mem_erase
|
| 172 |
+
|
| 173 |
+
theorem contains_of_contains_erase [TransCmp cmp] {k a : α} :
|
| 174 |
+
(t.erase k).contains a → t.contains a :=
|
| 175 |
+
ExtTreeMap.contains_of_contains_erase
|
| 176 |
+
|
| 177 |
+
theorem mem_of_mem_erase [TransCmp cmp] {k a : α} :
|
| 178 |
+
(t.erase k).contains a → t.contains a :=
|
| 179 |
+
ExtTreeMap.mem_of_mem_erase
|
| 180 |
+
|
| 181 |
+
@[grind =]
|
| 182 |
+
theorem size_erase [TransCmp cmp] {k : α} :
|
| 183 |
+
(t.erase k).size = if t.contains k then t.size - 1 else t.size :=
|
| 184 |
+
ExtTreeMap.size_erase
|
| 185 |
+
|
| 186 |
+
theorem size_erase_le [TransCmp cmp] {k : α} :
|
| 187 |
+
(t.erase k).size ≤ t.size :=
|
| 188 |
+
ExtTreeMap.size_erase_le
|
| 189 |
+
|
| 190 |
+
theorem size_le_size_erase [TransCmp cmp] {k : α} :
|
| 191 |
+
t.size ≤ (t.erase k).size + 1 :=
|
| 192 |
+
ExtTreeMap.size_le_size_erase
|
| 193 |
+
|
| 194 |
+
@[simp, grind =]
|
| 195 |
+
theorem get?_empty [TransCmp cmp] {a : α} : (∅ : ExtTreeSet α cmp).get? a = none :=
|
| 196 |
+
ExtTreeMap.getKey?_empty
|
| 197 |
+
|
| 198 |
+
@[grind =] theorem get?_insert [TransCmp cmp] {k a : α} :
|
| 199 |
+
(t.insert k).get? a = if cmp k a = .eq ∧ ¬k ∈ t then some k else t.get? a :=
|
| 200 |
+
ExtTreeMap.getKey?_insertIfNew
|
| 201 |
+
|
| 202 |
+
theorem contains_eq_isSome_get? [TransCmp cmp] {a : α} :
|
| 203 |
+
t.contains a = (t.get? a).isSome :=
|
| 204 |
+
ExtTreeMap.contains_eq_isSome_getKey?
|
| 205 |
+
|
| 206 |
+
@[simp, grind =]
|
| 207 |
+
theorem isSome_get?_eq_contains [TransCmp cmp] {a : α} :
|
| 208 |
+
(t.get? a).isSome = t.contains a :=
|
| 209 |
+
contains_eq_isSome_get?.symm
|
| 210 |
+
|
| 211 |
+
theorem mem_iff_isSome_get? [TransCmp cmp] {a : α} :
|
| 212 |
+
a ∈ t ↔ (t.get? a).isSome :=
|
| 213 |
+
ExtTreeMap.mem_iff_isSome_getKey?
|
| 214 |
+
|
| 215 |
+
@[simp]
|
| 216 |
+
theorem isSome_get?_iff_mem [TransCmp cmp] {a : α} :
|
| 217 |
+
(t.get? a).isSome ↔ a ∈ t :=
|
| 218 |
+
mem_iff_isSome_get?.symm
|
| 219 |
+
|
| 220 |
+
theorem get?_eq_none_of_contains_eq_false [TransCmp cmp] {a : α} :
|
| 221 |
+
t.contains a = false → t.get? a = none :=
|
| 222 |
+
ExtTreeMap.getKey?_eq_none_of_contains_eq_false
|
| 223 |
+
|
| 224 |
+
theorem get?_eq_none [TransCmp cmp] {a : α} :
|
| 225 |
+
¬ a ∈ t → t.get? a = none :=
|
| 226 |
+
ExtTreeMap.getKey?_eq_none
|
| 227 |
+
|
| 228 |
+
@[grind =] theorem get?_erase [TransCmp cmp] {k a : α} :
|
| 229 |
+
(t.erase k).get? a = if cmp k a = .eq then none else t.get? a :=
|
| 230 |
+
ExtTreeMap.getKey?_erase
|
| 231 |
+
|
| 232 |
+
@[simp]
|
| 233 |
+
theorem get?_erase_self [TransCmp cmp] {k : α} :
|
| 234 |
+
(t.erase k).get? k = none :=
|
| 235 |
+
ExtTreeMap.getKey?_erase_self
|
| 236 |
+
|
| 237 |
+
theorem compare_get?_self [TransCmp cmp] {k : α} :
|
| 238 |
+
(t.get? k).all (cmp · k = .eq) :=
|
| 239 |
+
ExtTreeMap.compare_getKey?_self
|
| 240 |
+
|
| 241 |
+
theorem get?_congr [TransCmp cmp] {k k' : α} (h' : cmp k k' = .eq) :
|
| 242 |
+
t.get? k = t.get? k' :=
|
| 243 |
+
ExtTreeMap.getKey?_congr h'
|
| 244 |
+
|
| 245 |
+
theorem get?_eq_some_of_contains [TransCmp cmp] [LawfulEqCmp cmp] {k : α} (h' : t.contains k) :
|
| 246 |
+
t.get? k = some k :=
|
| 247 |
+
ExtTreeMap.getKey?_eq_some_of_contains h'
|
| 248 |
+
|
| 249 |
+
theorem get?_eq_some [TransCmp cmp] [LawfulEqCmp cmp] {k : α} (h' : k ∈ t) :
|
| 250 |
+
t.get? k = some k :=
|
| 251 |
+
ExtTreeMap.getKey?_eq_some_of_contains h'
|
| 252 |
+
|
| 253 |
+
@[grind =] theorem get_insert [TransCmp cmp] {k a : α} {h₁} :
|
| 254 |
+
(t.insert k).get a h₁ =
|
| 255 |
+
if h₂ : cmp k a = .eq ∧ ¬ k ∈ t then k
|
| 256 |
+
else t.get a (mem_of_mem_insert' h₁ h₂) :=
|
| 257 |
+
ExtTreeMap.getKey_insertIfNew
|
| 258 |
+
|
| 259 |
+
@[simp, grind =] theorem get_erase [TransCmp cmp] {k a : α} {h'} :
|
| 260 |
+
(t.erase k).get a h' = t.get a (mem_of_mem_erase h') :=
|
| 261 |
+
ExtTreeMap.getKey_erase
|
| 262 |
+
|
| 263 |
+
theorem get?_eq_some_get [TransCmp cmp] {a : α} (h') :
|
| 264 |
+
t.get? a = some (t.get a h') :=
|
| 265 |
+
ExtTreeMap.getKey?_eq_some_getKey h'
|
| 266 |
+
|
| 267 |
+
theorem compare_get_self [TransCmp cmp] {k : α} (h' : k ∈ t) :
|
| 268 |
+
cmp (t.get k h') k = .eq :=
|
| 269 |
+
ExtTreeMap.compare_getKey_self h'
|
| 270 |
+
|
| 271 |
+
theorem get_congr [TransCmp cmp] {k₁ k₂ : α} (h' : cmp k₁ k₂ = .eq)
|
| 272 |
+
(h₁ : k₁ ∈ t) : t.get k₁ h₁ = t.get k₂ ((mem_congr h').mp h₁) :=
|
| 273 |
+
ExtTreeMap.getKey_congr h' h₁
|
| 274 |
+
|
| 275 |
+
@[simp, grind =]
|
| 276 |
+
theorem get_eq [TransCmp cmp] [LawfulEqCmp cmp] {k : α} (h' : k ∈ t) :
|
| 277 |
+
t.get k h' = k :=
|
| 278 |
+
ExtTreeMap.getKey_eq h'
|
| 279 |
+
|
| 280 |
+
@[simp, grind =]
|
| 281 |
+
theorem get!_empty [TransCmp cmp] [Inhabited α] {a : α} :
|
| 282 |
+
(∅ : ExtTreeSet α cmp).get! a = default :=
|
| 283 |
+
ExtTreeMap.getKey!_empty
|
| 284 |
+
|
| 285 |
+
@[grind =] theorem get!_insert [TransCmp cmp] [Inhabited α] {k a : α} :
|
| 286 |
+
(t.insert k).get! a = if cmp k a = .eq ∧ ¬ k ∈ t then k else t.get! a :=
|
| 287 |
+
ExtTreeMap.getKey!_insertIfNew
|
| 288 |
+
|
| 289 |
+
theorem get!_eq_default_of_contains_eq_false [TransCmp cmp] [Inhabited α] {a : α} :
|
| 290 |
+
t.contains a = false → t.get! a = default :=
|
| 291 |
+
ExtTreeMap.getKey!_eq_default_of_contains_eq_false
|
| 292 |
+
|
| 293 |
+
theorem get!_eq_default [TransCmp cmp] [Inhabited α] {a : α} :
|
| 294 |
+
¬ a ∈ t → t.get! a = default :=
|
| 295 |
+
ExtTreeMap.getKey!_eq_default
|
| 296 |
+
|
| 297 |
+
@[grind =] theorem get!_erase [TransCmp cmp] [Inhabited α] {k a : α} :
|
| 298 |
+
(t.erase k).get! a = if cmp k a = .eq then default else t.get! a :=
|
| 299 |
+
ExtTreeMap.getKey!_erase
|
| 300 |
+
|
| 301 |
+
@[simp]
|
| 302 |
+
theorem get!_erase_self [TransCmp cmp] [Inhabited α] {k : α} :
|
| 303 |
+
(t.erase k).get! k = default :=
|
| 304 |
+
ExtTreeMap.getKey!_erase_self
|
| 305 |
+
|
| 306 |
+
theorem get?_eq_some_get!_of_contains [TransCmp cmp] [Inhabited α] {a : α} :
|
| 307 |
+
t.contains a = true → t.get? a = some (t.get! a) :=
|
| 308 |
+
ExtTreeMap.getKey?_eq_some_getKey!_of_contains
|
| 309 |
+
|
| 310 |
+
theorem get?_eq_some_get! [TransCmp cmp] [Inhabited α] {a : α} :
|
| 311 |
+
a ∈ t → t.get? a = some (t.get! a) :=
|
| 312 |
+
ExtTreeMap.getKey?_eq_some_getKey!
|
| 313 |
+
|
| 314 |
+
theorem get!_eq_get!_get? [TransCmp cmp] [Inhabited α] {a : α} :
|
| 315 |
+
t.get! a = (t.get? a).get! :=
|
| 316 |
+
ExtTreeMap.getKey!_eq_get!_getKey?
|
| 317 |
+
|
| 318 |
+
theorem get_eq_get! [TransCmp cmp] [Inhabited α] {a : α} {h} :
|
| 319 |
+
t.get a h = t.get! a :=
|
| 320 |
+
ExtTreeMap.getKey_eq_getKey!
|
| 321 |
+
|
| 322 |
+
theorem get!_congr [TransCmp cmp] [Inhabited α] {k k' : α} (h' : cmp k k' = .eq) :
|
| 323 |
+
t.get! k = t.get! k' :=
|
| 324 |
+
ExtTreeMap.getKey!_congr h'
|
| 325 |
+
|
| 326 |
+
theorem get!_eq_of_contains [TransCmp cmp] [LawfulEqCmp cmp] [Inhabited α] {k : α}
|
| 327 |
+
(h' : t.contains k) :
|
| 328 |
+
t.get! k = k :=
|
| 329 |
+
ExtTreeMap.getKey!_eq_of_contains h'
|
| 330 |
+
|
| 331 |
+
theorem get!_eq_of_mem [TransCmp cmp] [LawfulEqCmp cmp] [Inhabited α] {k : α} (h' : k ∈ t) :
|
| 332 |
+
t.get! k = k :=
|
| 333 |
+
ExtTreeMap.getKey!_eq_of_mem h'
|
| 334 |
+
|
| 335 |
+
@[simp, grind =]
|
| 336 |
+
theorem getD_empty [TransCmp cmp] {a : α} {fallback : α} :
|
| 337 |
+
(∅ : ExtTreeSet α cmp).getD a fallback = fallback :=
|
| 338 |
+
ExtTreeMap.getKeyD_empty
|
| 339 |
+
|
| 340 |
+
@[grind =] theorem getD_insert [TransCmp cmp] {k a fallback : α} :
|
| 341 |
+
(t.insert k).getD a fallback =
|
| 342 |
+
if cmp k a = .eq ∧ ¬ k ∈ t then k else t.getD a fallback :=
|
| 343 |
+
ExtTreeMap.getKeyD_insertIfNew
|
| 344 |
+
|
| 345 |
+
theorem getD_eq_fallback_of_contains_eq_false [TransCmp cmp] {a fallback : α} :
|
| 346 |
+
t.contains a = false → t.getD a fallback = fallback :=
|
| 347 |
+
ExtTreeMap.getKeyD_eq_fallback_of_contains_eq_false
|
| 348 |
+
|
| 349 |
+
theorem getD_eq_fallback [TransCmp cmp] {a fallback : α} :
|
| 350 |
+
¬ a ∈ t → t.getD a fallback = fallback :=
|
| 351 |
+
ExtTreeMap.getKeyD_eq_fallback
|
| 352 |
+
|
| 353 |
+
@[grind =] theorem getD_erase [TransCmp cmp] {k a fallback : α} :
|
| 354 |
+
(t.erase k).getD a fallback =
|
| 355 |
+
if cmp k a = .eq then fallback else t.getD a fallback :=
|
| 356 |
+
ExtTreeMap.getKeyD_erase
|
| 357 |
+
|
| 358 |
+
@[simp]
|
| 359 |
+
theorem getD_erase_self [TransCmp cmp] {k fallback : α} :
|
| 360 |
+
(t.erase k).getD k fallback = fallback :=
|
| 361 |
+
ExtTreeMap.getKeyD_erase_self
|
| 362 |
+
|
| 363 |
+
theorem get?_eq_some_getD_of_contains [TransCmp cmp] {a fallback : α} :
|
| 364 |
+
t.contains a = true → t.get? a = some (t.getD a fallback) :=
|
| 365 |
+
ExtTreeMap.getKey?_eq_some_getKeyD_of_contains
|
| 366 |
+
|
| 367 |
+
theorem get?_eq_some_getD [TransCmp cmp] {a fallback : α} :
|
| 368 |
+
a ∈ t → t.get? a = some (t.getD a fallback) :=
|
| 369 |
+
ExtTreeMap.getKey?_eq_some_getKeyD
|
| 370 |
+
|
| 371 |
+
theorem getD_eq_getD_get? [TransCmp cmp] {a fallback : α} :
|
| 372 |
+
t.getD a fallback = (t.get? a).getD fallback :=
|
| 373 |
+
ExtTreeMap.getKeyD_eq_getD_getKey?
|
| 374 |
+
|
| 375 |
+
theorem get_eq_getD [TransCmp cmp] {a fallback : α} {h} :
|
| 376 |
+
t.get a h = t.getD a fallback :=
|
| 377 |
+
ExtTreeMap.getKey_eq_getKeyD
|
| 378 |
+
|
| 379 |
+
theorem get!_eq_getD_default [TransCmp cmp] [Inhabited α] {a : α} :
|
| 380 |
+
t.get! a = t.getD a default :=
|
| 381 |
+
ExtTreeMap.getKey!_eq_getKeyD_default
|
| 382 |
+
|
| 383 |
+
theorem getD_congr [TransCmp cmp] {k k' fallback : α} (h' : cmp k k' = .eq) :
|
| 384 |
+
t.getD k fallback = t.getD k' fallback :=
|
| 385 |
+
ExtTreeMap.getKeyD_congr h'
|
| 386 |
+
|
| 387 |
+
theorem getD_eq_of_contains [TransCmp cmp] [LawfulEqCmp cmp] {k fallback : α} (h' : t.contains k) :
|
| 388 |
+
t.getD k fallback = k :=
|
| 389 |
+
ExtTreeMap.getKeyD_eq_of_contains h'
|
| 390 |
+
|
| 391 |
+
theorem getD_eq_of_mem [TransCmp cmp] [LawfulEqCmp cmp] {k fallback : α} (h' : k ∈ t) :
|
| 392 |
+
t.getD k fallback = k :=
|
| 393 |
+
ExtTreeMap.getKeyD_eq_of_contains h'
|
| 394 |
+
|
| 395 |
+
@[simp, grind =]
|
| 396 |
+
theorem containsThenInsert_fst [TransCmp cmp] {k : α} :
|
| 397 |
+
(t.containsThenInsert k).1 = t.contains k :=
|
| 398 |
+
ExtTreeMap.containsThenInsertIfNew_fst
|
| 399 |
+
|
| 400 |
+
@[simp, grind =]
|
| 401 |
+
theorem containsThenInsert_snd [TransCmp cmp] {k : α} :
|
| 402 |
+
(t.containsThenInsert k).2 = t.insert k :=
|
| 403 |
+
ext <| ExtTreeMap.containsThenInsertIfNew_snd
|
| 404 |
+
|
| 405 |
+
@[simp, grind =]
|
| 406 |
+
theorem length_toList [TransCmp cmp] :
|
| 407 |
+
t.toList.length = t.size :=
|
| 408 |
+
ExtTreeMap.length_keys
|
| 409 |
+
|
| 410 |
+
@[simp, grind =]
|
| 411 |
+
theorem isEmpty_toList [TransCmp cmp] :
|
| 412 |
+
t.toList.isEmpty = t.isEmpty :=
|
| 413 |
+
ExtTreeMap.isEmpty_keys
|
| 414 |
+
|
| 415 |
+
@[simp]
|
| 416 |
+
theorem toList_eq_nil_iff [TransCmp cmp] :
|
| 417 |
+
t.toList = [] ↔ t = ∅ :=
|
| 418 |
+
ExtTreeMap.keys_eq_nil_iff.trans ext_iff.symm
|
| 419 |
+
|
| 420 |
+
@[simp, grind =]
|
| 421 |
+
theorem contains_toList [BEq α] [LawfulBEqCmp cmp] [TransCmp cmp] {k : α} :
|
| 422 |
+
t.toList.contains k = t.contains k :=
|
| 423 |
+
ExtTreeMap.contains_keys
|
| 424 |
+
|
| 425 |
+
@[simp]
|
| 426 |
+
theorem mem_toList [LawfulEqCmp cmp] [TransCmp cmp] {k : α} :
|
| 427 |
+
k ∈ t.toList ↔ k ∈ t :=
|
| 428 |
+
ExtTreeMap.mem_keys
|
| 429 |
+
|
| 430 |
+
theorem distinct_toList [TransCmp cmp] :
|
| 431 |
+
t.toList.Pairwise (fun a b => ¬ cmp a b = .eq) :=
|
| 432 |
+
ExtTreeMap.distinct_keys
|
| 433 |
+
|
| 434 |
+
theorem ordered_toList [TransCmp cmp] :
|
| 435 |
+
t.toList.Pairwise (fun a b => cmp a b = .lt) :=
|
| 436 |
+
ExtTreeMap.ordered_keys
|
| 437 |
+
|
| 438 |
+
section monadic
|
| 439 |
+
|
| 440 |
+
variable {δ : Type w} {m : Type w → Type w'}
|
| 441 |
+
|
| 442 |
+
theorem foldlM_eq_foldlM_toList [TransCmp cmp] [Monad m] [LawfulMonad m] {f : δ → α → m δ} {init : δ} :
|
| 443 |
+
t.foldlM f init = t.toList.foldlM f init :=
|
| 444 |
+
ExtTreeMap.foldlM_eq_foldlM_keys
|
| 445 |
+
|
| 446 |
+
theorem foldl_eq_foldl_toList [TransCmp cmp] {f : δ → α → δ} {init : δ} :
|
| 447 |
+
t.foldl f init = t.toList.foldl f init :=
|
| 448 |
+
ExtTreeMap.foldl_eq_foldl_keys
|
| 449 |
+
|
| 450 |
+
theorem foldrM_eq_foldrM_toList [TransCmp cmp] [Monad m] [LawfulMonad m] {f : α → δ → m δ} {init : δ} :
|
| 451 |
+
t.foldrM f init = t.toList.foldrM f init :=
|
| 452 |
+
ExtTreeMap.foldrM_eq_foldrM_keys
|
| 453 |
+
|
| 454 |
+
theorem foldr_eq_foldr_toList [TransCmp cmp] {f : α → δ → δ} {init : δ} :
|
| 455 |
+
t.foldr f init = t.toList.foldr f init :=
|
| 456 |
+
ExtTreeMap.foldr_eq_foldr_keys
|
| 457 |
+
|
| 458 |
+
@[simp, grind =]
|
| 459 |
+
theorem forM_eq_forM [TransCmp cmp] [Monad m] [LawfulMonad m] {f : α → m PUnit} :
|
| 460 |
+
t.forM f = ForM.forM t f := rfl
|
| 461 |
+
|
| 462 |
+
theorem forM_eq_forM_toList [TransCmp cmp] [Monad m] [LawfulMonad m] {f : α → m PUnit} :
|
| 463 |
+
ForM.forM t f = t.toList.forM f :=
|
| 464 |
+
ExtTreeMap.forM_eq_forM_keys
|
| 465 |
+
|
| 466 |
+
@[simp, grind =]
|
| 467 |
+
theorem forIn_eq_forIn [TransCmp cmp] [Monad m] [LawfulMonad m] {f : α → δ → m (ForInStep δ)} {init : δ} :
|
| 468 |
+
t.forIn f init = ForIn.forIn t init f := rfl
|
| 469 |
+
|
| 470 |
+
theorem forIn_eq_forIn_toList [TransCmp cmp] [Monad m] [LawfulMonad m] {f : α → δ → m (ForInStep δ)} {init : δ} :
|
| 471 |
+
ForIn.forIn t init f = ForIn.forIn t.toList init f :=
|
| 472 |
+
ExtTreeMap.forIn_eq_forIn_keys
|
| 473 |
+
|
| 474 |
+
end monadic
|
| 475 |
+
|
| 476 |
+
@[simp, grind =]
|
| 477 |
+
theorem insertMany_nil [TransCmp cmp] : t.insertMany [] = t :=
|
| 478 |
+
rfl
|
| 479 |
+
|
| 480 |
+
@[simp, grind =]
|
| 481 |
+
theorem insertMany_list_singleton [TransCmp cmp] {k : α} :
|
| 482 |
+
t.insertMany [k] = t.insert k :=
|
| 483 |
+
rfl
|
| 484 |
+
|
| 485 |
+
@[grind _=_]
|
| 486 |
+
theorem insertMany_cons [TransCmp cmp] {l : List α} {k : α} :
|
| 487 |
+
t.insertMany (k :: l) = (t.insert k).insertMany l :=
|
| 488 |
+
ext ExtTreeMap.insertManyIfNewUnit_cons
|
| 489 |
+
|
| 490 |
+
@[grind _=_]
|
| 491 |
+
theorem insertMany_append [TransCmp cmp] {l₁ l₂ : List α} :
|
| 492 |
+
insertMany t (l₁ ++ l₂) = insertMany (insertMany t l₁) l₂ := by
|
| 493 |
+
induction l₁ generalizing t with
|
| 494 |
+
| nil => simp
|
| 495 |
+
| cons hd tl ih =>
|
| 496 |
+
rw [List.cons_append, insertMany_cons, insertMany_cons, ih]
|
| 497 |
+
|
| 498 |
+
@[simp, grind =]
|
| 499 |
+
theorem contains_insertMany_list [TransCmp cmp] [BEq α] [LawfulBEqCmp cmp]
|
| 500 |
+
{l : List α} {k : α} :
|
| 501 |
+
(t.insertMany l).contains k = (t.contains k || l.contains k) :=
|
| 502 |
+
ExtTreeMap.contains_insertManyIfNewUnit_list
|
| 503 |
+
|
| 504 |
+
@[simp, grind =]
|
| 505 |
+
theorem mem_insertMany_list [TransCmp cmp] [BEq α] [LawfulBEqCmp cmp]
|
| 506 |
+
{l : List α} {k : α} :
|
| 507 |
+
k ∈ insertMany t l ↔ k ∈ t ∨ l.contains k :=
|
| 508 |
+
ExtTreeMap.mem_insertManyIfNewUnit_list
|
| 509 |
+
|
| 510 |
+
theorem mem_of_mem_insertMany_list [TransCmp cmp] [BEq α] [LawfulBEqCmp cmp]
|
| 511 |
+
{l : List α} {k : α} (contains_eq_false : l.contains k = false) :
|
| 512 |
+
k ∈ insertMany t l → k ∈ t :=
|
| 513 |
+
ExtTreeMap.mem_of_mem_insertManyIfNewUnit_list contains_eq_false
|
| 514 |
+
|
| 515 |
+
theorem get?_insertMany_list_of_not_mem_of_contains_eq_false
|
| 516 |
+
[TransCmp cmp] [BEq α] [LawfulBEqCmp cmp] {l : List α} {k : α}
|
| 517 |
+
(not_mem : ¬ k ∈ t) (contains_eq_false : l.contains k = false) :
|
| 518 |
+
get? (insertMany t l) k = none :=
|
| 519 |
+
ExtTreeMap.getKey?_insertManyIfNewUnit_list_of_not_mem_of_contains_eq_false
|
| 520 |
+
not_mem contains_eq_false
|
| 521 |
+
|
| 522 |
+
theorem get?_insertMany_list_of_not_mem_of_mem [TransCmp cmp]
|
| 523 |
+
{l : List α} {k k' : α} (k_eq : cmp k k' = .eq)
|
| 524 |
+
(not_mem : ¬ k ∈ t) (distinct : l.Pairwise (fun a b => ¬ cmp a b = .eq)) (mem : k ∈ l) :
|
| 525 |
+
get? (insertMany t l) k' = some k :=
|
| 526 |
+
ExtTreeMap.getKey?_insertManyIfNewUnit_list_of_not_mem_of_mem k_eq not_mem distinct mem
|
| 527 |
+
|
| 528 |
+
theorem get?_insertMany_list_of_mem [TransCmp cmp]
|
| 529 |
+
{l : List α} {k : α} (mem : k ∈ t) :
|
| 530 |
+
get? (insertMany t l) k = get? t k :=
|
| 531 |
+
ExtTreeMap.getKey?_insertManyIfNewUnit_list_of_mem mem
|
| 532 |
+
|
| 533 |
+
theorem get_insertMany_list_of_mem [TransCmp cmp]
|
| 534 |
+
{l : List α} {k : α} {h'} (contains : k ∈ t) :
|
| 535 |
+
get (insertMany t l) k h' = get t k contains :=
|
| 536 |
+
ExtTreeMap.getKey_insertManyIfNewUnit_list_of_mem contains
|
| 537 |
+
|
| 538 |
+
theorem get_insertMany_list_of_not_mem_of_mem [TransCmp cmp]
|
| 539 |
+
{l : List α}
|
| 540 |
+
{k k' : α} (k_eq : cmp k k' = .eq) {h'} (not_mem : ¬ k ∈ t)
|
| 541 |
+
(distinct : l.Pairwise (fun a b => ¬ cmp a b = .eq)) (mem : k ∈ l) :
|
| 542 |
+
get (insertMany t l) k' h' = k :=
|
| 543 |
+
ExtTreeMap.getKey_insertManyIfNewUnit_list_of_not_mem_of_mem k_eq not_mem distinct mem
|
| 544 |
+
|
| 545 |
+
theorem get!_insertMany_list_of_not_mem_of_contains_eq_false
|
| 546 |
+
[TransCmp cmp] [BEq α] [LawfulBEqCmp cmp] [Inhabited α] {l : List α} {k : α}
|
| 547 |
+
(not_mem : ¬ k ∈ t) (contains_eq_false : l.contains k = false) :
|
| 548 |
+
get! (insertMany t l) k = default :=
|
| 549 |
+
ExtTreeMap.getKey!_insertManyIfNewUnit_list_of_not_mem_of_contains_eq_false
|
| 550 |
+
not_mem contains_eq_false
|
| 551 |
+
|
| 552 |
+
theorem get!_insertMany_list_of_not_mem_of_mem [TransCmp cmp]
|
| 553 |
+
[Inhabited α] {l : List α} {k k' : α} (k_eq : cmp k k' = .eq)
|
| 554 |
+
(not_mem : ¬ k ∈ t) (distinct : l.Pairwise (fun a b => ¬ cmp a b = .eq)) (mem : k ∈ l) :
|
| 555 |
+
get! (insertMany t l) k' = k :=
|
| 556 |
+
ExtTreeMap.getKey!_insertManyIfNewUnit_list_of_not_mem_of_mem k_eq not_mem distinct mem
|
| 557 |
+
|
| 558 |
+
theorem get!_insertMany_list_of_mem [TransCmp cmp]
|
| 559 |
+
[Inhabited α] {l : List α} {k : α} (mem : k ∈ t):
|
| 560 |
+
get! (insertMany t l) k = get! t k :=
|
| 561 |
+
ExtTreeMap.getKey!_insertManyIfNewUnit_list_of_mem mem
|
| 562 |
+
|
| 563 |
+
theorem getD_insertMany_list_of_not_mem_of_contains_eq_false
|
| 564 |
+
[TransCmp cmp] [BEq α] [LawfulBEqCmp cmp] {l : List α} {k fallback : α}
|
| 565 |
+
(not_mem : ¬ k ∈ t) (contains_eq_false : l.contains k = false) :
|
| 566 |
+
getD (insertMany t l) k fallback = fallback :=
|
| 567 |
+
ExtTreeMap.getKeyD_insertManyIfNewUnit_list_of_not_mem_of_contains_eq_false
|
| 568 |
+
not_mem contains_eq_false
|
| 569 |
+
|
| 570 |
+
theorem getD_insertMany_list_of_not_mem_of_mem [TransCmp cmp]
|
| 571 |
+
{l : List α} {k k' fallback : α} (k_eq : cmp k k' = .eq)
|
| 572 |
+
(not_mem : ¬ k ∈ t) (distinct : l.Pairwise (fun a b => ¬ cmp a b = .eq)) (mem : k ∈ l) :
|
| 573 |
+
getD (insertMany t l) k' fallback = k :=
|
| 574 |
+
ExtTreeMap.getKeyD_insertManyIfNewUnit_list_of_not_mem_of_mem k_eq not_mem distinct mem
|
| 575 |
+
|
| 576 |
+
theorem getD_insertMany_list_of_mem [TransCmp cmp]
|
| 577 |
+
{l : List α} {k fallback : α} (mem : k ∈ t) :
|
| 578 |
+
getD (insertMany t l) k fallback = getD t k fallback :=
|
| 579 |
+
ExtTreeMap.getKeyD_insertManyIfNewUnit_list_of_mem mem
|
| 580 |
+
|
| 581 |
+
theorem size_insertMany_list [TransCmp cmp] [BEq α] [LawfulBEqCmp cmp]
|
| 582 |
+
{l : List α}
|
| 583 |
+
(distinct : l.Pairwise (fun a b => ¬ cmp a b = .eq)) :
|
| 584 |
+
(∀ (a : α), a ∈ t → l.contains a = false) →
|
| 585 |
+
(insertMany t l).size = t.size + l.length :=
|
| 586 |
+
ExtTreeMap.size_insertManyIfNewUnit_list distinct
|
| 587 |
+
|
| 588 |
+
theorem size_le_size_insertMany_list [TransCmp cmp]
|
| 589 |
+
{l : List α} :
|
| 590 |
+
t.size ≤ (insertMany t l).size :=
|
| 591 |
+
ExtTreeMap.size_le_size_insertManyIfNewUnit_list
|
| 592 |
+
|
| 593 |
+
grind_pattern size_le_size_insertMany_list => (insertMany t l).size
|
| 594 |
+
|
| 595 |
+
theorem size_insertMany_list_le [TransCmp cmp]
|
| 596 |
+
{l : List α} :
|
| 597 |
+
(insertMany t l).size ≤ t.size + l.length :=
|
| 598 |
+
ExtTreeMap.size_insertManyIfNewUnit_list_le
|
| 599 |
+
|
| 600 |
+
grind_pattern size_insertMany_list_le => (insertMany t l).size
|
| 601 |
+
|
| 602 |
+
@[simp, grind =]
|
| 603 |
+
theorem isEmpty_insertMany_list [TransCmp cmp] {l : List α} :
|
| 604 |
+
(t.insertMany l).isEmpty = (t.isEmpty && l.isEmpty) :=
|
| 605 |
+
ExtTreeMap.isEmpty_insertManyIfNewUnit_list
|
| 606 |
+
|
| 607 |
+
@[simp]
|
| 608 |
+
theorem insertMany_list_eq_empty_iff [TransCmp cmp] {l : List α} :
|
| 609 |
+
t.insertMany l = ∅ ↔ t = ∅ ∧ l = [] := by
|
| 610 |
+
simpa only [ext_iff] using ExtTreeMap.insertManyIfNewUnit_list_eq_empty_iff
|
| 611 |
+
|
| 612 |
+
@[simp, grind =]
|
| 613 |
+
theorem ofList_nil :
|
| 614 |
+
ofList ([] : List α) cmp =
|
| 615 |
+
(∅ : ExtTreeSet α cmp) :=
|
| 616 |
+
rfl
|
| 617 |
+
|
| 618 |
+
@[simp]
|
| 619 |
+
theorem ofList_singleton [TransCmp cmp] {k : α} :
|
| 620 |
+
ofList [k] cmp = (∅ : ExtTreeSet α cmp).insert k :=
|
| 621 |
+
rfl
|
| 622 |
+
|
| 623 |
+
@[grind _=_]
|
| 624 |
+
theorem ofList_cons [TransCmp cmp] {hd : α} {tl : List α} :
|
| 625 |
+
ofList (hd :: tl) cmp =
|
| 626 |
+
insertMany ((∅ : ExtTreeSet α cmp).insert hd) tl :=
|
| 627 |
+
ext ExtTreeMap.unitOfList_cons
|
| 628 |
+
|
| 629 |
+
theorem ofList_eq_insertMany_empty [TransCmp cmp] {l : List α} :
|
| 630 |
+
ofList l cmp = insertMany (∅ : ExtTreeSet α cmp) l :=
|
| 631 |
+
match l with
|
| 632 |
+
| [] => by simp
|
| 633 |
+
| hd :: tl => by simp [ofList_cons, insertMany_cons]
|
| 634 |
+
|
| 635 |
+
@[simp, grind =]
|
| 636 |
+
theorem contains_ofList [TransCmp cmp] [BEq α] [LawfulBEqCmp cmp] {l : List α} {k : α} :
|
| 637 |
+
(ofList l cmp).contains k = l.contains k :=
|
| 638 |
+
ExtTreeMap.contains_unitOfList
|
| 639 |
+
|
| 640 |
+
@[simp, grind =]
|
| 641 |
+
theorem mem_ofList [TransCmp cmp] [BEq α] [LawfulBEqCmp cmp] {l : List α} {k : α} :
|
| 642 |
+
k ∈ ofList l cmp ↔ l.contains k := by
|
| 643 |
+
simp [mem_iff_contains]
|
| 644 |
+
|
| 645 |
+
theorem get?_ofList_of_contains_eq_false [TransCmp cmp] [BEq α] [LawfulBEqCmp cmp]
|
| 646 |
+
{l : List α} {k : α}
|
| 647 |
+
(contains_eq_false : l.contains k = false) :
|
| 648 |
+
get? (ofList l cmp) k = none :=
|
| 649 |
+
ExtTreeMap.getKey?_unitOfList_of_contains_eq_false contains_eq_false
|
| 650 |
+
|
| 651 |
+
theorem get?_ofList_of_mem [TransCmp cmp]
|
| 652 |
+
{l : List α} {k k' : α} (k_eq : cmp k k' = .eq)
|
| 653 |
+
(distinct : l.Pairwise (fun a b => ¬ cmp a b = .eq)) (mem : k ∈ l) :
|
| 654 |
+
get? (ofList l cmp) k' = some k :=
|
| 655 |
+
ExtTreeMap.getKey?_unitOfList_of_mem k_eq distinct mem
|
| 656 |
+
|
| 657 |
+
theorem get_ofList_of_mem [TransCmp cmp]
|
| 658 |
+
{l : List α}
|
| 659 |
+
{k k' : α} (k_eq : cmp k k' = .eq)
|
| 660 |
+
(distinct : l.Pairwise (fun a b => ¬ cmp a b = .eq))
|
| 661 |
+
(mem : k ∈ l) {h'} :
|
| 662 |
+
get (ofList l cmp) k' h' = k :=
|
| 663 |
+
ExtTreeMap.getKey_unitOfList_of_mem k_eq distinct mem
|
| 664 |
+
|
| 665 |
+
theorem get!_ofList_of_contains_eq_false [TransCmp cmp] [BEq α]
|
| 666 |
+
[LawfulBEqCmp cmp] [Inhabited α] {l : List α} {k : α}
|
| 667 |
+
(contains_eq_false : l.contains k = false) :
|
| 668 |
+
get! (ofList l cmp) k = default :=
|
| 669 |
+
ExtTreeMap.getKey!_unitOfList_of_contains_eq_false contains_eq_false
|
| 670 |
+
|
| 671 |
+
theorem get!_ofList_of_mem [TransCmp cmp]
|
| 672 |
+
[Inhabited α] {l : List α} {k k' : α} (k_eq : cmp k k' = .eq)
|
| 673 |
+
(distinct : l.Pairwise (fun a b => ¬ cmp a b = .eq))
|
| 674 |
+
(mem : k ∈ l) :
|
| 675 |
+
get! (ofList l cmp) k' = k :=
|
| 676 |
+
ExtTreeMap.getKey!_unitOfList_of_mem k_eq distinct mem
|
| 677 |
+
|
| 678 |
+
theorem getD_ofList_of_contains_eq_false [TransCmp cmp] [BEq α]
|
| 679 |
+
[LawfulBEqCmp cmp] {l : List α} {k fallback : α}
|
| 680 |
+
(contains_eq_false : l.contains k = false) :
|
| 681 |
+
getD (ofList l cmp) k fallback = fallback :=
|
| 682 |
+
ExtTreeMap.getKeyD_unitOfList_of_contains_eq_false contains_eq_false
|
| 683 |
+
|
| 684 |
+
theorem getD_ofList_of_mem [TransCmp cmp]
|
| 685 |
+
{l : List α} {k k' fallback : α} (k_eq : cmp k k' = .eq)
|
| 686 |
+
(distinct : l.Pairwise (fun a b => ¬ cmp a b = .eq))
|
| 687 |
+
(mem : k ∈ l) :
|
| 688 |
+
getD (ofList l cmp) k' fallback = k :=
|
| 689 |
+
ExtTreeMap.getKeyD_unitOfList_of_mem k_eq distinct mem
|
| 690 |
+
|
| 691 |
+
theorem size_ofList [TransCmp cmp] {l : List α}
|
| 692 |
+
(distinct : l.Pairwise (fun a b => ¬ cmp a b = .eq)) :
|
| 693 |
+
(ofList l cmp).size = l.length :=
|
| 694 |
+
ExtTreeMap.size_unitOfList distinct
|
| 695 |
+
|
| 696 |
+
theorem size_ofList_le [TransCmp cmp] {l : List α} :
|
| 697 |
+
(ofList l cmp).size ≤ l.length :=
|
| 698 |
+
ExtTreeMap.size_unitOfList_le
|
| 699 |
+
|
| 700 |
+
grind_pattern size_ofList_le => (ofList l cmp).size
|
| 701 |
+
|
| 702 |
+
@[simp]
|
| 703 |
+
theorem ofList_eq_empty_iff [TransCmp cmp] {l : List α} :
|
| 704 |
+
ofList l cmp = ∅ ↔ l = [] :=
|
| 705 |
+
ext_iff.trans ExtTreeMap.unitOfList_eq_empty_iff
|
| 706 |
+
|
| 707 |
+
section Min
|
| 708 |
+
|
| 709 |
+
@[simp, grind =]
|
| 710 |
+
theorem min?_empty [TransCmp cmp] :
|
| 711 |
+
(∅ : ExtTreeSet α cmp).min? = none :=
|
| 712 |
+
ExtTreeMap.minKey?_empty
|
| 713 |
+
|
| 714 |
+
@[simp, grind =]
|
| 715 |
+
theorem min?_eq_none_iff [TransCmp cmp] :
|
| 716 |
+
t.min? = none ↔ t = ∅ :=
|
| 717 |
+
ExtTreeMap.minKey?_eq_none_iff.trans ext_iff.symm
|
| 718 |
+
|
| 719 |
+
theorem min?_eq_some_iff_get?_eq_self_and_forall [TransCmp cmp] {km} :
|
| 720 |
+
t.min? = some km ↔ t.get? km = some km ∧ ∀ k ∈ t, (cmp km k).isLE :=
|
| 721 |
+
ExtTreeMap.minKey?_eq_some_iff_getKey?_eq_self_and_forall
|
| 722 |
+
|
| 723 |
+
theorem min?_eq_some_iff_mem_and_forall [TransCmp cmp] [LawfulEqCmp cmp] {km} :
|
| 724 |
+
t.min? = some km ↔ km ∈ t ∧ ∀ k ∈ t, (cmp km k).isLE :=
|
| 725 |
+
ExtTreeMap.minKey?_eq_some_iff_mem_and_forall
|
| 726 |
+
|
| 727 |
+
@[simp, grind =]
|
| 728 |
+
theorem isNone_min?_eq_isEmpty [TransCmp cmp] :
|
| 729 |
+
t.min?.isNone = t.isEmpty :=
|
| 730 |
+
ExtTreeMap.isNone_minKey?_eq_isEmpty
|
| 731 |
+
|
| 732 |
+
@[simp, grind =]
|
| 733 |
+
theorem isSome_min?_eq_not_isEmpty [TransCmp cmp] :
|
| 734 |
+
t.min?.isSome = !t.isEmpty :=
|
| 735 |
+
ExtTreeMap.isSome_minKey?_eq_not_isEmpty
|
| 736 |
+
|
| 737 |
+
theorem isSome_min?_iff_ne_empty [TransCmp cmp] :
|
| 738 |
+
t.min?.isSome ↔ t ≠ ∅ :=
|
| 739 |
+
ExtTreeMap.isSome_minKey?_iff_ne_empty.trans (not_congr ext_iff).symm
|
| 740 |
+
|
| 741 |
+
@[grind =] theorem min?_insert [TransCmp cmp] {k} :
|
| 742 |
+
(t.insert k).min? =
|
| 743 |
+
some (t.min?.elim k fun k' => if cmp k k' = .lt then k else k') :=
|
| 744 |
+
ExtTreeMap.minKey?_insertIfNew
|
| 745 |
+
|
| 746 |
+
theorem isSome_min?_insert [TransCmp cmp] {k} :
|
| 747 |
+
(t.insert k).min?.isSome :=
|
| 748 |
+
ExtTreeMap.isSome_minKey?_insertIfNew
|
| 749 |
+
|
| 750 |
+
theorem min?_insert_le_min? [TransCmp cmp] {k km kmi} :
|
| 751 |
+
(hkm : t.min? = some km) →
|
| 752 |
+
(hkmi : (t.insert k |>.min? |>.get isSome_min?_insert) = kmi) →
|
| 753 |
+
cmp kmi km |>.isLE :=
|
| 754 |
+
ExtTreeMap.minKey?_insertIfNew_le_minKey?
|
| 755 |
+
|
| 756 |
+
theorem min?_insert_le_self [TransCmp cmp] {k kmi} :
|
| 757 |
+
(hkmi : (t.insert k |>.min?.get isSome_min?_insert) = kmi) →
|
| 758 |
+
cmp kmi k |>.isLE :=
|
| 759 |
+
ExtTreeMap.minKey?_insertIfNew_le_self
|
| 760 |
+
|
| 761 |
+
theorem contains_min? [TransCmp cmp] {km} :
|
| 762 |
+
(hkm : t.min? = some km) → t.contains km :=
|
| 763 |
+
ExtTreeMap.contains_minKey?
|
| 764 |
+
|
| 765 |
+
theorem min?_mem [TransCmp cmp] {km} :
|
| 766 |
+
(hkm : t.min? = some km) → km ∈ t :=
|
| 767 |
+
ExtTreeMap.minKey?_mem
|
| 768 |
+
|
| 769 |
+
theorem isSome_min?_of_contains [TransCmp cmp] {k} :
|
| 770 |
+
(hc : t.contains k) → t.min?.isSome :=
|
| 771 |
+
ExtTreeMap.isSome_minKey?_of_contains
|
| 772 |
+
|
| 773 |
+
theorem isSome_min?_of_mem [TransCmp cmp] {k} :
|
| 774 |
+
k ∈ t → t.min?.isSome :=
|
| 775 |
+
ExtTreeMap.isSome_minKey?_of_mem
|
| 776 |
+
|
| 777 |
+
theorem min?_le_of_contains [TransCmp cmp] {k km} :
|
| 778 |
+
(hc : t.contains k) → (hkm : (t.min?.get <| isSome_min?_of_contains hc) = km) →
|
| 779 |
+
cmp km k |>.isLE :=
|
| 780 |
+
ExtTreeMap.minKey?_le_of_contains
|
| 781 |
+
|
| 782 |
+
theorem min?_le_of_mem [TransCmp cmp] {k km} :
|
| 783 |
+
(hc : k ∈ t) → (hkm : (t.min?.get <| isSome_min?_of_mem hc) = km) →
|
| 784 |
+
cmp km k |>.isLE :=
|
| 785 |
+
ExtTreeMap.minKey?_le_of_mem
|
| 786 |
+
|
| 787 |
+
theorem le_min? [TransCmp cmp] {k} :
|
| 788 |
+
(∀ k', t.min? = some k' → (cmp k k').isLE) ↔ (∀ k', k' ∈ t → (cmp k k').isLE) :=
|
| 789 |
+
ExtTreeMap.le_minKey?
|
| 790 |
+
|
| 791 |
+
theorem get?_min? [TransCmp cmp] {km} :
|
| 792 |
+
(hkm : t.min? = some km) → t.get? km = some km :=
|
| 793 |
+
ExtTreeMap.getKey?_minKey?
|
| 794 |
+
|
| 795 |
+
theorem get_min? [TransCmp cmp] {km hc} :
|
| 796 |
+
(hkm : t.min?.get (isSome_min?_of_contains hc) = km) → t.get km hc = km :=
|
| 797 |
+
ExtTreeMap.getKey_minKey?
|
| 798 |
+
|
| 799 |
+
theorem get!_min? [TransCmp cmp] [Inhabited α] {km} :
|
| 800 |
+
(hkm : t.min? = some km) → t.get! km = km :=
|
| 801 |
+
ExtTreeMap.getKey!_minKey?
|
| 802 |
+
|
| 803 |
+
theorem getD_min? [TransCmp cmp] {km fallback} :
|
| 804 |
+
(hkm : t.min? = some km) → t.getD km fallback = km :=
|
| 805 |
+
ExtTreeMap.getKeyD_minKey?
|
| 806 |
+
|
| 807 |
+
@[simp]
|
| 808 |
+
theorem min?_bind_get? [TransCmp cmp] :
|
| 809 |
+
t.min?.bind t.get? = t.min? :=
|
| 810 |
+
ExtTreeMap.minKey?_bind_getKey?
|
| 811 |
+
|
| 812 |
+
theorem min?_erase_eq_iff_not_compare_eq_min? [TransCmp cmp] {k} :
|
| 813 |
+
(t.erase k |>.min?) = t.min? ↔
|
| 814 |
+
∀ {km}, t.min? = some km → ¬ cmp k km = .eq :=
|
| 815 |
+
ExtTreeMap.minKey?_erase_eq_iff_not_compare_eq_minKey?
|
| 816 |
+
|
| 817 |
+
theorem min?_erase_eq_of_not_compare_eq_min? [TransCmp cmp] {k} :
|
| 818 |
+
(hc : ∀ {km}, t.min? = some km → ¬ cmp k km = .eq) →
|
| 819 |
+
(t.erase k |>.min?) = t.min? :=
|
| 820 |
+
ExtTreeMap.minKey?_erase_eq_of_not_compare_eq_minKey?
|
| 821 |
+
|
| 822 |
+
theorem isSome_min?_of_isSome_min?_erase [TransCmp cmp] {k} :
|
| 823 |
+
(hs : t.erase k |>.min?.isSome) →
|
| 824 |
+
t.min?.isSome :=
|
| 825 |
+
ExtTreeMap.isSome_minKey?_of_isSome_minKey?_erase
|
| 826 |
+
|
| 827 |
+
theorem min?_le_min?_erase [TransCmp cmp] {k km kme} :
|
| 828 |
+
(hkme : (t.erase k |>.min?) = some kme) →
|
| 829 |
+
(hkm : (t.min?.get <|
|
| 830 |
+
isSome_min?_of_isSome_min?_erase <| hkme ▸ Option.isSome_some) = km) →
|
| 831 |
+
cmp km kme |>.isLE :=
|
| 832 |
+
ExtTreeMap.minKey?_le_minKey?_erase
|
| 833 |
+
|
| 834 |
+
@[grind =_] theorem min?_eq_head?_toList [TransCmp cmp] :
|
| 835 |
+
t.min? = t.toList.head? :=
|
| 836 |
+
ExtTreeMap.minKey?_eq_head?_keys
|
| 837 |
+
|
| 838 |
+
theorem min_eq_get_min? [TransCmp cmp] {he} :
|
| 839 |
+
t.min he = t.min?.get (isSome_min?_iff_ne_empty.mpr he) :=
|
| 840 |
+
ExtTreeMap.minKey_eq_get_minKey?
|
| 841 |
+
|
| 842 |
+
theorem min?_eq_some_min [TransCmp cmp] (he) :
|
| 843 |
+
t.min? = some (t.min he) :=
|
| 844 |
+
ExtTreeMap.minKey?_eq_some_minKey (mt ext he)
|
| 845 |
+
|
| 846 |
+
theorem min_eq_iff_get?_eq_self_and_forall [TransCmp cmp] {he km} :
|
| 847 |
+
t.min he = km ↔ t.get? km = some km ∧ ∀ k ∈ t, (cmp km k).isLE :=
|
| 848 |
+
ExtTreeMap.minKey_eq_iff_getKey?_eq_self_and_forall
|
| 849 |
+
|
| 850 |
+
theorem min_eq_iff_mem_and_forall [TransCmp cmp] [LawfulEqCmp cmp] {he km} :
|
| 851 |
+
t.min he = km ↔ km ∈ t ∧ ∀ k ∈ t, (cmp km k).isLE :=
|
| 852 |
+
ExtTreeMap.minKey_eq_iff_mem_and_forall
|
| 853 |
+
|
| 854 |
+
@[grind =] theorem min_insert [TransCmp cmp] {k} :
|
| 855 |
+
(t.insert k).min insert_ne_empty =
|
| 856 |
+
t.min?.elim k fun k' => if cmp k k' = .lt then k else k' :=
|
| 857 |
+
ExtTreeMap.minKey_insertIfNew
|
| 858 |
+
|
| 859 |
+
theorem min_insert_le_min [TransCmp cmp] {k he} :
|
| 860 |
+
cmp (t.insert k |>.min insert_ne_empty)
|
| 861 |
+
(t.min he) |>.isLE :=
|
| 862 |
+
ExtTreeMap.minKey_insertIfNew_le_minKey
|
| 863 |
+
|
| 864 |
+
theorem min_insert_le_self [TransCmp cmp] {k} :
|
| 865 |
+
cmp (t.insert k |>.min <| insert_ne_empty) k |>.isLE :=
|
| 866 |
+
ExtTreeMap.minKey_insertIfNew_le_self
|
| 867 |
+
|
| 868 |
+
@[grind =] theorem contains_min [TransCmp cmp] {he} :
|
| 869 |
+
t.contains (t.min he) :=
|
| 870 |
+
ExtTreeMap.contains_minKey
|
| 871 |
+
|
| 872 |
+
@[grind] theorem min_mem [TransCmp cmp] {he} :
|
| 873 |
+
t.min he ∈ t :=
|
| 874 |
+
ExtTreeMap.minKey_mem
|
| 875 |
+
|
| 876 |
+
theorem min_le_of_contains [TransCmp cmp] {k} (hc : t.contains k) :
|
| 877 |
+
cmp (t.min (ne_empty_of_mem hc)) k |>.isLE :=
|
| 878 |
+
ExtTreeMap.minKey_le_of_contains hc
|
| 879 |
+
|
| 880 |
+
theorem min_le_of_mem [TransCmp cmp] {k} (hc : k ∈ t) :
|
| 881 |
+
cmp (t.min (ne_empty_of_mem hc)) k |>.isLE :=
|
| 882 |
+
ExtTreeMap.minKey_le_of_mem hc
|
| 883 |
+
|
| 884 |
+
theorem le_min [TransCmp cmp] {k he} :
|
| 885 |
+
(cmp k (t.min he)).isLE ↔ (∀ k', k' ∈ t → (cmp k k').isLE) :=
|
| 886 |
+
ExtTreeMap.le_minKey
|
| 887 |
+
|
| 888 |
+
@[simp, grind =]
|
| 889 |
+
theorem get?_min [TransCmp cmp] {he} :
|
| 890 |
+
t.get? (t.min he) = some (t.min he) :=
|
| 891 |
+
ExtTreeMap.getKey?_minKey
|
| 892 |
+
|
| 893 |
+
@[simp, grind =]
|
| 894 |
+
theorem get_min [TransCmp cmp] {he hc} :
|
| 895 |
+
t.get (t.min he) hc = t.min he :=
|
| 896 |
+
ExtTreeMap.getKey_minKey
|
| 897 |
+
|
| 898 |
+
@[simp, grind =]
|
| 899 |
+
theorem get!_min [TransCmp cmp] [Inhabited α] {he} :
|
| 900 |
+
t.get! (t.min he) = t.min he :=
|
| 901 |
+
ExtTreeMap.getKey!_minKey
|
| 902 |
+
|
| 903 |
+
@[simp, grind =]
|
| 904 |
+
theorem getD_min [TransCmp cmp] {he fallback} :
|
| 905 |
+
t.getD (t.min he) fallback = t.min he :=
|
| 906 |
+
ExtTreeMap.getKeyD_minKey
|
| 907 |
+
|
| 908 |
+
@[simp]
|
| 909 |
+
theorem min_erase_eq_iff_not_compare_eq_min [TransCmp cmp] {k he} :
|
| 910 |
+
(t.erase k |>.min he) =
|
| 911 |
+
t.min (ne_empty_of_erase_ne_empty he) ↔
|
| 912 |
+
¬ cmp k (t.min <| ne_empty_of_erase_ne_empty he) = .eq :=
|
| 913 |
+
ExtTreeMap.minKey_erase_eq_iff_not_compare_eq_minKey
|
| 914 |
+
|
| 915 |
+
theorem min_erase_eq_of_not_compare_eq_min [TransCmp cmp] {k he} :
|
| 916 |
+
(hc : ¬ cmp k (t.min (ne_empty_of_erase_ne_empty he)) = .eq) →
|
| 917 |
+
(t.erase k |>.min he) =
|
| 918 |
+
t.min (ne_empty_of_erase_ne_empty he) :=
|
| 919 |
+
ExtTreeMap.minKey_erase_eq_of_not_compare_eq_minKey
|
| 920 |
+
|
| 921 |
+
theorem min_le_min_erase [TransCmp cmp] {k he} :
|
| 922 |
+
cmp (t.min <| ne_empty_of_erase_ne_empty he)
|
| 923 |
+
(t.erase k |>.min he) |>.isLE :=
|
| 924 |
+
ExtTreeMap.minKey_le_minKey_erase
|
| 925 |
+
|
| 926 |
+
theorem min_eq_head_toList [TransCmp cmp] {he} :
|
| 927 |
+
t.min he = t.toList.head (mt toList_eq_nil_iff.mp he) :=
|
| 928 |
+
ExtTreeMap.minKey_eq_head_keys
|
| 929 |
+
|
| 930 |
+
theorem min?_eq_some_min! [TransCmp cmp] [Inhabited α] (he : t ≠ ∅) :
|
| 931 |
+
t.min? = some t.min! :=
|
| 932 |
+
ExtTreeMap.minKey?_eq_some_minKey! (mt ext he)
|
| 933 |
+
|
| 934 |
+
theorem min_eq_min! [TransCmp cmp] [Inhabited α] {he : t ≠ ∅} :
|
| 935 |
+
t.min he = t.min! :=
|
| 936 |
+
ExtTreeMap.minKey_eq_minKey!
|
| 937 |
+
|
| 938 |
+
theorem min!_empty [TransCmp cmp] [Inhabited α] :
|
| 939 |
+
(∅ : ExtTreeSet α cmp).min! = default :=
|
| 940 |
+
ExtTreeMap.minKey!_empty
|
| 941 |
+
|
| 942 |
+
theorem min!_eq_iff_get?_eq_self_and_forall [TransCmp cmp] [Inhabited α]
|
| 943 |
+
(he : t ≠ ∅) {km} :
|
| 944 |
+
t.min! = km ↔ t.get? km = some km ∧ ∀ k, k ∈ t → (cmp km k).isLE :=
|
| 945 |
+
ExtTreeMap.minKey!_eq_iff_getKey?_eq_self_and_forall (mt ext he)
|
| 946 |
+
|
| 947 |
+
theorem min!_eq_iff_mem_and_forall [TransCmp cmp] [LawfulEqCmp cmp] [Inhabited α]
|
| 948 |
+
(he : t ≠ ∅) {km} :
|
| 949 |
+
t.min! = km ↔ km ∈ t ∧ ∀ k, k ∈ t → (cmp km k).isLE :=
|
| 950 |
+
ExtTreeMap.minKey!_eq_iff_mem_and_forall (mt ext he)
|
| 951 |
+
|
| 952 |
+
@[grind =] theorem min!_insert [TransCmp cmp] [Inhabited α] {k} :
|
| 953 |
+
(t.insert k |>.min!) =
|
| 954 |
+
t.min?.elim k fun k' => if cmp k k' = .lt then k else k' :=
|
| 955 |
+
ExtTreeMap.minKey!_insertIfNew
|
| 956 |
+
|
| 957 |
+
theorem min!_insert_le_min! [TransCmp cmp] [Inhabited α] (he : t ≠ ∅) {k} :
|
| 958 |
+
cmp (t.insert k |>.min!) t.min! |>.isLE :=
|
| 959 |
+
ExtTreeMap.minKey!_insertIfNew_le_minKey! (mt ext he)
|
| 960 |
+
|
| 961 |
+
theorem min!_insert_le_self [TransCmp cmp] [Inhabited α] {k} :
|
| 962 |
+
cmp (t.insert k |>.min!) k |>.isLE :=
|
| 963 |
+
ExtTreeMap.minKey!_insertIfNew_le_self
|
| 964 |
+
|
| 965 |
+
@[grind =] theorem contains_min! [TransCmp cmp] [Inhabited α] (he : t ≠ ∅) :
|
| 966 |
+
t.contains t.min! :=
|
| 967 |
+
ExtTreeMap.contains_minKey! (mt ext he)
|
| 968 |
+
|
| 969 |
+
@[grind] theorem min!_mem [TransCmp cmp] [Inhabited α] (he : t ≠ ∅) :
|
| 970 |
+
t.min! ∈ t :=
|
| 971 |
+
ExtTreeMap.minKey!_mem (mt ext he)
|
| 972 |
+
|
| 973 |
+
theorem min!_le_of_contains [TransCmp cmp] [Inhabited α] {k} (hc : t.contains k) :
|
| 974 |
+
cmp t.min! k |>.isLE :=
|
| 975 |
+
ExtTreeMap.minKey!_le_of_contains hc
|
| 976 |
+
|
| 977 |
+
theorem min!_le_of_mem [TransCmp cmp] [Inhabited α] {k} (hc : k ∈ t) :
|
| 978 |
+
cmp t.min! k |>.isLE :=
|
| 979 |
+
ExtTreeMap.minKey!_le_of_mem hc
|
| 980 |
+
|
| 981 |
+
theorem le_min! [TransCmp cmp] [Inhabited α] (he : t ≠ ∅) {k} :
|
| 982 |
+
(cmp k t.min!).isLE ↔ (∀ k', k' ∈ t → (cmp k k').isLE) :=
|
| 983 |
+
ExtTreeMap.le_minKey! (mt ext he)
|
| 984 |
+
|
| 985 |
+
theorem get?_min! [TransCmp cmp] [Inhabited α] (he : t ≠ ∅) :
|
| 986 |
+
t.get? t.min! = some t.min! :=
|
| 987 |
+
ExtTreeMap.getKey?_minKey! (mt ext he)
|
| 988 |
+
|
| 989 |
+
@[grind =] theorem get_min! [TransCmp cmp] [Inhabited α] {hc} :
|
| 990 |
+
t.get t.min! hc = t.min! :=
|
| 991 |
+
ExtTreeMap.getKey_minKey!
|
| 992 |
+
|
| 993 |
+
@[simp, grind =]
|
| 994 |
+
theorem get_min!_eq_min [TransCmp cmp] [Inhabited α] {hc} :
|
| 995 |
+
t.get t.min! hc = t.min (ne_empty_of_mem hc) :=
|
| 996 |
+
ExtTreeMap.getKey_minKey!_eq_minKey
|
| 997 |
+
|
| 998 |
+
theorem get!_min! [TransCmp cmp] [Inhabited α] (he : t ≠ ∅) :
|
| 999 |
+
t.get! t.min! = t.min! :=
|
| 1000 |
+
ExtTreeMap.getKey!_minKey! (mt ext he)
|
| 1001 |
+
|
| 1002 |
+
theorem getD_min! [TransCmp cmp] [Inhabited α] (he : t ≠ ∅) {fallback} :
|
| 1003 |
+
t.getD t.min! fallback = t.min! :=
|
| 1004 |
+
ExtTreeMap.getKeyD_minKey! (mt ext he)
|
| 1005 |
+
|
| 1006 |
+
theorem min!_erase_eq_of_not_compare_min!_eq [TransCmp cmp] [Inhabited α] {k}
|
| 1007 |
+
(he : t.erase k ≠ ∅) (heq : ¬ cmp k t.min! = .eq) :
|
| 1008 |
+
(t.erase k |>.min!) = t.min! :=
|
| 1009 |
+
ExtTreeMap.minKey!_erase_eq_of_not_compare_minKey!_eq (mt ext he) heq
|
| 1010 |
+
|
| 1011 |
+
theorem min!_le_min!_erase [TransCmp cmp] [Inhabited α] {k}
|
| 1012 |
+
(he : t.erase k ≠ ∅) :
|
| 1013 |
+
cmp t.min! (t.erase k |>.min!) |>.isLE :=
|
| 1014 |
+
ExtTreeMap.minKey!_le_minKey!_erase (mt ext he)
|
| 1015 |
+
|
| 1016 |
+
theorem min!_eq_head!_toList [TransCmp cmp] [Inhabited α] :
|
| 1017 |
+
t.min! = t.toList.head! :=
|
| 1018 |
+
ExtTreeMap.minKey!_eq_head!_keys
|
| 1019 |
+
|
| 1020 |
+
theorem min?_eq_some_minD [TransCmp cmp] (he : t ≠ ∅) {fallback} :
|
| 1021 |
+
t.min? = some (t.minD fallback) :=
|
| 1022 |
+
ExtTreeMap.minKey?_eq_some_minKeyD (mt ext he)
|
| 1023 |
+
|
| 1024 |
+
theorem minD_empty [TransCmp cmp] {fallback} :
|
| 1025 |
+
(∅ : ExtTreeSet α cmp).minD fallback = fallback :=
|
| 1026 |
+
ExtTreeMap.minKeyD_empty
|
| 1027 |
+
|
| 1028 |
+
theorem min!_eq_minD_default [TransCmp cmp] [Inhabited α] :
|
| 1029 |
+
t.min! = t.minD default :=
|
| 1030 |
+
ExtTreeMap.minKey!_eq_minKeyD_default
|
| 1031 |
+
|
| 1032 |
+
theorem minD_eq_iff_get?_eq_self_and_forall [TransCmp cmp]
|
| 1033 |
+
(he : t ≠ ∅) {km fallback} :
|
| 1034 |
+
t.minD fallback = km ↔ t.get? km = some km ∧ ∀ k, k ∈ t → (cmp km k).isLE :=
|
| 1035 |
+
ExtTreeMap.minKeyD_eq_iff_getKey?_eq_self_and_forall (mt ext he)
|
| 1036 |
+
|
| 1037 |
+
theorem minD_eq_iff_mem_and_forall [TransCmp cmp] [LawfulEqCmp cmp]
|
| 1038 |
+
(he : t ≠ ∅) {km fallback} :
|
| 1039 |
+
t.minD fallback = km ↔ km ∈ t ∧ ∀ k, k ∈ t → (cmp km k).isLE :=
|
| 1040 |
+
ExtTreeMap.minKeyD_eq_iff_mem_and_forall (mt ext he)
|
| 1041 |
+
|
| 1042 |
+
@[grind =] theorem minD_insert [TransCmp cmp] {k fallback} :
|
| 1043 |
+
(t.insert k |>.minD fallback) =
|
| 1044 |
+
t.min?.elim k fun k' => if cmp k k' = .lt then k else k' :=
|
| 1045 |
+
ExtTreeMap.minKeyD_insertIfNew
|
| 1046 |
+
|
| 1047 |
+
theorem minD_insert_le_minD [TransCmp cmp]
|
| 1048 |
+
(he : t ≠ ∅) {k fallback} :
|
| 1049 |
+
cmp (t.insert k |>.minD fallback) (t.minD fallback) |>.isLE :=
|
| 1050 |
+
ExtTreeMap.minKeyD_insertIfNew_le_minKeyD (mt ext he)
|
| 1051 |
+
|
| 1052 |
+
theorem minD_insert_le_self [TransCmp cmp] {k fallback} :
|
| 1053 |
+
cmp (t.insert k |>.minD fallback) k |>.isLE :=
|
| 1054 |
+
ExtTreeMap.minKeyD_insertIfNew_le_self
|
| 1055 |
+
|
| 1056 |
+
@[grind =] theorem contains_minD [TransCmp cmp] (he : t ≠ ∅) {fallback} :
|
| 1057 |
+
t.contains (t.minD fallback) :=
|
| 1058 |
+
ExtTreeMap.contains_minKeyD (mt ext he)
|
| 1059 |
+
|
| 1060 |
+
@[grind] theorem minD_mem [TransCmp cmp] (he : t ≠ ∅) {fallback} :
|
| 1061 |
+
t.minD fallback ∈ t :=
|
| 1062 |
+
ExtTreeMap.minKeyD_mem (mt ext he)
|
| 1063 |
+
|
| 1064 |
+
theorem minD_le_of_contains [TransCmp cmp] {k} (hc : t.contains k) {fallback} :
|
| 1065 |
+
cmp (t.minD fallback) k |>.isLE :=
|
| 1066 |
+
ExtTreeMap.minKeyD_le_of_contains hc
|
| 1067 |
+
|
| 1068 |
+
theorem minD_le_of_mem [TransCmp cmp] {k} (hc : k ∈ t) {fallback} :
|
| 1069 |
+
cmp (t.minD fallback) k |>.isLE :=
|
| 1070 |
+
ExtTreeMap.minKeyD_le_of_mem hc
|
| 1071 |
+
|
| 1072 |
+
theorem le_minD [TransCmp cmp] (he : t ≠ ∅) {k fallback} :
|
| 1073 |
+
(cmp k (t.minD fallback)).isLE ↔ (∀ k', k' ∈ t → (cmp k k').isLE) :=
|
| 1074 |
+
ExtTreeMap.le_minKeyD (mt ext he)
|
| 1075 |
+
|
| 1076 |
+
theorem get?_minD [TransCmp cmp] (he : t ≠ ∅) {fallback} :
|
| 1077 |
+
t.get? (t.minD fallback) = some (t.minD fallback) :=
|
| 1078 |
+
ExtTreeMap.getKey?_minKeyD (mt ext he)
|
| 1079 |
+
|
| 1080 |
+
@[grind =] theorem get_minD [TransCmp cmp] {fallback hc} :
|
| 1081 |
+
t.get (t.minD fallback) hc = t.minD fallback :=
|
| 1082 |
+
ExtTreeMap.getKey_minKeyD
|
| 1083 |
+
|
| 1084 |
+
theorem get!_minD [TransCmp cmp] [Inhabited α] (he : t ≠ ∅) {fallback} :
|
| 1085 |
+
t.get! (t.minD fallback) = t.minD fallback :=
|
| 1086 |
+
ExtTreeMap.getKey!_minKeyD (mt ext he)
|
| 1087 |
+
|
| 1088 |
+
theorem getD_minD [TransCmp cmp] (he : t ≠ ∅) {fallback fallback'} :
|
| 1089 |
+
t.getD (t.minD fallback) fallback' = t.minD fallback :=
|
| 1090 |
+
ExtTreeMap.getKeyD_minKeyD (mt ext he)
|
| 1091 |
+
|
| 1092 |
+
theorem minD_erase_eq_of_not_compare_minD_eq [TransCmp cmp] {k fallback}
|
| 1093 |
+
(he : t.erase k ≠ ∅) (heq : ¬ cmp k (t.minD fallback) = .eq) :
|
| 1094 |
+
(t.erase k |>.minD fallback) = t.minD fallback :=
|
| 1095 |
+
ExtTreeMap.minKeyD_erase_eq_of_not_compare_minKeyD_eq (mt ext he) heq
|
| 1096 |
+
|
| 1097 |
+
theorem minD_le_minD_erase [TransCmp cmp] {k}
|
| 1098 |
+
(he : t.erase k ≠ ∅) {fallback} :
|
| 1099 |
+
cmp (t.minD fallback) (t.erase k |>.minD fallback) |>.isLE :=
|
| 1100 |
+
ExtTreeMap.minKeyD_le_minKeyD_erase (mt ext he)
|
| 1101 |
+
|
| 1102 |
+
theorem minD_eq_headD_toList [TransCmp cmp] {fallback} :
|
| 1103 |
+
t.minD fallback = t.toList.headD fallback :=
|
| 1104 |
+
ExtTreeMap.minKeyD_eq_headD_keys
|
| 1105 |
+
|
| 1106 |
+
end Min
|
| 1107 |
+
|
| 1108 |
+
section Max
|
| 1109 |
+
|
| 1110 |
+
@[simp, grind =]
|
| 1111 |
+
theorem max?_empty [TransCmp cmp] :
|
| 1112 |
+
(∅ : ExtTreeSet α cmp).max? = none :=
|
| 1113 |
+
ExtTreeMap.maxKey?_empty
|
| 1114 |
+
|
| 1115 |
+
@[simp, grind =]
|
| 1116 |
+
theorem max?_eq_none_iff [TransCmp cmp] :
|
| 1117 |
+
t.max? = none ↔ t = ∅ :=
|
| 1118 |
+
ExtTreeMap.maxKey?_eq_none_iff.trans ext_iff.symm
|
| 1119 |
+
|
| 1120 |
+
theorem max?_eq_some_iff_get?_eq_self_and_forall [TransCmp cmp] {km} :
|
| 1121 |
+
t.max? = some km ↔ t.get? km = some km ∧ ∀ k ∈ t, (cmp k km).isLE :=
|
| 1122 |
+
ExtTreeMap.maxKey?_eq_some_iff_getKey?_eq_self_and_forall
|
| 1123 |
+
|
| 1124 |
+
theorem max?_eq_some_iff_mem_and_forall [TransCmp cmp] [LawfulEqCmp cmp] {km} :
|
| 1125 |
+
t.max? = some km ↔ km ∈ t ∧ ∀ k ∈ t, (cmp k km).isLE :=
|
| 1126 |
+
ExtTreeMap.maxKey?_eq_some_iff_mem_and_forall
|
| 1127 |
+
|
| 1128 |
+
@[simp, grind =]
|
| 1129 |
+
theorem isNone_max?_eq_isEmpty [TransCmp cmp] :
|
| 1130 |
+
t.max?.isNone = t.isEmpty :=
|
| 1131 |
+
ExtTreeMap.isNone_maxKey?_eq_isEmpty
|
| 1132 |
+
|
| 1133 |
+
@[simp, grind =]
|
| 1134 |
+
theorem isSome_max?_eq_not_isEmpty [TransCmp cmp] :
|
| 1135 |
+
t.max?.isSome = !t.isEmpty :=
|
| 1136 |
+
ExtTreeMap.isSome_maxKey?_eq_not_isEmpty
|
| 1137 |
+
|
| 1138 |
+
theorem isSome_max?_iff_ne_empty [TransCmp cmp] :
|
| 1139 |
+
t.max?.isSome ↔ t ≠ ∅ :=
|
| 1140 |
+
ExtTreeMap.isSome_maxKey?_iff_ne_empty.trans (not_congr ext_iff).symm
|
| 1141 |
+
|
| 1142 |
+
@[grind =] theorem max?_insert [TransCmp cmp] {k} :
|
| 1143 |
+
(t.insert k).max? =
|
| 1144 |
+
some (t.max?.elim k fun k' => if cmp k' k = .lt then k else k') :=
|
| 1145 |
+
ExtTreeMap.maxKey?_insertIfNew
|
| 1146 |
+
|
| 1147 |
+
theorem isSome_max?_insert [TransCmp cmp] {k} :
|
| 1148 |
+
(t.insert k).max?.isSome :=
|
| 1149 |
+
ExtTreeMap.isSome_maxKey?_insertIfNew
|
| 1150 |
+
|
| 1151 |
+
theorem max?_le_max?_insert [TransCmp cmp] {k km kmi} :
|
| 1152 |
+
(hkm : t.max? = some km) →
|
| 1153 |
+
(hkmi : (t.insert k |>.max? |>.get isSome_max?_insert) = kmi) →
|
| 1154 |
+
cmp km kmi |>.isLE :=
|
| 1155 |
+
ExtTreeMap.maxKey?_le_maxKey?_insertIfNew
|
| 1156 |
+
|
| 1157 |
+
theorem self_le_max?_insert [TransCmp cmp] {k kmi} :
|
| 1158 |
+
(hkmi : (t.insert k |>.max?.get isSome_max?_insert) = kmi) →
|
| 1159 |
+
cmp k kmi |>.isLE :=
|
| 1160 |
+
ExtTreeMap.self_le_maxKey?_insertIfNew
|
| 1161 |
+
|
| 1162 |
+
theorem contains_max? [TransCmp cmp] {km} :
|
| 1163 |
+
(hkm : t.max? = some km) → t.contains km :=
|
| 1164 |
+
ExtTreeMap.contains_maxKey?
|
| 1165 |
+
|
| 1166 |
+
theorem max?_mem [TransCmp cmp] {km} :
|
| 1167 |
+
(hkm : t.max? = some km) → km ∈ t :=
|
| 1168 |
+
ExtTreeMap.maxKey?_mem
|
| 1169 |
+
|
| 1170 |
+
theorem isSome_max?_of_contains [TransCmp cmp] {k} :
|
| 1171 |
+
(hc : t.contains k) → t.max?.isSome :=
|
| 1172 |
+
ExtTreeMap.isSome_maxKey?_of_contains
|
| 1173 |
+
|
| 1174 |
+
theorem isSome_max?_of_mem [TransCmp cmp] {k} :
|
| 1175 |
+
k ∈ t → t.max?.isSome :=
|
| 1176 |
+
ExtTreeMap.isSome_maxKey?_of_mem
|
| 1177 |
+
|
| 1178 |
+
theorem le_max?_of_contains [TransCmp cmp] {k km} :
|
| 1179 |
+
(hc : t.contains k) → (hkm : (t.max?.get <| isSome_max?_of_contains hc) = km) →
|
| 1180 |
+
cmp k km |>.isLE :=
|
| 1181 |
+
ExtTreeMap.le_maxKey?_of_contains
|
| 1182 |
+
|
| 1183 |
+
theorem le_max?_of_mem [TransCmp cmp] {k km} :
|
| 1184 |
+
(hc : k ∈ t) → (hkm : (t.max?.get <| isSome_max?_of_mem hc) = km) →
|
| 1185 |
+
cmp k km |>.isLE :=
|
| 1186 |
+
ExtTreeMap.le_maxKey?_of_mem
|
| 1187 |
+
|
| 1188 |
+
theorem max?_le [TransCmp cmp] {k} :
|
| 1189 |
+
(∀ k', t.max? = some k' → (cmp k' k).isLE) ↔ (∀ k', k' ∈ t → (cmp k' k).isLE) :=
|
| 1190 |
+
ExtTreeMap.maxKey?_le
|
| 1191 |
+
|
| 1192 |
+
theorem get?_max? [TransCmp cmp] {km} :
|
| 1193 |
+
(hkm : t.max? = some km) → t.get? km = some km :=
|
| 1194 |
+
ExtTreeMap.getKey?_maxKey?
|
| 1195 |
+
|
| 1196 |
+
theorem get_max? [TransCmp cmp] {km hc} :
|
| 1197 |
+
(hkm : t.max?.get (isSome_max?_of_contains hc) = km) → t.get km hc = km :=
|
| 1198 |
+
ExtTreeMap.getKey_maxKey?
|
| 1199 |
+
|
| 1200 |
+
theorem get!_max? [TransCmp cmp] [Inhabited α] {km} :
|
| 1201 |
+
(hkm : t.max? = some km) → t.get! km = km :=
|
| 1202 |
+
ExtTreeMap.getKey!_maxKey?
|
| 1203 |
+
|
| 1204 |
+
theorem getD_max? [TransCmp cmp] {km fallback} :
|
| 1205 |
+
(hkm : t.max? = some km) → t.getD km fallback = km :=
|
| 1206 |
+
ExtTreeMap.getKeyD_maxKey?
|
| 1207 |
+
|
| 1208 |
+
@[simp]
|
| 1209 |
+
theorem max?_bind_get? [TransCmp cmp] :
|
| 1210 |
+
t.max?.bind t.get? = t.max? :=
|
| 1211 |
+
ExtTreeMap.maxKey?_bind_getKey?
|
| 1212 |
+
|
| 1213 |
+
theorem max?_erase_eq_iff_not_compare_eq_max? [TransCmp cmp] {k} :
|
| 1214 |
+
(t.erase k |>.max?) = t.max? ↔
|
| 1215 |
+
∀ {km}, t.max? = some km → ¬ cmp k km = .eq :=
|
| 1216 |
+
ExtTreeMap.maxKey?_erase_eq_iff_not_compare_eq_maxKey?
|
| 1217 |
+
|
| 1218 |
+
theorem max?_erase_eq_of_not_compare_eq_max? [TransCmp cmp] {k} :
|
| 1219 |
+
(hc : ∀ {km}, t.max? = some km → ¬ cmp k km = .eq) →
|
| 1220 |
+
(t.erase k |>.max?) = t.max? :=
|
| 1221 |
+
ExtTreeMap.maxKey?_erase_eq_of_not_compare_eq_maxKey?
|
| 1222 |
+
|
| 1223 |
+
theorem isSome_max?_of_isSome_max?_erase [TransCmp cmp] {k} :
|
| 1224 |
+
(hs : t.erase k |>.max?.isSome) →
|
| 1225 |
+
t.max?.isSome :=
|
| 1226 |
+
ExtTreeMap.isSome_maxKey?_of_isSome_maxKey?_erase
|
| 1227 |
+
|
| 1228 |
+
theorem max?_erase_le_max? [TransCmp cmp] {k km kme} :
|
| 1229 |
+
(hkme : (t.erase k |>.max?) = some kme) →
|
| 1230 |
+
(hkm : (t.max?.get <|
|
| 1231 |
+
isSome_max?_of_isSome_max?_erase <| hkme ▸ Option.isSome_some) = km) →
|
| 1232 |
+
cmp kme km |>.isLE :=
|
| 1233 |
+
ExtTreeMap.maxKey?_erase_le_maxKey?
|
| 1234 |
+
|
| 1235 |
+
theorem max?_eq_getLast?_toList [TransCmp cmp] :
|
| 1236 |
+
t.max? = t.toList.getLast? :=
|
| 1237 |
+
ExtTreeMap.maxKey?_eq_getLast?_keys
|
| 1238 |
+
|
| 1239 |
+
theorem max_eq_get_max? [TransCmp cmp] {he} :
|
| 1240 |
+
t.max he = t.max?.get (isSome_max?_iff_ne_empty.mpr he) :=
|
| 1241 |
+
ExtTreeMap.maxKey_eq_get_maxKey?
|
| 1242 |
+
|
| 1243 |
+
theorem max?_eq_some_max [TransCmp cmp] (he) :
|
| 1244 |
+
t.max? = some (t.max he) :=
|
| 1245 |
+
ExtTreeMap.maxKey?_eq_some_maxKey (mt ext he)
|
| 1246 |
+
|
| 1247 |
+
theorem max_eq_iff_get?_eq_self_and_forall [TransCmp cmp] {he km} :
|
| 1248 |
+
t.max he = km ↔ t.get? km = some km ∧ ∀ k ∈ t, (cmp k km).isLE :=
|
| 1249 |
+
ExtTreeMap.maxKey_eq_iff_getKey?_eq_self_and_forall
|
| 1250 |
+
|
| 1251 |
+
theorem max_eq_iff_mem_and_forall [TransCmp cmp] [LawfulEqCmp cmp] {he km} :
|
| 1252 |
+
t.max he = km ↔ km ∈ t ∧ ∀ k ∈ t, (cmp k km).isLE :=
|
| 1253 |
+
ExtTreeMap.maxKey_eq_iff_mem_and_forall
|
| 1254 |
+
|
| 1255 |
+
@[grind =] theorem max_insert [TransCmp cmp] {k} :
|
| 1256 |
+
(t.insert k).max insert_ne_empty =
|
| 1257 |
+
t.max?.elim k fun k' => if cmp k' k = .lt then k else k' :=
|
| 1258 |
+
ExtTreeMap.maxKey_insertIfNew
|
| 1259 |
+
|
| 1260 |
+
theorem max_le_max_insert [TransCmp cmp] {k he} :
|
| 1261 |
+
cmp (t.max he)
|
| 1262 |
+
(t.insert k |>.max insert_ne_empty) |>.isLE :=
|
| 1263 |
+
ExtTreeMap.maxKey_le_maxKey_insertIfNew
|
| 1264 |
+
|
| 1265 |
+
theorem self_le_max_insert [TransCmp cmp] {k} :
|
| 1266 |
+
cmp k (t.insert k |>.max <| insert_ne_empty) |>.isLE :=
|
| 1267 |
+
ExtTreeMap.self_le_maxKey_insertIfNew
|
| 1268 |
+
|
| 1269 |
+
@[grind =] theorem contains_max [TransCmp cmp] {he} :
|
| 1270 |
+
t.contains (t.max he) :=
|
| 1271 |
+
ExtTreeMap.contains_maxKey
|
| 1272 |
+
|
| 1273 |
+
@[grind] theorem max_mem [TransCmp cmp] {he} :
|
| 1274 |
+
t.max he ∈ t :=
|
| 1275 |
+
ExtTreeMap.maxKey_mem
|
| 1276 |
+
|
| 1277 |
+
theorem le_max_of_contains [TransCmp cmp] {k} (hc : t.contains k) :
|
| 1278 |
+
cmp k (t.max (ne_empty_of_mem hc)) |>.isLE :=
|
| 1279 |
+
ExtTreeMap.le_maxKey_of_contains hc
|
| 1280 |
+
|
| 1281 |
+
theorem le_max_of_mem [TransCmp cmp] {k} (hc : k ∈ t) :
|
| 1282 |
+
cmp k (t.max (ne_empty_of_mem hc)) |>.isLE :=
|
| 1283 |
+
ExtTreeMap.le_maxKey_of_mem hc
|
| 1284 |
+
|
| 1285 |
+
theorem max_le [TransCmp cmp] {k he} :
|
| 1286 |
+
(cmp (t.max he) k).isLE ↔ (∀ k', k' ∈ t → (cmp k' k).isLE) :=
|
| 1287 |
+
ExtTreeMap.maxKey_le
|
| 1288 |
+
|
| 1289 |
+
@[simp, grind =]
|
| 1290 |
+
theorem get?_max [TransCmp cmp] {he} :
|
| 1291 |
+
t.get? (t.max he) = some (t.max he) :=
|
| 1292 |
+
ExtTreeMap.getKey?_maxKey
|
| 1293 |
+
|
| 1294 |
+
@[simp, grind =]
|
| 1295 |
+
theorem get_max [TransCmp cmp] {he hc} :
|
| 1296 |
+
t.get (t.max he) hc = t.max he :=
|
| 1297 |
+
ExtTreeMap.getKey_maxKey
|
| 1298 |
+
|
| 1299 |
+
@[simp, grind =]
|
| 1300 |
+
theorem get!_max [TransCmp cmp] [Inhabited α] {he} :
|
| 1301 |
+
t.get! (t.max he) = t.max he :=
|
| 1302 |
+
ExtTreeMap.getKey!_maxKey
|
| 1303 |
+
|
| 1304 |
+
@[simp, grind =]
|
| 1305 |
+
theorem getD_max [TransCmp cmp] {he fallback} :
|
| 1306 |
+
t.getD (t.max he) fallback = t.max he :=
|
| 1307 |
+
ExtTreeMap.getKeyD_maxKey
|
| 1308 |
+
|
| 1309 |
+
@[simp]
|
| 1310 |
+
theorem max_erase_eq_iff_not_compare_eq_max [TransCmp cmp] {k he} :
|
| 1311 |
+
(t.erase k |>.max he) =
|
| 1312 |
+
t.max (ne_empty_of_erase_ne_empty he) ↔
|
| 1313 |
+
¬ cmp k (t.max <| ne_empty_of_erase_ne_empty he) = .eq :=
|
| 1314 |
+
ExtTreeMap.maxKey_erase_eq_iff_not_compare_eq_maxKey
|
| 1315 |
+
|
| 1316 |
+
theorem max_erase_eq_of_not_compare_eq_max [TransCmp cmp] {k he} :
|
| 1317 |
+
(hc : ¬ cmp k (t.max (ne_empty_of_erase_ne_empty he)) = .eq) →
|
| 1318 |
+
(t.erase k |>.max he) =
|
| 1319 |
+
t.max (ne_empty_of_erase_ne_empty he) :=
|
| 1320 |
+
ExtTreeMap.maxKey_erase_eq_of_not_compare_eq_maxKey
|
| 1321 |
+
|
| 1322 |
+
theorem max_erase_le_max [TransCmp cmp] {k he} :
|
| 1323 |
+
cmp (t.erase k |>.max he)
|
| 1324 |
+
(t.max <| ne_empty_of_erase_ne_empty he) |>.isLE :=
|
| 1325 |
+
ExtTreeMap.maxKey_erase_le_maxKey
|
| 1326 |
+
|
| 1327 |
+
@[grind =_]
|
| 1328 |
+
theorem max_eq_getLast_toList [TransCmp cmp] {he} :
|
| 1329 |
+
t.max he = t.toList.getLast (mt toList_eq_nil_iff.mp he) :=
|
| 1330 |
+
ExtTreeMap.maxKey_eq_getLast_keys
|
| 1331 |
+
|
| 1332 |
+
theorem max?_eq_some_max! [TransCmp cmp] [Inhabited α] (he : t ≠ ∅) :
|
| 1333 |
+
t.max? = some t.max! :=
|
| 1334 |
+
ExtTreeMap.maxKey?_eq_some_maxKey! (mt ext he)
|
| 1335 |
+
|
| 1336 |
+
theorem max_eq_max! [TransCmp cmp] [Inhabited α] {he : t ≠ ∅} :
|
| 1337 |
+
t.max he = t.max! :=
|
| 1338 |
+
ExtTreeMap.maxKey_eq_maxKey!
|
| 1339 |
+
|
| 1340 |
+
theorem max!_empty [TransCmp cmp] [Inhabited α] :
|
| 1341 |
+
(∅ : ExtTreeSet α cmp).max! = default :=
|
| 1342 |
+
ExtTreeMap.maxKey!_empty
|
| 1343 |
+
|
| 1344 |
+
theorem max!_eq_iff_get?_eq_self_and_forall [TransCmp cmp] [Inhabited α]
|
| 1345 |
+
(he : t ≠ ∅) {km} :
|
| 1346 |
+
t.max! = km ↔ t.get? km = some km ∧ ∀ k, k ∈ t → (cmp k km).isLE :=
|
| 1347 |
+
ExtTreeMap.maxKey!_eq_iff_getKey?_eq_self_and_forall (mt ext he)
|
| 1348 |
+
|
| 1349 |
+
theorem max!_eq_iff_mem_and_forall [TransCmp cmp] [LawfulEqCmp cmp] [Inhabited α]
|
| 1350 |
+
(he : t ≠ ∅) {km} :
|
| 1351 |
+
t.max! = km ↔ km ∈ t ∧ ∀ k, k ∈ t → (cmp k km).isLE :=
|
| 1352 |
+
ExtTreeMap.maxKey!_eq_iff_mem_and_forall (mt ext he)
|
| 1353 |
+
|
| 1354 |
+
@[grind =] theorem max!_insert [TransCmp cmp] [Inhabited α] {k} :
|
| 1355 |
+
(t.insert k |>.max!) =
|
| 1356 |
+
t.max?.elim k fun k' => if cmp k' k = .lt then k else k' :=
|
| 1357 |
+
ExtTreeMap.maxKey!_insertIfNew
|
| 1358 |
+
|
| 1359 |
+
theorem max!_le_max!_insert [TransCmp cmp] [Inhabited α] (he : t ≠ ∅) {k} :
|
| 1360 |
+
cmp t.max! (t.insert k |>.max!) |>.isLE :=
|
| 1361 |
+
ExtTreeMap.maxKey!_le_maxKey!_insertIfNew (mt ext he)
|
| 1362 |
+
|
| 1363 |
+
theorem self_le_max!_insert [TransCmp cmp] [Inhabited α] {k} :
|
| 1364 |
+
cmp k (t.insert k |>.max!) |>.isLE :=
|
| 1365 |
+
ExtTreeMap.self_le_maxKey!_insertIfNew
|
| 1366 |
+
|
| 1367 |
+
@[grind =] theorem contains_max! [TransCmp cmp] [Inhabited α] (he : t ≠ ∅) :
|
| 1368 |
+
t.contains t.max! :=
|
| 1369 |
+
ExtTreeMap.contains_maxKey! (mt ext he)
|
| 1370 |
+
|
| 1371 |
+
@[grind] theorem max!_mem [TransCmp cmp] [Inhabited α] (he : t ≠ ∅) :
|
| 1372 |
+
t.max! ∈ t :=
|
| 1373 |
+
ExtTreeMap.maxKey!_mem (mt ext he)
|
| 1374 |
+
|
| 1375 |
+
theorem le_max!_of_contains [TransCmp cmp] [Inhabited α] {k} (hc : t.contains k) :
|
| 1376 |
+
cmp k t.max! |>.isLE :=
|
| 1377 |
+
ExtTreeMap.le_maxKey!_of_contains hc
|
| 1378 |
+
|
| 1379 |
+
theorem le_max!_of_mem [TransCmp cmp] [Inhabited α] {k} (hc : k ∈ t) :
|
| 1380 |
+
cmp k t.max! |>.isLE :=
|
| 1381 |
+
ExtTreeMap.le_maxKey!_of_mem hc
|
| 1382 |
+
|
| 1383 |
+
theorem max!_le [TransCmp cmp] [Inhabited α] (he : t ≠ ∅) {k} :
|
| 1384 |
+
(cmp t.max! k).isLE ↔ (∀ k', k' ∈ t → (cmp k' k).isLE) :=
|
| 1385 |
+
ExtTreeMap.maxKey!_le (mt ext he)
|
| 1386 |
+
|
| 1387 |
+
theorem get?_max! [TransCmp cmp] [Inhabited α] (he : t ≠ ∅) :
|
| 1388 |
+
t.get? t.max! = some t.max! :=
|
| 1389 |
+
ExtTreeMap.getKey?_maxKey! (mt ext he)
|
| 1390 |
+
|
| 1391 |
+
@[grind =] theorem get_max! [TransCmp cmp] [Inhabited α] {hc} :
|
| 1392 |
+
t.get t.max! hc = t.max! :=
|
| 1393 |
+
ExtTreeMap.getKey_maxKey!
|
| 1394 |
+
|
| 1395 |
+
@[simp, grind =]
|
| 1396 |
+
theorem get_max!_eq_max [TransCmp cmp] [Inhabited α] {hc} :
|
| 1397 |
+
t.get t.max! hc = t.max (ne_empty_of_mem hc) :=
|
| 1398 |
+
ExtTreeMap.getKey_maxKey!_eq_maxKey
|
| 1399 |
+
|
| 1400 |
+
theorem get!_max! [TransCmp cmp] [Inhabited α] (he : t ≠ ∅) :
|
| 1401 |
+
t.get! t.max! = t.max! :=
|
| 1402 |
+
ExtTreeMap.getKey!_maxKey! (mt ext he)
|
| 1403 |
+
|
| 1404 |
+
theorem getD_max! [TransCmp cmp] [Inhabited α] (he : t ≠ ∅) {fallback} :
|
| 1405 |
+
t.getD t.max! fallback = t.max! :=
|
| 1406 |
+
ExtTreeMap.getKeyD_maxKey! (mt ext he)
|
| 1407 |
+
|
| 1408 |
+
theorem max!_erase_eq_of_not_compare_max!_eq [TransCmp cmp] [Inhabited α] {k}
|
| 1409 |
+
(he : t.erase k ≠ ∅) (heq : ¬ cmp k t.max! = .eq) :
|
| 1410 |
+
(t.erase k |>.max!) = t.max! :=
|
| 1411 |
+
ExtTreeMap.maxKey!_erase_eq_of_not_compare_maxKey!_eq (mt ext he) heq
|
| 1412 |
+
|
| 1413 |
+
theorem max!_erase_le_max! [TransCmp cmp] [Inhabited α] {k}
|
| 1414 |
+
(he : t.erase k ≠ ∅) :
|
| 1415 |
+
cmp (t.erase k |>.max!) t.max! |>.isLE :=
|
| 1416 |
+
ExtTreeMap.maxKey!_erase_le_maxKey! (mt ext he)
|
| 1417 |
+
|
| 1418 |
+
@[grind =_]
|
| 1419 |
+
theorem max!_eq_getLast!_toList [TransCmp cmp] [Inhabited α] :
|
| 1420 |
+
t.max! = t.toList.getLast! :=
|
| 1421 |
+
ExtTreeMap.maxKey!_eq_getLast!_keys
|
| 1422 |
+
|
| 1423 |
+
theorem max?_eq_some_maxD [TransCmp cmp] (he : t ≠ ∅) {fallback} :
|
| 1424 |
+
t.max? = some (t.maxD fallback) :=
|
| 1425 |
+
ExtTreeMap.maxKey?_eq_some_maxKeyD (mt ext he)
|
| 1426 |
+
|
| 1427 |
+
theorem maxD_empty [TransCmp cmp] {fallback} :
|
| 1428 |
+
(∅ : ExtTreeSet α cmp).maxD fallback = fallback :=
|
| 1429 |
+
ExtTreeMap.maxKeyD_empty
|
| 1430 |
+
|
| 1431 |
+
theorem max!_eq_maxD_default [TransCmp cmp] [Inhabited α] :
|
| 1432 |
+
t.max! = t.maxD default :=
|
| 1433 |
+
ExtTreeMap.maxKey!_eq_maxKeyD_default
|
| 1434 |
+
|
| 1435 |
+
theorem maxD_eq_iff_get?_eq_self_and_forall [TransCmp cmp]
|
| 1436 |
+
(he : t ≠ ∅) {km fallback} :
|
| 1437 |
+
t.maxD fallback = km ↔ t.get? km = some km ∧ ∀ k, k ∈ t → (cmp k km).isLE :=
|
| 1438 |
+
ExtTreeMap.maxKeyD_eq_iff_getKey?_eq_self_and_forall (mt ext he)
|
| 1439 |
+
|
| 1440 |
+
theorem maxD_eq_iff_mem_and_forall [TransCmp cmp] [LawfulEqCmp cmp]
|
| 1441 |
+
(he : t ≠ ∅) {km fallback} :
|
| 1442 |
+
t.maxD fallback = km ↔ km ∈ t ∧ ∀ k, k ∈ t → (cmp k km).isLE :=
|
| 1443 |
+
ExtTreeMap.maxKeyD_eq_iff_mem_and_forall (mt ext he)
|
| 1444 |
+
|
| 1445 |
+
@[grind =] theorem maxD_insert [TransCmp cmp] {k fallback} :
|
| 1446 |
+
(t.insert k |>.maxD fallback) =
|
| 1447 |
+
t.max?.elim k fun k' => if cmp k' k = .lt then k else k' :=
|
| 1448 |
+
ExtTreeMap.maxKeyD_insertIfNew
|
| 1449 |
+
|
| 1450 |
+
theorem maxD_le_maxD_insert [TransCmp cmp]
|
| 1451 |
+
(he : t ≠ ∅) {k fallback} :
|
| 1452 |
+
cmp (t.maxD fallback) (t.insert k |>.maxD fallback) |>.isLE :=
|
| 1453 |
+
ExtTreeMap.maxKeyD_le_maxKeyD_insertIfNew (mt ext he)
|
| 1454 |
+
|
| 1455 |
+
theorem self_le_maxD_insert [TransCmp cmp] {k fallback} :
|
| 1456 |
+
cmp k (t.insert k |>.maxD fallback) |>.isLE :=
|
| 1457 |
+
ExtTreeMap.self_le_maxKeyD_insertIfNew
|
| 1458 |
+
|
| 1459 |
+
@[grind =] theorem contains_maxD [TransCmp cmp] (he : t ≠ ∅) {fallback} :
|
| 1460 |
+
t.contains (t.maxD fallback) :=
|
| 1461 |
+
ExtTreeMap.contains_maxKeyD (mt ext he)
|
| 1462 |
+
|
| 1463 |
+
@[grind] theorem maxD_mem [TransCmp cmp] (he : t ≠ ∅) {fallback} :
|
| 1464 |
+
t.maxD fallback ∈ t :=
|
| 1465 |
+
ExtTreeMap.maxKeyD_mem (mt ext he)
|
| 1466 |
+
|
| 1467 |
+
theorem le_maxD_of_contains [TransCmp cmp] {k} (hc : t.contains k) {fallback} :
|
| 1468 |
+
cmp k (t.maxD fallback) |>.isLE :=
|
| 1469 |
+
ExtTreeMap.le_maxKeyD_of_contains hc
|
| 1470 |
+
|
| 1471 |
+
theorem le_maxD_of_mem [TransCmp cmp] {k} (hc : k ∈ t) {fallback} :
|
| 1472 |
+
cmp k (t.maxD fallback) |>.isLE :=
|
| 1473 |
+
ExtTreeMap.le_maxKeyD_of_mem hc
|
| 1474 |
+
|
| 1475 |
+
theorem maxD_le [TransCmp cmp] (he : t ≠ ∅) {k fallback} :
|
| 1476 |
+
(cmp (t.maxD fallback) k).isLE ↔ (∀ k', k' ∈ t → (cmp k' k).isLE) :=
|
| 1477 |
+
ExtTreeMap.maxKeyD_le (mt ext he)
|
| 1478 |
+
|
| 1479 |
+
theorem get?_maxD [TransCmp cmp] (he : t ≠ ∅) {fallback} :
|
| 1480 |
+
t.get? (t.maxD fallback) = some (t.maxD fallback) :=
|
| 1481 |
+
ExtTreeMap.getKey?_maxKeyD (mt ext he)
|
| 1482 |
+
|
| 1483 |
+
@[grind =] theorem get_maxD [TransCmp cmp] {fallback hc} :
|
| 1484 |
+
t.get (t.maxD fallback) hc = t.maxD fallback :=
|
| 1485 |
+
ExtTreeMap.getKey_maxKeyD
|
| 1486 |
+
|
| 1487 |
+
theorem get!_maxD [TransCmp cmp] [Inhabited α] (he : t ≠ ∅) {fallback} :
|
| 1488 |
+
t.get! (t.maxD fallback) = t.maxD fallback :=
|
| 1489 |
+
ExtTreeMap.getKey!_maxKeyD (mt ext he)
|
| 1490 |
+
|
| 1491 |
+
theorem getD_maxD [TransCmp cmp] (he : t ≠ ∅) {fallback fallback'} :
|
| 1492 |
+
t.getD (t.maxD fallback) fallback' = t.maxD fallback :=
|
| 1493 |
+
ExtTreeMap.getKeyD_maxKeyD (mt ext he)
|
| 1494 |
+
|
| 1495 |
+
theorem maxD_erase_eq_of_not_compare_maxD_eq [TransCmp cmp] {k fallback}
|
| 1496 |
+
(he : t.erase k ≠ ∅) (heq : ¬ cmp k (t.maxD fallback) = .eq) :
|
| 1497 |
+
(t.erase k |>.maxD fallback) = t.maxD fallback :=
|
| 1498 |
+
ExtTreeMap.maxKeyD_erase_eq_of_not_compare_maxKeyD_eq (mt ext he) heq
|
| 1499 |
+
|
| 1500 |
+
theorem maxD_erase_le_maxD [TransCmp cmp] {k}
|
| 1501 |
+
(he : t.erase k ≠ ∅) {fallback} :
|
| 1502 |
+
cmp (t.erase k |>.maxD fallback) (t.maxD fallback) |>.isLE :=
|
| 1503 |
+
ExtTreeMap.maxKeyD_erase_le_maxKeyD (mt ext he)
|
| 1504 |
+
|
| 1505 |
+
theorem maxD_eq_getLastD_toList [TransCmp cmp] {fallback} :
|
| 1506 |
+
t.maxD fallback = t.toList.getLastD fallback :=
|
| 1507 |
+
ExtTreeMap.maxKeyD_eq_getLastD_keys
|
| 1508 |
+
|
| 1509 |
+
end Max
|
| 1510 |
+
|
| 1511 |
+
section Ext
|
| 1512 |
+
|
| 1513 |
+
variable {t₁ t₂ : ExtTreeSet α cmp}
|
| 1514 |
+
|
| 1515 |
+
@[ext 900]
|
| 1516 |
+
theorem ext_get? [TransCmp cmp] {t₁ t₂ : ExtTreeSet α cmp}
|
| 1517 |
+
(h : ∀ k, t₁.get? k = t₂.get? k) : t₁ = t₂ :=
|
| 1518 |
+
ext (ExtTreeMap.ext_getKey?_unit h)
|
| 1519 |
+
|
| 1520 |
+
theorem ext_contains [TransCmp cmp] [LawfulEqCmp cmp]
|
| 1521 |
+
{t₁ t₂ : ExtTreeSet α cmp}
|
| 1522 |
+
(h : ∀ k, t₁.contains k = t₂.contains k) : t₁ = t₂ :=
|
| 1523 |
+
ext (ExtTreeMap.ext_contains_unit h)
|
| 1524 |
+
|
| 1525 |
+
@[ext]
|
| 1526 |
+
theorem ext_mem [TransCmp cmp] [LawfulEqCmp cmp]
|
| 1527 |
+
{t₁ t₂ : ExtTreeSet α cmp}
|
| 1528 |
+
(h : ∀ k, k ∈ t₁ ↔ k ∈ t₂) : t₁ = t₂ :=
|
| 1529 |
+
ext (ExtTreeMap.ext_mem_unit h)
|
| 1530 |
+
|
| 1531 |
+
theorem ext_toList [TransCmp cmp] (h : t₁.toList.Perm t₂.toList) : t₁ = t₂ :=
|
| 1532 |
+
ext (ExtTreeMap.ext_keys_unit h)
|
| 1533 |
+
|
| 1534 |
+
theorem toList_inj [TransCmp cmp] : t₁.toList = t₂.toList ↔ t₁ = t₂ :=
|
| 1535 |
+
⟨ext_toList ∘ .of_eq, fun h => h ▸ rfl⟩
|
| 1536 |
+
|
| 1537 |
+
end Ext
|
| 1538 |
+
|
| 1539 |
+
end Std.ExtTreeSet
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Std/Data/HashMap/AdditionalOperations.lean
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Markus Himmel
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Std.Data.DHashMap.AdditionalOperations
|
| 8 |
+
import Std.Data.HashMap.Basic
|
| 9 |
+
import Std.Data.HashMap.Raw
|
| 10 |
+
|
| 11 |
+
/-!
|
| 12 |
+
# Additional hash map operations
|
| 13 |
+
|
| 14 |
+
This module defines the operations `map` and `filterMap` on `Std.HashMap`.
|
| 15 |
+
Lemmas about these operations are found in `Std.Data.HashMap.Lemmas`.
|
| 16 |
+
-/
|
| 17 |
+
|
| 18 |
+
set_option linter.missingDocs true
|
| 19 |
+
set_option autoImplicit false
|
| 20 |
+
|
| 21 |
+
universe u v w
|
| 22 |
+
|
| 23 |
+
variable {α : Type u} {β : Type v} {γ : Type w}
|
| 24 |
+
|
| 25 |
+
namespace Std
|
| 26 |
+
|
| 27 |
+
namespace HashMap
|
| 28 |
+
|
| 29 |
+
namespace Raw
|
| 30 |
+
|
| 31 |
+
theorem WF.filterMap [BEq α] [Hashable α] {m : Raw α β} {f : α → β → Option γ} (h : m.WF) :
|
| 32 |
+
(m.filterMap f).WF :=
|
| 33 |
+
⟨DHashMap.Raw.WF.filterMap h.out⟩
|
| 34 |
+
|
| 35 |
+
theorem WF.map [BEq α] [Hashable α] {m : Raw α β} {f : α → β → γ} (h : m.WF) : (m.map f).WF :=
|
| 36 |
+
⟨DHashMap.Raw.WF.map h.out⟩
|
| 37 |
+
|
| 38 |
+
end Raw
|
| 39 |
+
|
| 40 |
+
@[inline, inherit_doc DHashMap.filterMap] def filterMap [BEq α] [Hashable α] (f : α → β → Option γ)
|
| 41 |
+
(m : HashMap α β) : HashMap α γ :=
|
| 42 |
+
⟨m.inner.filterMap f⟩
|
| 43 |
+
|
| 44 |
+
@[inline, inherit_doc DHashMap.map] def map [BEq α] [Hashable α] (f : α → β → γ) (m : HashMap α β) :
|
| 45 |
+
HashMap α γ :=
|
| 46 |
+
⟨m.inner.map f⟩
|
| 47 |
+
|
| 48 |
+
end HashMap
|
| 49 |
+
|
| 50 |
+
end Std
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Std/Data/HashMap/Basic.lean
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Markus Himmel
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Std.Data.DHashMap.Basic
|
| 8 |
+
|
| 9 |
+
set_option linter.missingDocs true
|
| 10 |
+
set_option autoImplicit false
|
| 11 |
+
|
| 12 |
+
/-!
|
| 13 |
+
# Hash maps
|
| 14 |
+
|
| 15 |
+
This module develops the type `Std.HashMap` of hash maps. Dependent hash maps are defined in
|
| 16 |
+
`Std.Data.DHashMap`.
|
| 17 |
+
|
| 18 |
+
The operations `map` and `filterMap` on `Std.HashMap` are defined in the module
|
| 19 |
+
`Std.Data.HashMap.AdditionalOperations`.
|
| 20 |
+
|
| 21 |
+
Lemmas about the operations on `Std.HashMap` are available in the
|
| 22 |
+
module `Std.Data.HashMap.Lemmas`.
|
| 23 |
+
|
| 24 |
+
See the module `Std.Data.HashMap.Raw` for a variant of this type which is safe to use in
|
| 25 |
+
nested inductive types.
|
| 26 |
+
-/
|
| 27 |
+
|
| 28 |
+
universe u v w
|
| 29 |
+
|
| 30 |
+
variable {α : Type u} {β : Type v} {_ : BEq α} {_ : Hashable α}
|
| 31 |
+
|
| 32 |
+
namespace Std
|
| 33 |
+
|
| 34 |
+
/--
|
| 35 |
+
Hash maps.
|
| 36 |
+
|
| 37 |
+
This is a simple separate-chaining hash table. The data of the hash map consists of a cached size
|
| 38 |
+
and an array of buckets, where each bucket is a linked list of key-value pais. The number of buckets
|
| 39 |
+
is always a power of two. The hash map doubles its size upon inserting an element such that the
|
| 40 |
+
number of elements is more than 75% of the number of buckets.
|
| 41 |
+
|
| 42 |
+
The hash table is backed by an `Array`. Users should make sure that the hash map is used linearly to
|
| 43 |
+
avoid expensive copies.
|
| 44 |
+
|
| 45 |
+
The hash map uses `==` (provided by the `BEq` typeclass) to compare keys and `hash` (provided by
|
| 46 |
+
the `Hashable` typeclass) to hash them. To ensure that the operations behave as expected, `==`
|
| 47 |
+
should be an equivalence relation and `a == b` should imply `hash a = hash b` (see also the
|
| 48 |
+
`EquivBEq` and `LawfulHashable` typeclasses). Both of these conditions are automatic if the BEq
|
| 49 |
+
instance is lawful, i.e., if `a == b` implies `a = b`.
|
| 50 |
+
|
| 51 |
+
These hash maps contain a bundled well-formedness invariant, which means that they cannot
|
| 52 |
+
be used in nested inductive types. For these use cases, `Std.Data.HashMap.Raw` and
|
| 53 |
+
`Std.Data.HashMap.Raw.WF` unbundle the invariant from the hash map. When in doubt, prefer
|
| 54 |
+
`HashMap` over `HashMap.Raw`.
|
| 55 |
+
|
| 56 |
+
Dependent hash maps, in which keys may occur in their values' types, are available as
|
| 57 |
+
`Std.Data.DHashMap`.
|
| 58 |
+
-/
|
| 59 |
+
structure HashMap (α : Type u) (β : Type v) [BEq α] [Hashable α] where
|
| 60 |
+
/-- Internal implementation detail of the hash map -/
|
| 61 |
+
inner : DHashMap α (fun _ => β)
|
| 62 |
+
|
| 63 |
+
namespace HashMap
|
| 64 |
+
|
| 65 |
+
@[inline, inherit_doc DHashMap.empty] def emptyWithCapacity [BEq α] [Hashable α] (capacity := 8) :
|
| 66 |
+
HashMap α β :=
|
| 67 |
+
⟨DHashMap.emptyWithCapacity capacity⟩
|
| 68 |
+
|
| 69 |
+
@[deprecated emptyWithCapacity (since := "2025-03-12"), inherit_doc emptyWithCapacity]
|
| 70 |
+
abbrev empty := @emptyWithCapacity
|
| 71 |
+
|
| 72 |
+
instance [BEq α] [Hashable α] : EmptyCollection (HashMap α β) where
|
| 73 |
+
emptyCollection := emptyWithCapacity
|
| 74 |
+
|
| 75 |
+
instance [BEq α] [Hashable α] : Inhabited (HashMap α β) where
|
| 76 |
+
default := ∅
|
| 77 |
+
|
| 78 |
+
@[inherit_doc DHashMap.Equiv]
|
| 79 |
+
structure Equiv (m₁ m₂ : HashMap α β) where
|
| 80 |
+
/-- Internal implementation detail of the hash map -/
|
| 81 |
+
inner : m₁.1.Equiv m₂.1
|
| 82 |
+
|
| 83 |
+
@[inherit_doc] scoped infixl:50 " ~m " => Equiv
|
| 84 |
+
|
| 85 |
+
@[inline, inherit_doc DHashMap.insert] def insert (m : HashMap α β) (a : α)
|
| 86 |
+
(b : β) : HashMap α β :=
|
| 87 |
+
⟨m.inner.insert a b⟩
|
| 88 |
+
|
| 89 |
+
instance : Singleton (α × β) (HashMap α β) := ⟨fun ⟨a, b⟩ => (∅ : HashMap α β).insert a b⟩
|
| 90 |
+
|
| 91 |
+
instance : Insert (α × β) (HashMap α β) := ⟨fun ⟨a, b⟩ s => s.insert a b⟩
|
| 92 |
+
|
| 93 |
+
instance : LawfulSingleton (α × β) (HashMap α β) := ⟨fun _ => rfl⟩
|
| 94 |
+
|
| 95 |
+
@[inline, inherit_doc DHashMap.insertIfNew] def insertIfNew (m : HashMap α β)
|
| 96 |
+
(a : α) (b : β) : HashMap α β :=
|
| 97 |
+
⟨m.inner.insertIfNew a b⟩
|
| 98 |
+
|
| 99 |
+
@[inline, inherit_doc DHashMap.containsThenInsert] def containsThenInsert
|
| 100 |
+
(m : HashMap α β) (a : α) (b : β) : Bool × HashMap α β :=
|
| 101 |
+
let ⟨replaced, r⟩ := m.inner.containsThenInsert a b
|
| 102 |
+
⟨replaced, ⟨r⟩⟩
|
| 103 |
+
|
| 104 |
+
@[inline, inherit_doc DHashMap.containsThenInsertIfNew] def containsThenInsertIfNew
|
| 105 |
+
(m : HashMap α β) (a : α) (b : β) : Bool × HashMap α β :=
|
| 106 |
+
let ⟨replaced, r⟩ := m.inner.containsThenInsertIfNew a b
|
| 107 |
+
⟨replaced, ⟨r⟩⟩
|
| 108 |
+
|
| 109 |
+
/--
|
| 110 |
+
Checks whether a key is present in a map, returning the associate value, and inserts a value for
|
| 111 |
+
the key if it was not found.
|
| 112 |
+
|
| 113 |
+
If the returned value is `some v`, then the returned map is unaltered. If it is `none`, then the
|
| 114 |
+
returned map has a new value inserted.
|
| 115 |
+
|
| 116 |
+
Equivalent to (but potentially faster than) calling `get?` followed by `insertIfNew`.
|
| 117 |
+
-/
|
| 118 |
+
@[inline] def getThenInsertIfNew? (m : HashMap α β) (a : α) (b : β) :
|
| 119 |
+
Option β × HashMap α β :=
|
| 120 |
+
let ⟨previous, r⟩ := DHashMap.Const.getThenInsertIfNew? m.inner a b
|
| 121 |
+
⟨previous, ⟨r⟩⟩
|
| 122 |
+
|
| 123 |
+
/--
|
| 124 |
+
The notation `m[a]?` is preferred over calling this function directly.
|
| 125 |
+
|
| 126 |
+
Tries to retrieve the mapping for the given key, returning `none` if no such mapping is present.
|
| 127 |
+
-/
|
| 128 |
+
@[inline] def get? (m : HashMap α β) (a : α) : Option β :=
|
| 129 |
+
DHashMap.Const.get? m.inner a
|
| 130 |
+
|
| 131 |
+
@[inline, inherit_doc DHashMap.contains] def contains (m : HashMap α β)
|
| 132 |
+
(a : α) : Bool :=
|
| 133 |
+
m.inner.contains a
|
| 134 |
+
|
| 135 |
+
instance [BEq α] [Hashable α] : Membership α (HashMap α β) where
|
| 136 |
+
mem m a := a ∈ m.inner
|
| 137 |
+
|
| 138 |
+
instance [BEq α] [Hashable α] {m : HashMap α β} {a : α} : Decidable (a ∈ m) :=
|
| 139 |
+
inferInstanceAs (Decidable (a ∈ m.inner))
|
| 140 |
+
|
| 141 |
+
/--
|
| 142 |
+
The notation `m[a]` or `m[a]'h` is preferred over calling this function directly.
|
| 143 |
+
|
| 144 |
+
Retrieves the mapping for the given key. Ensures that such a mapping exists by requiring a proof of
|
| 145 |
+
`a ∈ m`.
|
| 146 |
+
-/
|
| 147 |
+
@[inline] def get (m : HashMap α β) (a : α) (h : a ∈ m) : β :=
|
| 148 |
+
DHashMap.Const.get m.inner a h
|
| 149 |
+
|
| 150 |
+
@[inline, inherit_doc DHashMap.Const.getD] def getD (m : HashMap α β) (a : α)
|
| 151 |
+
(fallback : β) : β :=
|
| 152 |
+
DHashMap.Const.getD m.inner a fallback
|
| 153 |
+
|
| 154 |
+
/--
|
| 155 |
+
The notation `m[a]!` is preferred over calling this function directly.
|
| 156 |
+
|
| 157 |
+
Tries to retrieve the mapping for the given key, panicking if no such mapping is present.
|
| 158 |
+
-/
|
| 159 |
+
@[inline] def get! [Inhabited β] (m : HashMap α β) (a : α) : β :=
|
| 160 |
+
DHashMap.Const.get! m.inner a
|
| 161 |
+
|
| 162 |
+
instance [BEq α] [Hashable α] : GetElem? (HashMap α β) α β (fun m a => a ∈ m) where
|
| 163 |
+
getElem m a h := m.get a h
|
| 164 |
+
getElem? m a := m.get? a
|
| 165 |
+
getElem! m a := m.get! a
|
| 166 |
+
|
| 167 |
+
@[inline, inherit_doc DHashMap.getKey?] def getKey? (m : HashMap α β) (a : α) : Option α :=
|
| 168 |
+
DHashMap.getKey? m.inner a
|
| 169 |
+
|
| 170 |
+
@[inline, inherit_doc DHashMap.getKey] def getKey (m : HashMap α β) (a : α) (h : a ∈ m) : α :=
|
| 171 |
+
DHashMap.getKey m.inner a h
|
| 172 |
+
|
| 173 |
+
@[inline, inherit_doc DHashMap.getKeyD] def getKeyD (m : HashMap α β) (a : α) (fallback : α) : α :=
|
| 174 |
+
DHashMap.getKeyD m.inner a fallback
|
| 175 |
+
|
| 176 |
+
@[inline, inherit_doc DHashMap.getKey!] def getKey! [Inhabited α] (m : HashMap α β) (a : α) : α :=
|
| 177 |
+
DHashMap.getKey! m.inner a
|
| 178 |
+
|
| 179 |
+
@[inline, inherit_doc DHashMap.erase] def erase (m : HashMap α β) (a : α) :
|
| 180 |
+
HashMap α β :=
|
| 181 |
+
⟨m.inner.erase a⟩
|
| 182 |
+
|
| 183 |
+
@[inline, inherit_doc DHashMap.size] def size (m : HashMap α β) : Nat :=
|
| 184 |
+
m.inner.size
|
| 185 |
+
|
| 186 |
+
@[inline, inherit_doc DHashMap.isEmpty] def isEmpty (m : HashMap α β) : Bool :=
|
| 187 |
+
m.inner.isEmpty
|
| 188 |
+
|
| 189 |
+
@[inline, inherit_doc DHashMap.keys] def keys (m : HashMap α β) : List α :=
|
| 190 |
+
m.inner.keys
|
| 191 |
+
|
| 192 |
+
@[inline, inherit_doc DHashMap.Const.ofList] def ofList [BEq α] [Hashable α] (l : List (α × β)) :
|
| 193 |
+
HashMap α β :=
|
| 194 |
+
⟨DHashMap.Const.ofList l⟩
|
| 195 |
+
|
| 196 |
+
@[inline, inherit_doc DHashMap.Const.unitOfList] def unitOfList [BEq α] [Hashable α] (l : List α) :
|
| 197 |
+
HashMap α Unit :=
|
| 198 |
+
⟨DHashMap.Const.unitOfList l⟩
|
| 199 |
+
|
| 200 |
+
@[inline, inherit_doc DHashMap.Const.toList] def toList (m : HashMap α β) :
|
| 201 |
+
List (α × β) :=
|
| 202 |
+
DHashMap.Const.toList m.inner
|
| 203 |
+
|
| 204 |
+
@[inline, inherit_doc DHashMap.foldM] def foldM {m : Type w → Type w}
|
| 205 |
+
[Monad m] {γ : Type w} (f : γ → α → β → m γ) (init : γ) (b : HashMap α β) : m γ :=
|
| 206 |
+
b.inner.foldM f init
|
| 207 |
+
|
| 208 |
+
@[inline, inherit_doc DHashMap.fold] def fold {γ : Type w}
|
| 209 |
+
(f : γ → α → β → γ) (init : γ) (b : HashMap α β) : γ :=
|
| 210 |
+
b.inner.fold f init
|
| 211 |
+
|
| 212 |
+
@[inline, inherit_doc DHashMap.forM] def forM {m : Type w → Type w} [Monad m]
|
| 213 |
+
(f : (a : α) → β → m PUnit) (b : HashMap α β) : m PUnit :=
|
| 214 |
+
b.inner.forM f
|
| 215 |
+
|
| 216 |
+
@[inline, inherit_doc DHashMap.forIn] def forIn {m : Type w → Type w} [Monad m]
|
| 217 |
+
{γ : Type w} (f : (a : α) → β → γ → m (ForInStep γ)) (init : γ) (b : HashMap α β) : m γ :=
|
| 218 |
+
b.inner.forIn f init
|
| 219 |
+
|
| 220 |
+
instance [BEq α] [Hashable α] {m : Type w → Type w} : ForM m (HashMap α β) (α × β) where
|
| 221 |
+
forM m f := m.forM (fun a b => f (a, b))
|
| 222 |
+
|
| 223 |
+
instance [BEq α] [Hashable α] {m : Type w → Type w} : ForIn m (HashMap α β) (α × β) where
|
| 224 |
+
forIn m init f := m.forIn (fun a b acc => f (a, b) acc) init
|
| 225 |
+
|
| 226 |
+
@[inline, inherit_doc DHashMap.filter] def filter (f : α → β → Bool)
|
| 227 |
+
(m : HashMap α β) : HashMap α β :=
|
| 228 |
+
⟨m.inner.filter f⟩
|
| 229 |
+
|
| 230 |
+
@[inline, inherit_doc DHashMap.modify] def modify (m : HashMap α β) (a : α) (f : β → β) :
|
| 231 |
+
HashMap α β :=
|
| 232 |
+
⟨DHashMap.Const.modify m.inner a f⟩
|
| 233 |
+
|
| 234 |
+
@[inline, inherit_doc DHashMap.alter] def alter (m : HashMap α β) (a : α)
|
| 235 |
+
(f : Option β → Option β) : HashMap α β :=
|
| 236 |
+
⟨DHashMap.Const.alter m.inner a f⟩
|
| 237 |
+
|
| 238 |
+
@[inline, inherit_doc DHashMap.Const.insertMany] def insertMany {ρ : Type w}
|
| 239 |
+
[ForIn Id ρ (α × β)] (m : HashMap α β) (l : ρ) : HashMap α β :=
|
| 240 |
+
⟨DHashMap.Const.insertMany m.inner l⟩
|
| 241 |
+
|
| 242 |
+
@[inline, inherit_doc DHashMap.Const.insertManyIfNewUnit] def insertManyIfNewUnit
|
| 243 |
+
{ρ : Type w} [ForIn Id ρ α] (m : HashMap α Unit) (l : ρ) : HashMap α Unit :=
|
| 244 |
+
⟨DHashMap.Const.insertManyIfNewUnit m.inner l⟩
|
| 245 |
+
|
| 246 |
+
|
| 247 |
+
section Unverified
|
| 248 |
+
|
| 249 |
+
/-! We currently do not provide lemmas for the functions below. -/
|
| 250 |
+
|
| 251 |
+
@[inline, inherit_doc DHashMap.partition] def partition (f : α → β → Bool)
|
| 252 |
+
(m : HashMap α β) : HashMap α β × HashMap α β :=
|
| 253 |
+
let ⟨l, r⟩ := m.inner.partition f
|
| 254 |
+
⟨⟨l⟩, ⟨r⟩⟩
|
| 255 |
+
|
| 256 |
+
@[inline, inherit_doc DHashMap.Const.toArray] def toArray (m : HashMap α β) :
|
| 257 |
+
Array (α × β) :=
|
| 258 |
+
DHashMap.Const.toArray m.inner
|
| 259 |
+
|
| 260 |
+
@[inline, inherit_doc DHashMap.keysArray] def keysArray (m : HashMap α β) :
|
| 261 |
+
Array α :=
|
| 262 |
+
m.inner.keysArray
|
| 263 |
+
|
| 264 |
+
@[inline, inherit_doc DHashMap.values] def values (m : HashMap α β) : List β :=
|
| 265 |
+
m.inner.values
|
| 266 |
+
|
| 267 |
+
@[inline, inherit_doc DHashMap.valuesArray] def valuesArray (m : HashMap α β) :
|
| 268 |
+
Array β :=
|
| 269 |
+
m.inner.valuesArray
|
| 270 |
+
|
| 271 |
+
/-- Computes the union of the given hash maps, by traversing `m₂` and inserting its elements into `m₁`. -/
|
| 272 |
+
@[inline] def union [BEq α] [Hashable α] (m₁ m₂ : HashMap α β) : HashMap α β :=
|
| 273 |
+
m₂.fold (init := m₁) fun acc x => acc.insert x
|
| 274 |
+
|
| 275 |
+
instance [BEq α] [Hashable α] : Union (HashMap α β) := ⟨union⟩
|
| 276 |
+
|
| 277 |
+
@[inline, inherit_doc DHashMap.Const.unitOfArray] def unitOfArray [BEq α] [Hashable α] (l : Array α) :
|
| 278 |
+
HashMap α Unit :=
|
| 279 |
+
⟨DHashMap.Const.unitOfArray l⟩
|
| 280 |
+
|
| 281 |
+
@[inline, inherit_doc DHashMap.Internal.numBuckets] def Internal.numBuckets
|
| 282 |
+
(m : HashMap α β) : Nat :=
|
| 283 |
+
DHashMap.Internal.numBuckets m.inner
|
| 284 |
+
|
| 285 |
+
instance [BEq α] [Hashable α] [Repr α] [Repr β] : Repr (HashMap α β) where
|
| 286 |
+
reprPrec m prec := Repr.addAppParen ("Std.HashMap.ofList " ++ reprArg m.toList) prec
|
| 287 |
+
|
| 288 |
+
end Unverified
|
| 289 |
+
|
| 290 |
+
end Std.HashMap
|
| 291 |
+
|
| 292 |
+
/--
|
| 293 |
+
Groups the elements of an array `xs` according to the function `key`, returning a hash map in which
|
| 294 |
+
each group is associated with its key. Groups preserve the relative order of elements in `xs`.
|
| 295 |
+
|
| 296 |
+
Example:
|
| 297 |
+
```lean example
|
| 298 |
+
#eval #[0, 1, 2, 3, 4, 5, 6].groupByKey (· % 2)
|
| 299 |
+
```
|
| 300 |
+
```output
|
| 301 |
+
Std.HashMap.ofList [(0, #[0, 2, 4, 6]), (1, #[1, 3, 5])]
|
| 302 |
+
```
|
| 303 |
+
-/
|
| 304 |
+
def Array.groupByKey [BEq α] [Hashable α] (key : β → α) (xs : Array β)
|
| 305 |
+
: Std.HashMap α (Array β) := Id.run do
|
| 306 |
+
let mut groups := ∅
|
| 307 |
+
for x in xs do
|
| 308 |
+
groups := groups.alter (key x) (some <| ·.getD #[] |>.push x)
|
| 309 |
+
return groups
|
| 310 |
+
|
| 311 |
+
/--
|
| 312 |
+
Groups the elements of a list `xs` according to the function `key`, returning a hash map in which
|
| 313 |
+
each group is associated with its key. Groups preserve the relative order of elements in `xs`.
|
| 314 |
+
|
| 315 |
+
Example:
|
| 316 |
+
```lean example
|
| 317 |
+
#eval [0, 1, 2, 3, 4, 5, 6].groupByKey (· % 2)
|
| 318 |
+
```
|
| 319 |
+
```output
|
| 320 |
+
Std.HashMap.ofList [(0, [0, 2, 4, 6]), (1, [1, 3, 5])]
|
| 321 |
+
```
|
| 322 |
+
-/
|
| 323 |
+
def List.groupByKey [BEq α] [Hashable α] (key : β → α) (xs : List β) :
|
| 324 |
+
Std.HashMap α (List β) :=
|
| 325 |
+
xs.foldr (init := ∅) fun x acc => acc.alter (key x) (fun v => some <| x :: v.getD [])
|
backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/Util/Store.lean
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2022 Mac Malone. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Mac Malone
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Init.Notation
|
| 8 |
+
|
| 9 |
+
namespace Lake
|
| 10 |
+
|
| 11 |
+
/-- A monad equipped with a dependently typed key-value store for a particular key. -/
|
| 12 |
+
class MonadStore1Of {κ : Type u} (k : κ) (α : semiOutParam $ Type v) (m : Type v → Type w) where
|
| 13 |
+
fetch? : m (Option α)
|
| 14 |
+
store : α → m PUnit
|
| 15 |
+
|
| 16 |
+
export MonadStore1Of (store)
|
| 17 |
+
|
| 18 |
+
/-- Similar to `MonadStore1Of`, but `α` is an `outParam` for convenience. -/
|
| 19 |
+
class MonadStore1 {κ : Type u} (k : κ) (α : outParam $ Type v) (m : Type v → Type w) where
|
| 20 |
+
fetch? : m (Option α)
|
| 21 |
+
store : α → m PUnit
|
| 22 |
+
|
| 23 |
+
export MonadStore1 (fetch?)
|
| 24 |
+
|
| 25 |
+
instance [MonadStore1Of k α m] : MonadStore1 k α m where
|
| 26 |
+
fetch? := MonadStore1Of.fetch? k
|
| 27 |
+
store := MonadStore1Of.store k
|
| 28 |
+
|
| 29 |
+
/-- A monad equipped with a dependently typed key-object store. -/
|
| 30 |
+
class MonadDStore (κ : Type u) (β : semiOutParam $ κ → Type v) (m : Type v → Type w) where
|
| 31 |
+
fetch? : (key : κ) → m (Option (β key))
|
| 32 |
+
store : (key : κ) → β key → m PUnit
|
| 33 |
+
|
| 34 |
+
instance [MonadDStore κ β m] : MonadStore1Of k (β k) m where
|
| 35 |
+
fetch? := MonadDStore.fetch? k
|
| 36 |
+
store o := MonadDStore.store k o
|
| 37 |
+
|
| 38 |
+
/-- A monad equipped with a key-object store. -/
|
| 39 |
+
abbrev MonadStore κ α m := MonadDStore κ (fun _ => α) m
|
| 40 |
+
|
| 41 |
+
instance [MonadLift m n] [MonadDStore κ β m] : MonadDStore κ β n where
|
| 42 |
+
fetch? k := liftM (m := m) <| fetch? k
|
| 43 |
+
store k a := liftM (m := m) <| store k a
|
| 44 |
+
|
| 45 |
+
@[inline] def fetchOrCreate [Monad m]
|
| 46 |
+
(key : κ) [MonadStore1Of key α m] (create : m α) : m α := do
|
| 47 |
+
if let some val ← fetch? key then
|
| 48 |
+
return val
|
| 49 |
+
else
|
| 50 |
+
let val ← create
|
| 51 |
+
store key val
|
| 52 |
+
return val
|
backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/Util/StoreInsts.lean
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2022 Mac Malone. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Mac Malone
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lake.Util.DRBMap
|
| 8 |
+
import Lake.Util.RBArray
|
| 9 |
+
import Lake.Util.Family
|
| 10 |
+
import Lake.Util.Store
|
| 11 |
+
|
| 12 |
+
open Lean
|
| 13 |
+
namespace Lake
|
| 14 |
+
|
| 15 |
+
instance [Monad m] [EqOfCmpWrt κ β cmp] : MonadDStore κ β (StateT (DRBMap κ β cmp) m) where
|
| 16 |
+
fetch? k := return (← get).find? k
|
| 17 |
+
store k a := modify (·.insert k a)
|
| 18 |
+
|
| 19 |
+
instance [MonadLiftT (ST ω) m] [Monad m] [EqOfCmpWrt κ β cmp] : MonadDStore κ β (StateRefT' ω (DRBMap κ β cmp) m) where
|
| 20 |
+
fetch? k := return (← get).find? k
|
| 21 |
+
store k a := modify (·.insert k a)
|
| 22 |
+
|
| 23 |
+
instance [Monad m] : MonadStore κ α (StateT (RBMap κ α cmp) m) where
|
| 24 |
+
fetch? k := return (← get).find? k
|
| 25 |
+
store k a := modify (·.insert k a)
|
| 26 |
+
|
| 27 |
+
instance [MonadLiftT (ST ω) m] [Monad m] : MonadStore κ α (StateRefT' ω (RBMap κ α cmp) m) where
|
| 28 |
+
fetch? k := return (← get).find? k
|
| 29 |
+
store k a := modify (·.insert k a)
|
| 30 |
+
|
| 31 |
+
instance [Monad m] : MonadStore κ α (StateT (RBArray κ α cmp) m) where
|
| 32 |
+
fetch? k := return (← get).find? k
|
| 33 |
+
store k a := modify (·.insert k a)
|
| 34 |
+
|
| 35 |
+
instance [MonadLiftT (ST ω) m] [Monad m] : MonadStore κ α (StateRefT' ω (RBArray κ α cmp) m) where
|
| 36 |
+
fetch? k := return (← get).find? k
|
| 37 |
+
store k a := modify (·.insert k a)
|
| 38 |
+
|
| 39 |
+
-- uses the eagerly specialized `RBMap` functions in `NameMap`
|
| 40 |
+
instance [Monad m] : MonadStore Name α (StateT (NameMap α) m) where
|
| 41 |
+
fetch? k := return (← get).find? k
|
| 42 |
+
store k a := modify (·.insert k a)
|
| 43 |
+
|
| 44 |
+
instance [MonadLiftT (ST ω) m] [Monad m] : MonadStore Name α (StateRefT' ω (NameMap α) m) where
|
| 45 |
+
fetch? k := return (← get).find? k
|
| 46 |
+
store k a := modify (·.insert k a)
|
| 47 |
+
|
| 48 |
+
@[inline] instance [MonadDStore κ β m] [t : FamilyOut β k α] : MonadStore1Of k α m where
|
| 49 |
+
fetch? := cast (by rw [t.fam_eq]) <| fetch? (m := m) k
|
| 50 |
+
store a := store k <| cast t.fam_eq.symm a
|
backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/Util/Sugar.lean
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2022 Mac Malone. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Mac Malone
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Init.Notation
|
| 8 |
+
|
| 9 |
+
namespace Lake
|
| 10 |
+
|
| 11 |
+
macro "try " x:term " else " y:term : term =>
|
| 12 |
+
``($x <|> $y)
|
| 13 |
+
|
| 14 |
+
macro "try " x:doSeq " else " y:doSeq : doElem =>
|
| 15 |
+
`(doElem| (do $x) <|> (do $y))
|
backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/Util/Task.lean
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2021 Mac Malone. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Mac Malone
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Init.Control.Option
|
| 8 |
+
|
| 9 |
+
namespace Lake
|
| 10 |
+
|
| 11 |
+
instance : Monad Task where
|
| 12 |
+
map := Task.map
|
| 13 |
+
pure := Task.pure
|
| 14 |
+
bind := Task.bind
|
| 15 |
+
|
| 16 |
+
abbrev ETask ε := ExceptT ε Task
|
| 17 |
+
abbrev OptionTask := OptionT Task
|
| 18 |
+
|
| 19 |
+
def BaseIOTask := Task
|
| 20 |
+
instance : Monad BaseIOTask := inferInstanceAs <| Monad Task
|
| 21 |
+
instance [Inhabited α] : Inhabited (BaseIOTask α) := inferInstance
|
| 22 |
+
|
| 23 |
+
abbrev EIOTask ε := ExceptT ε BaseIOTask
|
| 24 |
+
abbrev OptionIOTask := OptionT BaseIOTask
|
| 25 |
+
|
| 26 |
+
instance : Inhabited (OptionIOTask α) := ⟨failure⟩
|
backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/Util/Version.lean
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2024 Mac Malone. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Mac Malone
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Data.Json
|
| 8 |
+
import Lake.Util.Date
|
| 9 |
+
|
| 10 |
+
/-! # Version
|
| 11 |
+
|
| 12 |
+
This module contains useful definitions for manipulating versions.
|
| 13 |
+
It also defines a `v!"<ver>"` syntax for version literals.
|
| 14 |
+
-/
|
| 15 |
+
|
| 16 |
+
open System Lean
|
| 17 |
+
|
| 18 |
+
namespace Lake
|
| 19 |
+
|
| 20 |
+
/-- The major-minor-patch triple of a [SemVer](https://semver.org/). -/
|
| 21 |
+
structure SemVerCore where
|
| 22 |
+
major : Nat := 0
|
| 23 |
+
minor : Nat := 0
|
| 24 |
+
patch : Nat := 0
|
| 25 |
+
deriving Inhabited, Repr, DecidableEq, Ord
|
| 26 |
+
|
| 27 |
+
instance : LT SemVerCore := ltOfOrd
|
| 28 |
+
instance : LE SemVerCore := leOfOrd
|
| 29 |
+
instance : Min SemVerCore := minOfLe
|
| 30 |
+
instance : Max SemVerCore := maxOfLe
|
| 31 |
+
|
| 32 |
+
def SemVerCore.parse (ver : String) : Except String SemVerCore := do
|
| 33 |
+
try
|
| 34 |
+
match ver.split (· == '.') with
|
| 35 |
+
| [major, minor, patch] =>
|
| 36 |
+
let parseNat (v : String) (what : String) := do
|
| 37 |
+
let some v := v.toNat?
|
| 38 |
+
| throw s!"expect numeral {what} version, got '{v}'"
|
| 39 |
+
return v
|
| 40 |
+
return {
|
| 41 |
+
major := ← parseNat major "major"
|
| 42 |
+
minor := ← parseNat minor "minor"
|
| 43 |
+
patch := ← parseNat patch "patch"
|
| 44 |
+
}
|
| 45 |
+
| ps => throw s!"incorrect number of components: got {ps.length}, expected 3"
|
| 46 |
+
catch e =>
|
| 47 |
+
throw s!"invalid version core: {e}"
|
| 48 |
+
|
| 49 |
+
protected def SemVerCore.toString (ver : SemVerCore) : String :=
|
| 50 |
+
s!"{ver.major}.{ver.minor}.{ver.patch}"
|
| 51 |
+
|
| 52 |
+
instance : ToString SemVerCore := ⟨SemVerCore.toString⟩
|
| 53 |
+
instance : ToJson SemVerCore := ⟨(·.toString)⟩
|
| 54 |
+
instance : FromJson SemVerCore := ⟨(do SemVerCore.parse <| ← fromJson? ·)⟩
|
| 55 |
+
|
| 56 |
+
/--
|
| 57 |
+
A Lean-style semantic version.
|
| 58 |
+
A major-minor-patch triple with an optional arbitrary `-` suffix.
|
| 59 |
+
-/
|
| 60 |
+
structure StdVer extends SemVerCore where
|
| 61 |
+
specialDescr : String := ""
|
| 62 |
+
deriving Inhabited, Repr, DecidableEq
|
| 63 |
+
|
| 64 |
+
/-- A Lean version. -/
|
| 65 |
+
abbrev LeanVer := StdVer
|
| 66 |
+
|
| 67 |
+
instance : Coe StdVer SemVerCore := ⟨StdVer.toSemVerCore⟩
|
| 68 |
+
|
| 69 |
+
@[inline] protected def StdVer.ofSemVerCore (ver : SemVerCore) : StdVer :=
|
| 70 |
+
{toSemVerCore := ver, specialDescr := ""}
|
| 71 |
+
|
| 72 |
+
instance : Coe SemVerCore StdVer := ⟨StdVer.ofSemVerCore⟩
|
| 73 |
+
|
| 74 |
+
protected def StdVer.compare (a b : StdVer) : Ordering :=
|
| 75 |
+
match compare a.toSemVerCore b.toSemVerCore with
|
| 76 |
+
| .eq =>
|
| 77 |
+
match a.specialDescr, b.specialDescr with
|
| 78 |
+
| "", "" => .eq
|
| 79 |
+
| _, "" => .lt
|
| 80 |
+
| "", _ => .gt
|
| 81 |
+
| a, b => compare a b
|
| 82 |
+
| ord => ord
|
| 83 |
+
|
| 84 |
+
instance : Ord StdVer := ⟨StdVer.compare⟩
|
| 85 |
+
|
| 86 |
+
instance : LT StdVer := ltOfOrd
|
| 87 |
+
instance : LE StdVer := leOfOrd
|
| 88 |
+
instance : Min StdVer := minOfLe
|
| 89 |
+
instance : Max StdVer := maxOfLe
|
| 90 |
+
|
| 91 |
+
def StdVer.parse (ver : String) : Except String StdVer := do
|
| 92 |
+
let sepPos := ver.find (· == '-')
|
| 93 |
+
if h : ver.atEnd sepPos then
|
| 94 |
+
SemVerCore.parse ver
|
| 95 |
+
else
|
| 96 |
+
let core ← SemVerCore.parse <| ver.extract 0 sepPos
|
| 97 |
+
let specialDescr := ver.extract (ver.next' sepPos h) ver.endPos
|
| 98 |
+
if specialDescr.isEmpty then
|
| 99 |
+
throw "invalid version: '-' suffix cannot be empty"
|
| 100 |
+
return {toSemVerCore := core, specialDescr}
|
| 101 |
+
|
| 102 |
+
protected def StdVer.toString (ver : StdVer) : String :=
|
| 103 |
+
if ver.specialDescr.isEmpty then
|
| 104 |
+
ver.toSemVerCore.toString
|
| 105 |
+
else
|
| 106 |
+
s!"{ver.toSemVerCore}-{ver.specialDescr}"
|
| 107 |
+
|
| 108 |
+
instance : ToString StdVer := ⟨StdVer.toString⟩
|
| 109 |
+
instance : ToJson StdVer := ⟨(·.toString)⟩
|
| 110 |
+
instance : FromJson StdVer := ⟨(do StdVer.parse <| ← fromJson? ·)⟩
|
| 111 |
+
|
| 112 |
+
/-- A Lean toolchain version. -/
|
| 113 |
+
inductive ToolchainVer
|
| 114 |
+
| release (ver : LeanVer)
|
| 115 |
+
| nightly (date : Date)
|
| 116 |
+
| pr (no : Nat)
|
| 117 |
+
| other (name : String)
|
| 118 |
+
deriving Repr, DecidableEq
|
| 119 |
+
|
| 120 |
+
instance : Coe LeanVer ToolchainVer := ⟨ToolchainVer.release⟩
|
| 121 |
+
|
| 122 |
+
def ToolchainVer.defaultOrigin := "leanprover/lean4"
|
| 123 |
+
def ToolchainVer.prOrigin := "leanprover/lean4-pr-releases"
|
| 124 |
+
|
| 125 |
+
def ToolchainVer.ofString (ver : String) : ToolchainVer := Id.run do
|
| 126 |
+
let colonPos := ver.posOf ':'
|
| 127 |
+
let (origin, tag) :=
|
| 128 |
+
if h : colonPos < ver.endPos then
|
| 129 |
+
let pos := ver.next' colonPos (by simp_all [String.endPos, String.atEnd])
|
| 130 |
+
(ver.extract 0 colonPos, ver.extract pos ver.endPos)
|
| 131 |
+
else
|
| 132 |
+
("", ver)
|
| 133 |
+
if tag.startsWith "v" then
|
| 134 |
+
if let .ok ver := StdVer.parse (tag.drop 1) then
|
| 135 |
+
if origin.isEmpty || origin == defaultOrigin then
|
| 136 |
+
return .release ver
|
| 137 |
+
return .other ver
|
| 138 |
+
else if tag.startsWith "nightly-" then
|
| 139 |
+
if let some date := Date.ofString? (tag.drop "nightly-".length) then
|
| 140 |
+
if origin.isEmpty || origin == defaultOrigin then
|
| 141 |
+
return .nightly date
|
| 142 |
+
else if tag.startsWith "pr-release-" then
|
| 143 |
+
if let some n := (tag.drop "pr-release-".length).toNat? then
|
| 144 |
+
if origin.isEmpty || origin == prOrigin then
|
| 145 |
+
return .pr n
|
| 146 |
+
else
|
| 147 |
+
if let .ok ver := StdVer.parse ver then
|
| 148 |
+
if origin.isEmpty || origin == defaultOrigin then
|
| 149 |
+
return .release ver
|
| 150 |
+
return .other ver
|
| 151 |
+
|
| 152 |
+
/-- Parse a toolchain from a `lean-toolchain` file. -/
|
| 153 |
+
def ToolchainVer.ofFile? (toolchainFile : FilePath) : IO (Option ToolchainVer) := do
|
| 154 |
+
try
|
| 155 |
+
let toolchainString ← IO.FS.readFile toolchainFile
|
| 156 |
+
return some <| ToolchainVer.ofString toolchainString.trim
|
| 157 |
+
catch
|
| 158 |
+
| .noFileOrDirectory .. =>
|
| 159 |
+
return none
|
| 160 |
+
| e => throw e
|
| 161 |
+
|
| 162 |
+
/-- The `elan` toolchain file name (i.e., `lean-toolchain`). -/
|
| 163 |
+
def toolchainFileName : FilePath := "lean-toolchain"
|
| 164 |
+
|
| 165 |
+
/-- Parse a toolchain from the `lean-toolchain` file of the directory `dir`. -/
|
| 166 |
+
@[inline] def ToolchainVer.ofDir? (dir : FilePath) : IO (Option ToolchainVer) :=
|
| 167 |
+
ToolchainVer.ofFile? (dir / toolchainFileName)
|
| 168 |
+
|
| 169 |
+
protected def ToolchainVer.toString (ver : ToolchainVer) : String :=
|
| 170 |
+
match ver with
|
| 171 |
+
| .release ver => s!"{defaultOrigin}:v{ver}"
|
| 172 |
+
| .nightly date => s!"{defaultOrigin}:nightly-{date}"
|
| 173 |
+
| .pr n => s!"{prOrigin}:pr-release-{n}"
|
| 174 |
+
| .other s => s
|
| 175 |
+
|
| 176 |
+
instance : ToString ToolchainVer := ⟨ToolchainVer.toString⟩
|
| 177 |
+
instance : ToJson ToolchainVer := ⟨(·.toString)⟩
|
| 178 |
+
instance : FromJson ToolchainVer := ⟨(ToolchainVer.ofString <$> fromJson? ·)⟩
|
| 179 |
+
|
| 180 |
+
protected def ToolchainVer.lt (a b : ToolchainVer) : Prop :=
|
| 181 |
+
match a, b with
|
| 182 |
+
| .release v1, .release v2 => v1 < v2
|
| 183 |
+
| .nightly d1, .nightly d2 => d1 < d2
|
| 184 |
+
| _, _ => False
|
| 185 |
+
|
| 186 |
+
instance : LT ToolchainVer := ⟨ToolchainVer.lt⟩
|
| 187 |
+
|
| 188 |
+
instance ToolchainVer.decLt (a b : ToolchainVer) : Decidable (a < b) :=
|
| 189 |
+
match a, b with
|
| 190 |
+
| .release v1, .release v2 => inferInstanceAs (Decidable (v1 < v2))
|
| 191 |
+
| .nightly d1, .nightly d2 => inferInstanceAs (Decidable (d1 < d2))
|
| 192 |
+
| .release _, .pr _ | .release _, .nightly _ | .release _, .other _
|
| 193 |
+
| .nightly _, .release _ | .nightly _, .pr _ | .nightly _, .other _
|
| 194 |
+
| .pr _, _ | .other _, _ => .isFalse (by simp [LT.lt, ToolchainVer.lt])
|
| 195 |
+
|
| 196 |
+
protected def ToolchainVer.le (a b : ToolchainVer) : Prop :=
|
| 197 |
+
match a, b with
|
| 198 |
+
| .release v1, .release v2 => v1 ≤ v2
|
| 199 |
+
| .nightly d1, .nightly d2 => d1 ≤ d2
|
| 200 |
+
| .pr n1, .pr n2 => n1 = n2
|
| 201 |
+
| .other v1, .other v2 => v1 = v2
|
| 202 |
+
| _, _ => False
|
| 203 |
+
|
| 204 |
+
instance : LE ToolchainVer := ⟨ToolchainVer.le⟩
|
| 205 |
+
|
| 206 |
+
instance ToolchainVer.decLe (a b : ToolchainVer) : Decidable (a ≤ b) :=
|
| 207 |
+
match a, b with
|
| 208 |
+
| .release v1, .release v2 => inferInstanceAs (Decidable (v1 ≤ v2))
|
| 209 |
+
| .nightly d1, .nightly d2 => inferInstanceAs (Decidable (d1 ≤ d2))
|
| 210 |
+
| .pr n1, .pr n2 => inferInstanceAs (Decidable (n1 = n2))
|
| 211 |
+
| .other v1, .other v2 => inferInstanceAs (Decidable (v1 = v2))
|
| 212 |
+
| .release _, .pr _ | .release _, .nightly _ | .release _, .other _
|
| 213 |
+
| .nightly _, .release _ | .nightly _, .pr _ | .nightly _, other _
|
| 214 |
+
| .pr _, .release _ | .pr _, .nightly _ | .pr _, .other _
|
| 215 |
+
| .other _, .release _ | .other _, .nightly _ | .other _, .pr _ =>
|
| 216 |
+
.isFalse (by simp [LE.le, ToolchainVer.le])
|
| 217 |
+
|
| 218 |
+
/-- Parses a version from a string. -/
|
| 219 |
+
class DecodeVersion (α : Type u) where
|
| 220 |
+
decodeVersion : String → Except String α
|
| 221 |
+
|
| 222 |
+
export DecodeVersion (decodeVersion)
|
| 223 |
+
|
| 224 |
+
instance : DecodeVersion SemVerCore := ⟨SemVerCore.parse⟩
|
| 225 |
+
@[default_instance] instance : DecodeVersion StdVer := ⟨StdVer.parse⟩
|
| 226 |
+
instance : DecodeVersion ToolchainVer := ⟨(pure <| ToolchainVer.ofString ·)⟩
|
backend/core/leanprover--lean4---v4.22.0/src/lean/lake/tests/8448/A.lean
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
@[extern "increment8"]
|
| 2 |
+
opaque A.increment8 : Int8 → Int8
|
backend/core/leanprover--lean4---v4.22.0/src/lean/lake/tests/8448/B.lean
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import A
|
| 2 |
+
|
| 3 |
+
def B.increment8 := A.increment8
|
| 4 |
+
|
| 5 |
+
#eval B.increment8 1
|