ZAIDX11 commited on
Commit
65cc3bf
·
verified ·
1 Parent(s): 9526b1c

Add files using upload-large-folder tool

Browse files
Files changed (20) hide show
  1. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Tactic/Do/ProofMode/LeftRight.lean +38 -0
  2. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Tactic/Do/ProofMode/MGoal.lean +154 -0
  3. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Tactic/Do/ProofMode/Pure.lean +50 -0
  4. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Tactic/Do/ProofMode/Refine.lean +80 -0
  5. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Tactic/Do/ProofMode/Revert.lean +37 -0
  6. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Tactic/Do/ProofMode/Specialize.lean +176 -0
  7. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Tactic/Omega/Core.lean +575 -0
  8. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Tactic/Omega/Frontend.lean +712 -0
  9. backend/core/leanprover--lean4---v4.22.0/src/lean/Std/Data/ExtTreeMap/Lemmas.lean +0 -0
  10. backend/core/leanprover--lean4---v4.22.0/src/lean/Std/Data/ExtTreeSet/Basic.lean +523 -0
  11. backend/core/leanprover--lean4---v4.22.0/src/lean/Std/Data/ExtTreeSet/Lemmas.lean +1539 -0
  12. backend/core/leanprover--lean4---v4.22.0/src/lean/Std/Data/HashMap/AdditionalOperations.lean +50 -0
  13. backend/core/leanprover--lean4---v4.22.0/src/lean/Std/Data/HashMap/Basic.lean +325 -0
  14. backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/Util/Store.lean +52 -0
  15. backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/Util/StoreInsts.lean +50 -0
  16. backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/Util/Sugar.lean +15 -0
  17. backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/Util/Task.lean +26 -0
  18. backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/Util/Version.lean +226 -0
  19. backend/core/leanprover--lean4---v4.22.0/src/lean/lake/tests/8448/A.lean +2 -0
  20. 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