-
Notifications
You must be signed in to change notification settings - Fork 80
/
injection_tactic.lean
114 lines (100 loc) · 4.64 KB
/
injection_tactic.lean
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
/-
Copyright (c) 2016 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura, Jannis Limperg
-/
prelude
import init.meta.tactic
namespace tactic
open expr
/- Given a local constant `H : C x₁ ... xₙ = D y₁ ... yₘ`, where `C` and `D` are
fully applied constructors, `injection_with H ns base offset` does the
following:
- If `C ≠ D`, it solves the goal (using the no-confusion rule).
- If `C = D` (and thus `n = m`), it adds hypotheses
`h₁ : x₁ = y₁, ..., hₙ : xₙ = yₙ` to the local context. Names for the `hᵢ`
are taken from `ns`. If `ns` does not contain enough names, then the names
are derived from `base` and `offset` (by default `h_1`, `h_2` etc.; see
`intro_fresh`).
- Special case: if `C = D` and `n = 0` (i.e. the constructors have no
arguments), the hypothesis `h : true` is added to the context.
`injection_with` returns the new hypotheses and the leftover names from `ns`
(i.e. those names that were not used to name the new hypotheses). If (and only
if) the goal was solved, the list of new hypotheses is empty.
-/
meta def injection_with (h : expr) (ns : list name)
(base := `h) (offset := some 1) : tactic (list expr × list name) :=
do
H ← infer_type h,
(lhs, rhs, constructor_left, constructor_right, inj_name) ← do {
(lhs, rhs) ← match_eq H,
lhs ← whnf_ginductive lhs,
rhs ← whnf_ginductive rhs,
env ← get_env,
(const constructor_left _) ← pure $ get_app_fn lhs,
(const constructor_right _) ← pure $ get_app_fn rhs,
inj_name ← resolve_constant $ constructor_left ++ "inj_arrow",
pure (lhs, rhs, constructor_left, constructor_right, inj_name)
} <|> fail
"injection tactic failed, argument must be an equality proof where lhs and rhs are of the form (c ...), where c is a constructor",
if constructor_left = constructor_right then do
-- C.inj_arrow, for a given constructor C of datatype D, has type
--
-- ∀ (A₁ ... Aₙ) (x₁ ... xₘ) (y₁ ... yₘ), C x₁ ... xₘ = C y₁ ... yₘ
-- → ∀ ⦃P : Sort u⦄, (x₁ = y₁ → ... → yₖ = yₖ → P) → P
--
-- where the Aᵢ are parameters of D and the xᵢ/yᵢ are arguments of C.
-- Note that if xᵢ/yᵢ are propositions, no equation is generated, so the
-- number of equations is not necessarily the constructor arity.
-- First, we find out how many equations we need to intro later.
inj ← mk_const inj_name,
inj_type ← infer_type inj,
inj_arity ← get_pi_arity inj_type,
let num_equations :=
(inj_type.nth_binding_body (inj_arity - 1)).binding_domain.pi_arity,
-- Now we generate the actual proof of the target.
tgt ← target,
proof ← mk_mapp inj_name (list.repeat none (inj_arity - 3) ++ [some h, some tgt]),
eapply proof,
intron_with num_equations ns base offset
else do
tgt ← target,
let inductive_name := constructor_left.get_prefix,
pr ← mk_app (inductive_name <.> "no_confusion") [tgt, lhs, rhs, h],
exact pr,
return ([], ns)
/--
Simplify the equation `h` using injectivity of constructors. See
`injection_with`. Returns the hypotheses that were added to the context, or an
empty list if the goal was solved by contradiction.
-/
meta def injection (h : expr) (base := `h) (offset := some 1)
: tactic (list expr) :=
prod.fst <$> injection_with h [] base offset
private meta def injections_with_inner (base : name) (offset : option ℕ)
: ℕ → list expr → list name → tactic unit
| 0 lc ns := fail "recursion depth exceeded"
| (n+1) [] ns := skip
| (n+1) (h :: lc) ns := do
o ← try_core (injection_with h ns base offset),
match o with
| none := injections_with_inner (n+1) lc ns
| some ([], _) := skip -- This means that the contradiction part was triggered and the goal is done
| some (t, ns') := injections_with_inner n (t ++ lc) ns'
end
/--
Simplifies equations in the context using injectivity of constructors. For
each equation `h : C x₁ ... xₙ = D y₁ ... yₘ` in the context, where `C` and `D`
are constructors of the same data type, `injections_with` does the following:
- If `C = D`, it adds equations `x₁ = y₁`, ..., `xₙ = yₙ`.
- If `C ≠ D`, it solves the goal by contradiction.
See `injection_with` for details, including information about `base` and
`offset`.
`injections_with` also recurses into the new equations `xᵢ = yᵢ`. If it has to
recurse more than five times, it fails.
-/
meta def injections_with (ns : list name) (base := `h) (offset := some 1)
: tactic unit :=
do lc ← local_context,
injections_with_inner base offset 5 lc ns
end tactic