(* * Copyright 2020, Data61, CSIRO (ABN 41 687 119 230) * * SPDX-License-Identifier: BSD-2-Clause *) theory Extract_Conjunct imports "Main" Eisbach_Tools.Eisbach_Methods begin section \Extracting conjuncts in the conclusion\ text \ Methods for extracting a conjunct from a nest of conjuncts in the conclusion of a goal, typically by pattern matching. When faced with a conclusion which is a big conjunction, it is often the case that a small number of conjuncts require special attention, while the rest can be solved easily by @{method clarsimp}, @{method auto} or similar. However, sometimes the method that would solve the bulk of the conjuncts would put some of the conjuncts into a more difficult or unsolvable state. The higher-order methods defined here provide an efficient way to select a conjunct requiring special treatment, so that it can be dealt with first. Once all such conjuncts have been removed, the remaining conjuncts can all be solved together by some automated method. Each method takes an inner method as an argument, and selects the leftmost conjunct for which that inner method succeeds. The methods differ according to what they do with the selected conjunct. See below for more information and some simple examples. \ context begin subsection \Focused conjunct with context\ text \ We define a predicate which allows us to identify a particular sub-tree and its context within a nest of conjunctions. We express this sub-tree-with-context using a function which reconstructs the original nest of conjunctions. The context consists of a list of parent contexts, where each parent context consists of a sibling sub-tree, and a tag indicating whether the focused sub-tree is on the left or right. Rebuilding the original tree works from the focused sub-tree up towards the root of the original structure. This sub-tree-with-context is sometimes known as a zipper. \ private fun focus_conj :: "bool \ bool list \ bool" where "focus_conj current [] = current" | "focus_conj current (sibling # parents) = focus_conj (current \ sibling) parents" private definition "focus \ focus_conj" private definition "tag t P \ P" private lemmas focus_defs = focus_def tag_def private abbreviation "left \ tag Left" private abbreviation "right \ tag Right" private lemma focus_example: "focus C [right B, left D, left E, right A] \ A \ ((B \ C) \ D) \ E" unfolding focus_defs by auto subsection \Moving the focus\ text \ We now prove some rules which allow us to switch between focused and unfocused structures, and to move the focus around. Some versions of these rules carry an extra conjunct @{term E} outside the structure. Once we find the conjunct we want, this @{term E} allows to keep track of it while we reassemble the rest of the original structure. First, we have rules for going between focused and unfocused structures. \ private lemma focus_top_iff: "E \ focus P [] \ E \ P" unfolding focus_def by simp private lemmas to_focus = focus_top_iff[where E=True, simplified, THEN iffD1] private lemmas from_focusE = focus_top_iff[THEN iffD2] private lemmas from_focus = from_focusE[where E=True, simplified] text \ Next, we have rules for moving the focus to and from the left conjunct. \ private lemma focus_left_iff: "E \ focus L (left R # P) \ E \ focus (L \ R) P" unfolding focus_defs by simp private lemmas focus_left = focus_left_iff[where E=True, simplified, THEN iffD1] private lemmas unfocusE_left = focus_left_iff[THEN iffD2] private lemmas unfocus_left = unfocusE_left[where E=True, simplified] text \ Next, we have rules for moving the focus to and from the right conjunct. \ private lemma focus_right_iff: "E \ focus R (right L # P) \ E \ focus (L \ R) P" unfolding focus_defs using conj_commute by simp private lemmas focus_right = focus_right_iff[where E=True, simplified, THEN iffD1] private lemmas unfocusE_right = focus_right_iff[THEN iffD2] private lemmas unfocus_right = unfocusE_right[where E=True, simplified] text \ Finally, we have rules for extracting the current focus. The sibling of the extracted focus becomes the new focus of the remaining structure. \ private lemma extract_focus_iff: "focus C (tag t S # P) \ (C \ focus S P)" unfolding focus_defs by (induct P arbitrary: S) auto private lemmas extract_focus = extract_focus_iff[THEN iffD2] subsection \Primitive methods for navigating a conjunction\ text \ Using these rules as transitions, we implement a machine which navigates a tree of conjunctions, searching from left to right for a conjunct for which a given method will succeed. Once a matching conjunct is found, it is extracted, and the remaining conjuncts are reassembled. \ text \From the current focus, move to the leftmost sub-conjunct.\ private method focus_leftmost = (intro focus_left)? text \Find the furthest ancestor for which the current focus is still on the right.\ private method unfocus_rightmost = (intro unfocus_right)? text \Move to the immediate-right sibling.\ private method focus_right_sibling = (rule unfocus_left, rule focus_right) text \Move to the next conjunct in right-to-left ordering.\ private method focus_next_conjunct = (unfocus_rightmost, focus_right_sibling, focus_leftmost) text \Search from current focus toward the right until we find a matching conjunct.\ private method find_match methods m = (rule extract_focus, m | focus_next_conjunct, find_match m) text \Search within nest of conjuncts, leaving remaining structure focused.\ private method extract_match methods m = (rule to_focus, focus_leftmost, find_match m) text \Move all the way out of focus, keeping track of any extracted conjunct.\ private method unfocusE = ((intro unfocusE_right unfocusE_left)?, rule from_focusE) private method unfocus = ((intro unfocus_right unfocus_left)?, rule from_focus) subsection \Methods for selecting the leftmost matching conjunct\ text \ See the introduction at the top of this theory for motivation, and below for some simple examples. \ text \ Assuming the conclusion of the goal is a nest of conjunctions, method @{text lift_conjunct} finds the leftmost conjunct for which the given method succeeds, and moves it to the front of the conjunction in the goal. \ method lift_conjunct methods m = (extract_match \succeeds \rule conjI, m\\, unfocusE) text \ Method @{text extract_conjunct} finds the leftmost conjunct for which the given method succeeds, and splits it into a fresh subgoal, leaving the remaining conjuncts untouched in the second subgoal. It is equivalent to @{method lift_conjunct} followed by @{method rule} @{thm conjI}. \ method extract_conjunct methods m = (extract_match \rule conjI, succeeds m\; unfocus?) text \ Method @{text apply_conjunct} finds the leftmost conjunct for which the given method succeeds, leaving any subgoals created by the application of that method, and a subgoal containing the remaining conjuncts untouched. It is equivalent to @{method extract_conjunct} followed by the given method, but more efficient. \ method apply_conjunct methods m = (extract_match \rule conjI, m\; unfocus?) subsection \Examples\ text \ Given an inner method based on @{method match}, which only succeeds on the desired conjunct @{term C}, @{method lift_conjunct} moves the conjunct @{term C} to the front. The body of the @{method match} here is irrelevant, since @{method lift_conjunct} always discards the effect of the method it is given. \ lemma "\ A; B; \ A; B; D; E \ \ C; D; E \ \ A \ ((B \ C) \ D) \ E" apply (lift_conjunct \match conclusion in C \ \-\\) \ \@{term C} as been moved to the front of the conclusion.\ apply (match conclusion in \C \ A \ (B \ D) \ E\ \ \-\) oops text \ Method @{method extract_conjunct} works similarly, but peels of the matched conjunct as a separate subgoal. As for @{method lift_conjunct}, the effect of the given method is discarded, so the body of the @{method match} is irrelevant. \ lemma "\ A; B; \ A; B; D; E \ \ C; D; E \ \ A \ ((B \ C) \ D) \ E" apply (extract_conjunct \match conclusion in C \ \-\\) \ \@{method extract_conjunct} gives us the matched conjunct @{term C} as a separate subgoal.\ apply (match conclusion in C \ \-\) apply blast \ \The other subgoal contains the remaining conjuncts untouched.\ apply (match conclusion in \A \ (B \ D) \ E\ \ \-\) oops text \ Method @{method apply_conjunct} goes one step further, and applies the given method to the extracted subgoal. \ lemma "\ A; B; \ A; B; D; E \ \ C; D; E \ \ A \ ((B \ C) \ D) \ E" apply (apply_conjunct \match conclusion in C \ \match premises in H: _ \ \rule H\\\) \ \We get four subgoals from applying the given method to the matched conjunct @{term C}.\ apply (match premises in H: A \ \rule H\) apply (match premises in H: B \ \rule H\) apply (match premises in H: D \ \rule H\) apply (match premises in H: E \ \rule H\) \ \The last subgoal contains the remaining conjuncts untouched.\ apply (match conclusion in \A \ (B \ D) \ E\ \ \-\) oops end end