Ltac fold_closed_hyps :=
repeat match goal with h: shift _ ?t = ?t |- _ =>
generalize (fold_closed _ h); clear h; intro h
end.
(* The following tactics help invert (destruct) a closedness hypothesis. [inversion_closed_in h] destructs a specific closedness hypothesis [h], whereas [inversion_closed] destructs all closedness hypotheses. *)
Ltac inversion_closed_in_internal h :=
(* Unfold the definition of [closed]. This exposes [lift]. *)
unfold closed in h;
(* If [lift] is applied to a constructor, simplify it. *)
rewrite expand_lift in h; simpl (@traverse _ _ _) in h; simpl_lift;
(* This may result in an equality that involves two constructors. Decompose it. *)
try (injection h; clear h; intros).
(* The above line assumes that [lift] is opaque. Otherwise, the following would be required: try (protects @lift do (injection h; clear h; intros)) We make [lift] opaque at the end of this file. *)
(* At this point, [closed] is still unfolded. This is intentional. *)
Ltac inversion_closed_in h :=
inversion_closed_in_internal h;
fold_closed_hyps.
Ltac inversion_closed :=
repeat match goal with h: closed _ _ |- _ =>
inversion_closed_in_internal h
end;
fold_closed_hyps.
(* The tactic solves a closedness goal by using whatever closedness hypotheses are available. *)
Hint Extern 1 => f_equal : construction_closed.
(* more hints to be added later on into this database *)
Ltac construction_closed :=
solve [
(* Expose the definition of [closed] in terms of [lift]. *)
unfold closed in *;
(* Presumably [lift] is applied to a constructor in the goal. Simplify it. *)
simpl_lift_goal;
(* If we are looking at a variable, the equation should look like this: [var (shift k x) = var x]. Simplify [var] away, and apply the tactic [lift_idx] to simplify [shift] away. *)
try (simpl; lift_idx);
(* Conclude. *)
eauto with omega construction_closed
].
(* The following hint proves a goal of the form [shift x v = v] when the value [v] is [k]-closed for some [k] that is less than or equal to [x]. *)
Hint Extern 1 (shift ?x ?v = ?v) =>
solve [
eapply closed_monotonic; [
eauto with typeclass_instances (* LiftLift *)
| construction_closed (* [v] is [k]-closed *)
| omega (* [k <= x] *)
]
]
: shift_closed.
(* The following tactic replaces [shift x v] with [v] in the goal when an explicit hypothesis states that [v] is closed. *)
Ltac shift_closed :=
match goal with h: closed ?x ?v |- context[shift ?x ?v] =>
replace (shift x v) with v (* no subgoals are generated;
apparently the hypothesis [h] allows Coq to recognize
that these terms are equal *)
end.
(* The following hint proves a goal of the form [subst w x v = v] when the value [v] is [k]-closed for some [k] that is less than or equal to [x]. *)
Hint Extern 1 (subst ?w ?x ?v = ?v) =>
solve [
eapply closed_subst_invariant; [
eauto with typeclass_instances
| eauto with typeclass_instances
| construction_closed
| omega
]
]
: subst_closed.
(* ------------------------------------------------------------------------------ *)
(* A few properties of the function [rotate]. *)
(* This lemma ensures that we got the definition right. It is otherwise unused. *)

Lemma rotate_characterization:
forall n k,
(k = 0 -> rotate n k = n) /\
(k > 0 -> k <= n -> rotate n k = k - 1) /\
(k > n -> rotate n k = k).