diff options
Diffstat (limited to 'src/Smtpredicate.v')
-rw-r--r-- | src/Smtpredicate.v | 5138 |
1 files changed, 5138 insertions, 0 deletions
diff --git a/src/Smtpredicate.v b/src/Smtpredicate.v new file mode 100644 index 0000000..3594d53 --- /dev/null +++ b/src/Smtpredicate.v @@ -0,0 +1,5138 @@ +Require Import Coq.Logic.Decidable. +Require Import Coq.Structures.Equalities. +Require Import Coq.Numbers.Cyclic.Int63.Int63. +Require Import Coq.Sorting.Sorted. +Require Import Coq.Classes.RelationClasses. + +Require Import TVSMT.Coqlib. +Require Import TVSMT.Maps. +Require Import TVSMT.Errors. +Require Import TVSMT.Sat. +Require Import TVSMT.Common. +Require Import TVSMT.Predicate. +Require Import TVSMT.Hashtree. +Import TVSMT.Predicate.PredicateNotation. + +Require SMTCoq.SMTCoq. +Import SMTCoq.SMT_terms. +Import SMTCoq.SMT_terms.Atom. +Import SMTCoq.SMT_terms.Form. +Import SMTCoq.Trace. + +#[local] Open Scope error_monad_scope. +#[local] Open Scope predicate. + +#[export] Hint Unfold is_true : smtpred. + +Local Notation predicate := (@pred positive). + +Parameter pred_verit_unsat : + PArray.array Atom.atom -> + PArray.array Form.form -> + PArray.array Int63.int -> + (PArray.array Form.form + * PArray.array Atom.atom + * PArray.array Int63.int + * option (PArray.array Int63.int) + * Checker_Ext.certif) +. + +Notation latom := Int63.int (only parsing). +Notation _llit := Int63.int (only parsing). +Notation lfargs := (list _llit) (only parsing). + +Inductive lform : Type := +| LFatom (_:latom) +| LFtrue +| LFfalse +| LFnot2 (_:Int63.int) (_:_llit) +| LFand (_:lfargs) +| LFor (_:lfargs) +| LFimp (_:lfargs) +| LFxor (_:_llit) (_:_llit) +| LFiff (_:_llit) (_:_llit) +| LFite (_:_llit) (_:_llit) (_:_llit) +(* Bit-blasting predicate (theory of bit vectors): + bbT a [l1;...;ln] means [l1;...;ln] is the bitwise representation of a + (in little endian) + WARNING: this is a slight infringement of stratification + *) +| LFbbT (_:latom) (_:list _llit) +(* TODO: replace [list _lit] with [fargs] *) +. + +Fixpoint of_list' {A} (i: Z) (a: PArray.array A) (l: list A) := + match l with + | nil => a + | el :: b => + of_list' (i+1) (PArray.set a (Int63.of_Z i) el) b + end. + +Definition of_list {A} (d: A) (l: list A) := + of_list' 0 (PArray.make (Int63.of_Z (Zlength l)) d) l. + +Definition lform_to_form (f: lform): form := + match f with + | LFatom a => Fatom a + | LFtrue => Ftrue + | LFfalse => Ffalse + | LFnot2 i l => Fnot2 i l + | LFand args => Fand (of_list (Int63.of_Z 0) args) + | LFor args => For (of_list (Int63.of_Z 0) args) + | LFimp args => Fimp (of_list (Int63.of_Z 0) args) + | LFxor l1 l2 => Fxor l1 l2 + | LFiff l1 l2 => Fiff l1 l2 + | LFite l1 l2 l3 => Fite l1 l2 l3 + | LFbbT a b => FbbT a b + end. + +Definition form_to_lform (f: form): lform := + match f with + | Fatom a => LFatom a + | Ftrue => LFtrue + | Ffalse => LFfalse + | Fnot2 i l => LFnot2 i l + | Fand args => LFand (Misc.to_list args) + | For args => LFor (Misc.to_list args) + | Fimp args => LFimp (Misc.to_list args) + | Fxor l1 l2 => LFxor l1 l2 + | Fiff l1 l2 => LFiff l1 l2 + | Fite l1 l2 l3 => LFite l1 l2 l3 + | FbbT a b => LFbbT a b + end. + +Definition int_dec: forall a b: Int63.int, {a = b} + {a <> b}. +Proof. + intros. + destruct (Int63.eqb a b) eqn:?; [left|right]. + now apply Int63.eqb_correct. + unfold not; intros. apply Int63.eqb_complete in H. + now rewrite H in Heqb0. +Defined. + +Definition form_eq_dec : + forall d1 d2: lform, {d1 = d2} + {d1 <> d2}. +Proof. + intros. + pose proof int_dec. + pose proof (list_eq_dec H). + decide equality. +Defined. + +Definition atom_eq_dec : + forall d1 d2: atom, {d1 = d2} + {d1 <> d2}. +Proof. + intros. destruct (eqb d1 d2) eqn:?. apply eqb_spec in Heqb. auto. + right. unfold not; intros. apply Typ.not_is_true_eq_false in Heqb. + apply Heqb. unfold is_true. apply eqb_spec. trivial. +Defined. + +Module AtomDec <: MiniDecidableType. + Definition t := atom. + Definition eq_dec := atom_eq_dec. +End AtomDec. +Module AtomHash := Make_UDT(AtomDec). +Module Import AH := HashTree(AtomHash). +Module Import AH_Prop := HashTreeProperties(AtomHash). + +Module FormDec <: MiniDecidableType. + Definition t := lform. + Definition eq_dec := form_eq_dec. +End FormDec. +Module FormHash := Make_UDT(FormDec). +Module Import FH := HashTree(FormHash). +Module Import FH_Prop := HashTreeProperties(FormHash). + +Definition pred_list: Type := (predicate * list (positive * predicate)). + +Definition to_equiv (p: list (positive * predicate)) := + map (fun x => equiv (Pbase (fst x)) (snd x)) p. + +Definition eval_pred_list (p: pred_list) (a: PTree.t bool): option bool := + eval_hash_pred + (Pand (fst p) + (fold_left Pand (to_equiv (snd p)) Ptrue)) a. + +Fixpoint unnest_predicate (fresh: positive) (p: predicate) + : (pred_list * positive) := + match p with + | Ptrue => (Ptrue, nil, fresh) + | Pfalse => (Pfalse, nil, fresh) + | Pundef => (Pundef, nil, fresh) + | Pbase p' => (Pbase p', nil, fresh) + | Pand p1 p2 => + let '(p1', p1l, fresh1) := unnest_predicate fresh p1 in + let '(p2', p2l, fresh2) := unnest_predicate fresh1 p2 in + let nvar := Pbase fresh2 in + (nvar, (fresh2, (Pand p1' p2')) :: p2l ++ p1l, Pos.succ fresh2) + | Por p1 p2 => + let '(p1', p1l, fresh1) := unnest_predicate fresh p1 in + let '(p2', p2l, fresh2) := unnest_predicate fresh1 p2 in + let nvar := Pbase fresh2 in + (nvar, (fresh2, (Por p1' p2')) :: p2l ++ p1l, Pos.succ fresh2) + | Pimp p1 p2 => + let '(p1', p1l, fresh1) := unnest_predicate fresh p1 in + let '(p2', p2l, fresh2) := unnest_predicate fresh1 p2 in + let nvar := Pbase fresh2 in + (nvar, (fresh2, (Pimp p1' p2')) :: p2l ++ p1l, Pos.succ fresh2) + | Pnot p1 => + let '(p1', p1l, fresh1) := unnest_predicate fresh p1 in + let nvar := Pbase fresh1 in + (nvar, (fresh1, Pnot p1') :: p1l, Pos.succ fresh1) + end. + +Notation form_t := (PArray.array SMT_terms.Form.form). +Notation atom_t := (PArray.array SMT_terms.Atom.atom). +Notation root_t := (PArray.array Int63.int) (only parsing). + +Fixpoint all_preds (tree: PTree.t unit) (p: predicate): PTree.t unit := + match p with + | Ptrue | Pfalse | Pundef => tree + | Pbase p' => PTree.set p' tt tree + | Pand p1 p2 | Por p1 p2 | Pimp p1 p2 => all_preds (all_preds tree p1) p2 + | Pnot p1 => all_preds tree p1 + end. + +Definition of_P (a: positive) := Int63.of_Z (Zpos a - 2). +Definition to_P (a: Int63.int) := Z.to_pos (Int63.to_Z a + 2). + +Definition P_of_P (a: positive) := Int63.of_Z ((Zpos a - 2) * 2). +Definition N_of_P (a: positive) := Int63.of_Z ((Zpos a - 2) * 2 + 1). + +Definition of_map {A} (l: PMap.t A): PArray.array A := + PTree.fold (fun st i a => PArray.set st (of_P i) a) + (snd l) + (PArray.make (Int63.of_pos (max_key (snd l))) (fst l)). + +Definition of_tree {A} (d: A) (l: PTree.t A): option (PArray.array A) := + let max := ((max_key l) + 1)%positive in + if Z.pos max <? Int63.wB then + Some (PTree.fold (fun st i a => PArray.set st (of_P i) a) + l (PArray.make (of_P max) d)) + else None. + +Lemma max_key_empty : + forall A (m: PTree.t A), + PTree.bempty m = true -> + max_key m = 1%positive. +Proof. + intros. + rewrite PTree.bempty_correct in H. + unfold max_key. + rewrite PTree.elements_extensional with (n:= PTree.Leaf); auto. + intros. rewrite PTree.gleaf; auto. +Qed. + +Lemma max_key_list_in : forall l, + ((fold_right Pos.max 1%positive l) = 1%positive /\ l = nil) + \/ (In (fold_right Pos.max 1%positive l) l /\ l <> nil). +Proof. + induction l ; intros. + - auto. + - inv IHl. + + inv H. right. simpl. + split; [lia | congruence]. + + inv H. right. simpl. + split; [| congruence]. + edestruct Pos.max_spec with (n:= a) (m:= (fold_right Pos.max 1%positive l)). + * inv H. rewrite H3. right. auto. + * inv H. rewrite H3. left. auto. +Qed. + +Lemma max_key_list_not_nil : forall A l (m: PTree.t A), + (l <> nil) -> + (forall k v, m ! k = Some v -> In k l) -> + (forall k, In k l -> exists v, m ! k = Some v) -> + exists x, m ! (fold_right Pos.max 1%positive l) = Some x. +Proof. + intros A l. + generalize (max_key_list_in l). + intros [HCASE | HCASE]; intros. + - inv HCASE. congruence. + - inv HCASE. + eapply H1; eauto. +Qed. + +Lemma PTree_bempty_false_exists : forall A (m: PTree.t A), + PTree.bempty m = false -> + exists x v, m ! x = Some v. +Proof. + induction m ; intros. + - simpl in *. congruence. + - simpl in *. + destruct o. + + exists xH. simpl. eauto. + + apply andb_false_iff in H. + inv H. + * exploit IHm1; eauto. intros [x [v HSOME]]. + exists (xO x). eauto. + * exploit IHm2; eauto. intros [x [v HSOME]]. + exists (xI x). eauto. +Qed. + +Lemma max_key_in : + forall A (m: PTree.t A), + PTree.bempty m = false -> + exists x, m ! (max_key m) = Some x. +Proof. + unfold max_key. + intros * BEMPTY. + eapply max_key_list_not_nil with (l := (map fst (PTree.elements m))). + - intro. apply map_eq_nil in H. + generalize (PTree_bempty_false_exists _ m BEMPTY); eauto. + intros [x [v HGET]]. + eapply PTree.elements_correct in HGET. + rewrite H in *. inv HGET. + - intros. + eapply PTree.elements_correct in H. + apply in_map_iff. exists (k,v). auto. + - intros. + apply in_map_iff in H. destruct H as [[kk v] [Hfst Helt]]. subst. + simpl. eapply PTree.elements_complete in Helt. + eauto. +Qed. + +Lemma max_key_ext1 : + forall A (m: PTree.t A) m', + (forall x, m ! x = m' ! x) -> + PTree.bempty m = false -> + (max_key m <= max_key m')%positive. +Proof. + intros * ALL BEMPTY. + pose proof (max_key_in _ m BEMPTY) as X. inversion X as [x IN]; clear X. + pose proof IN as Y. rewrite ALL in Y. + assert (BEMPTY2: PTree.bempty m' = false). + { apply not_true_iff_false; unfold not; intros BEMPTY2. + apply not_true_iff_false in BEMPTY. apply BEMPTY. + apply PTree.bempty_correct; intros. + rewrite ALL. eapply PTree.bempty_correct in BEMPTY2; eauto. + } + pose proof (max_key_in _ m' BEMPTY2) as XX. inversion XX as [y IN2]; clear XX. + eapply max_key_correct; eauto. +Qed. + +Lemma max_key_ext2 : + forall A (m: PTree.t A) m', + (forall x, m ! x = m' ! x) -> + PTree.bempty m' = false -> + (max_key m' <= max_key m)%positive. +Proof. + intros * ALL BEMPTY. + pose proof (max_key_in _ m' BEMPTY) as X. inversion X as [x IN]; clear X. + pose proof IN as Y. rewrite <- ALL in Y. + assert (BEMPTY2: PTree.bempty m = false). + { apply not_true_iff_false; unfold not; intros BEMPTY2. + apply not_true_iff_false in BEMPTY. apply BEMPTY. + apply PTree.bempty_correct; intros. + rewrite <- ALL. eapply PTree.bempty_correct in BEMPTY2; eauto. + } + pose proof (max_key_in _ m BEMPTY2) as XX. inversion XX as [y IN2]; clear XX. + eapply max_key_correct; eauto. +Qed. + +Lemma max_key_ext : + forall A (m: PTree.t A) m', + (forall x, m ! x = m' ! x) -> + max_key m' = max_key m. +Proof. + unfold max_key; intros; erewrite PTree.elements_extensional; eauto. +Qed. + +Lemma max_key_map_le : + forall A B (m: PTree.t A) (m': PTree.t B), + (forall x, m ! x <> None <-> m' ! x <> None) -> + PTree.bempty m = false -> + (max_key m <= max_key m')%positive. +Proof. + intros * ALL BEMPTY. + assert (BEMPTY2: PTree.bempty m' = false). + { apply not_true_iff_false; unfold not; intros BEMPTY2. + apply not_true_iff_false in BEMPTY. apply BEMPTY. + apply PTree.bempty_correct; intros. + case_eq (m ! x); intros; auto. + specialize (ALL x). rewrite H in *. + rewrite PTree.bempty_correct in BEMPTY2. + specialize (BEMPTY2 x). + intuition congruence. + } + pose proof (max_key_in _ m BEMPTY) as X. inversion X as [x INx]; clear X. + assert (Y : exists y, m' ! (max_key m) = Some y). + { case_eq (m' ! (max_key m)); intros; eauto. + specialize (ALL (max_key m)). intuition congruence. + } + inversion Y as [y INy]. + try solve [eapply max_key_correct; eauto]. +Qed. + +Lemma ptree_map_same_keys: forall A B (f: positive -> A -> B) m, + forall x, m ! x <> None <-> (PTree.map f m) ! x <> None. +Proof. + split; intros. + - intro. elim H. + rewrite PTree.gmap in H0. + case_eq (m ! x); intros; auto. + rewrite H1 in H0; auto. + simpl in H0. congruence. + - intro. elim H. + rewrite PTree.gmap. + rewrite H0. auto. +Qed. + +Lemma max_key_map : + forall A B (m: PTree.t A) (f: positive -> A -> B), + max_key m = max_key (PTree.map f m). +Proof. + intros *. destruct (PTree.bempty m) eqn:Heqb. + - assert (BEMPTY2: PTree.bempty (PTree.map f m) = true). + { apply PTree.bempty_correct; intros. + eapply PTree.bempty_correct in Heqb; eauto. + rewrite PTree.gmap; eauto. + rewrite Heqb. auto. + } + rewrite ! max_key_empty; auto. + - assert (BEMPTY2: PTree.bempty (PTree.map f m) = false). + { apply not_true_iff_false; intro BEMPTY2. + apply not_true_iff_false in Heqb. apply Heqb. + apply PTree.bempty_correct; intros. + eapply PTree.bempty_correct with (x:= x) in BEMPTY2; eauto. + rewrite PTree.gmap in BEMPTY2; eauto. + unfold option_map in *. destruct_match; easy. + } + eapply Pos.le_antisym; eauto. + + eapply max_key_map_le; eauto. + eapply ptree_map_same_keys; eauto. + + eapply max_key_map_le; eauto. + split. + * intros. eapply ptree_map_same_keys; eauto. + * intros. eapply ptree_map_same_keys in H; eauto. +Qed. + +Lemma gt_1_map_iff : + forall A B (m: PTree.t A) (f: positive -> A -> B), + gt_1 m <-> gt_1 (PTree.map f m). +Proof. + unfold gt_1 in *; simplify. + split. + - simplify. + rewrite PTree.gmap in *. unfold option_map in *. + destruct (m ! x) eqn:?. eapply H. eauto. easy. + - simplify. + eapply H; eauto. + rewrite PTree.gmap. unfold option_map in *. + rewrite H0. easy. +Qed. + +(* From Coq.micromega Require Import RingMicromega QMicromega EnvRing Tauto ZifyComparison ZifyBool ZifyInt63. *) +(* Ltac Zify.zify_post_hook ::= Z.div_mod_to_equations. *) + +Lemma bounded_lt : + forall a b n, a < b -> 0 <= a < n -> 0 <= b < n -> a mod n < b mod n. +Proof. + intros; rewrite !Z.mod_small; lia. Qed. + +Lemma max_key_set_le : forall A (m: PTree.t A) k v, + (max_key m <= max_key (PTree.set k v m))%positive. +Proof. + intros. + case_eq (Pos.leb (max_key m) (max_key (PTree.set k v m)))%positive; intros. + - apply Pos.leb_le; auto. + - apply Pos.leb_gt in H; auto. + apply Pos.lt_gt in H. + exploit max_not_present; eauto. + intros. + assert (k <= max_key (PTree.set k v m))%positive. + { eapply max_key_correct; eauto. + rewrite PTree.gss; auto. + } + case_eq (PTree.bempty m); intros. + + exploit max_key_empty; eauto. + intros. rewrite H3 in *. + lia. + + exploit (max_key_in _ m); eauto. + intros [vmk HSome]. + rewrite PTree.gso in H0. + * congruence. + * intro; subst. + rewrite PTree.gss in H0. + congruence. +Qed. + +Lemma PArray_length_fold_set : forall A B a f (t: PTree.t A) (g: A -> B), + PArray.length (PTree.fold (fun st i a => PArray.set st (f i) (g a)) t a) + = PArray.length a. +Proof. + intros. + eapply PTree_Properties.fold_rec; eauto. + intros. + rewrite PArray.length_set. + auto. +Qed. + +Lemma of_pos_rec_inj: forall p1 p2 + (PVALID: 0 <= Z.pos p1 < wB) + (KVALID: 0 <= Z.pos p2 < wB), + (φ (of_pos_rec size p1) = φ (of_pos_rec size p2))%int63 -> + p1 = p2. +Proof. + unfold wB. intros. + rewrite ! of_pos_rec_spec in H; auto. + rewrite ! Z.mod_small in H; try lia. +Qed. + +Lemma of_P_inj : forall k p + (PVALID: 0 <= Z.pos p - 2 < wB) + (KVALID: 0 <= Z.pos k - 2 < wB), + of_P k = of_P p -> k = p. +Proof. + unfold of_P, of_Z, wB, of_pos; intros. + do 2 destruct_match; try lia. + - generalize (of_pos_rec_spec size (Nat.le_refl _) p0). + rewrite Z.mod_small; auto. intros. + rewrite <- H in H0. + rewrite to_Z_0 in H0. + lia. + - generalize (of_pos_rec_spec size (Nat.le_refl _) p0). + rewrite Z.mod_small; auto. intros. + rewrite H in H0. + rewrite to_Z_0 in H0. + lia. + - generalize (of_pos_rec_spec size (Nat.le_refl _) p0). + generalize (of_pos_rec_spec size (Nat.le_refl _) p1). + rewrite !Z.mod_small; auto. intros. + assert (p0 = p1). + { eapply of_pos_rec_inj; eauto. + congruence. + } + lia. +Qed. + +Definition tree_a_wf (t: PTree.t atom) := + 1 < Zpos (max_key t) < wB + /\ gt_1 t + /\ t ! 2 = Some (Acop CO_xH) + /\ t ! 3 = Some (Auop UO_Zpos (of_P 2)) + /\ t ! 4 = Some (Auop UO_Zopp (of_P 3)) +. + +Definition tree_f_wf (t: PTree.t lform) := + 1 < Zpos (max_key t) + /\ (Zpos (max_key t) - 2) * 2 + 1 < wB + /\ gt_1 t + /\ t ! 2 = Some (LFtrue) + /\ t ! 3 = Some (LFfalse) +. + +(* Abstracting over the type of elements of the tree *) +Definition tree_key_wf A (t: PTree.t A) := + 1 < Zpos (max_key t) + /\ ((Zpos (max_key t) - 2) * 2 + 1 < wB + \/ Zpos (max_key t) < wB) + /\ gt_1 t. + +Lemma of_tree_of_P : + forall A d t p (v: A) a + (TAWF: tree_key_wf _ t), + gt_1 t -> + t ! p = Some v -> + of_tree d t = Some a -> + PArray.get a (of_P p) = v. +Proof. + unfold of_tree. + intros. destruct_match; try discriminate. inv H1. + assert + (X: 0 <= Z.pos (max_key t) - 1 <= + to_Z (PArray.length (PTree.fold (fun st i a => PArray.set st (of_P i) a) t + (PArray.make (of_P (max_key t + 1)) d)))). + { erewrite PArray_length_fold_set; eauto. + rewrite PArray.length_make. + unfold PArray.max_length. + unfold wB, size in Heqb. + inv TAWF. + destruct_match; try lia. + - unfold of_P. rewrite of_Z_spec. + rewrite Z.mod_small; try lia. + - rewrite Misc.max_int_wB. + unfold wB in *. lia. + } + unfold gt_1 in *. + generalize dependent H. + generalize dependent Heqb. + generalize dependent v. + generalize dependent p. + generalize dependent X. + apply PTree_Properties.fold_rec; eauto. intros. + - rewrite <- H in H1. + eapply H0; auto. + + erewrite max_key_ext; eauto. + + erewrite max_key_ext; eauto. + + intros. rewrite H in H3; eauto. + - intros. + now rewrite PTree.gempty in H0. + - intros. + destruct (peq k p); subst. + + rewrite PTree.gss in H2. inv H2. + rewrite PArray.get_set_same; auto. + apply Int63.ltb_spec. + rewrite PArray.length_set in X. + destruct X as [XX X]. + eapply Z.lt_le_trans; eauto. + * unfold of_P. rewrite of_Z_spec. + assert (p <= max_key (PTree.set p v0 m))%positive. + { eapply max_key_correct; eauto. + rewrite PTree.gss; auto. + } + rewrite Z.mod_small; try lia. + inv TAWF. inv H5. unfold gt_1 in *. + specialize (H7 _ _ H0). + apply max_key_correct with (hi:= p) in H0; eauto. + apply Pos2Z.pos_le_pos in H0. lia. + + rewrite PTree.gso in H2; auto. + rewrite PArray.get_set_other. + * apply H1; auto. + -- rewrite PArray.length_set in X. + destruct X as [XX X]. + split; [lia |]. + eapply Z.le_trans; eauto. + assert (max_key m <= max_key (PTree.set k v m))%positive + by (eapply max_key_set_le; eauto). + lia. + -- rewrite PArray.length_set in X. + assert (max_key m <= max_key (PTree.set k v m))%positive + by (eapply max_key_set_le; eauto). + rewrite Z.ltb_lt in *. + lia. + -- intros. eapply H3; eauto. + erewrite PTree.gso; eauto. + congruence. + * intro; subst. + eapply of_P_inj in H4; eauto. + apply Z.ltb_lt in Heqb. + rewrite PArray.length_set in X. + -- assert (1 < p)%positive. + { eapply H3; eauto. + rewrite PTree.gso; eauto. + } + assert (p <= max_key (PTree.set k v m))%positive. + { eapply max_key_correct; eauto. + rewrite PTree.gso; eauto. + } + lia. + -- assert (1 < k)%positive. + { eapply H3; eauto. + rewrite PTree.gss; auto. + } + assert (k <= max_key (PTree.set k v m))%positive. + { eapply max_key_correct; eauto. + rewrite PTree.gss; auto. + } + apply Z.ltb_lt in Heqb; auto. + rewrite PArray.length_set in X. + lia. +Qed. + +Definition to_smt_i ah p := + match p with + | Pbase p => match AH.find_tree (Aapp (of_P p) nil) ah with + | Some pi => + if (1 <? Zpos pi) && (Zpos pi <? wB) && (0 <=? Z.pos p - 2) && (Z.pos p - 2 <? wB) + then Some (of_P pi) + else None + | _ => None + end + | Ptrue => Some (Int63.of_Z 1) + | Pfalse => Some (Int63.of_Z 2) + | _ => None + end. + +Fixpoint get_index_acc {A} (dec: A -> A -> bool) (l : list A) + (a : A) (acc : nat) {struct l} : option nat := + match l with + | nil => None + | x :: m => if dec x a then Some acc else get_index_acc dec m a (acc + 1)%nat + end. + +Definition get_index {A} dec (l : list A) (a : A) := get_index_acc dec l a 0. + +Definition form_eqb (f1 f2: form) := + match f1, f2 with + | Fatom i1, Fatom i2 => Int63.eqb i1 i2 + | Ftrue, Ftrue => true + | Ffalse, Ffalse => true + | Fnot2 a1 b1, Fnot2 a2 b2 + | Fiff a1 b1, Fiff a2 b2 + | Fxor a1 b1, Fxor a2 b2 => Int63.eqb a1 a2 && Int63.eqb b1 b2 + | Fand args1, Fand args2 + | For args1, For args2 + | Fimp args1, Fimp args2 => list_beq Int63.eqb (Misc.to_list args1) (Misc.to_list args2) + | Fite a1 b1 c1, Fite a2 b2 c2 => Int63.eqb a1 a2 && Int63.eqb b1 b2 && Int63.eqb c1 c2 + | _, _ => false + end. + +Definition get_fi (lf: list form) (fresh: Z) (f: form): Z * Z * list form := + match get_index form_eqb lf f with Some i => (Z.of_nat i, fresh, lf) | _ => (fresh, Z.succ fresh, lf ++ f::nil) end. + +Definition get_ai (lf: list atom) (fresh: Z) (f: atom): Z * Z * list atom := + match get_index Atom.eqb lf f with Some i => (Z.of_nat i, fresh, lf) | _ => (fresh, Z.succ fresh, lf ++ f::nil) end. + +Definition to_smt_and hf ha p p1 p2 := + let (a1, ha1) := AH.hash_value 1%positive (Abop BO_Zlt p1 p2) ha in + let (a2, ha2) := AH.hash_value 1%positive (Abop (BO_eq Typ.TZ) p p1) ha1 in + let (a3, ha3) := AH.hash_value 1%positive (Abop (BO_eq Typ.TZ) p p2) ha2 in + let (i1, hf1) := FH.hash_value 1%positive (LFatom (of_P a1)) hf in + let (i2, hf2) := FH.hash_value 1%positive (LFatom (of_P a2)) hf1 in + let (i3, hf3) := FH.hash_value 1%positive (LFatom (of_P a3)) hf2 in + let (i4, hf4) := FH.hash_value 1%positive (LFite (P_of_P i1) (P_of_P i2) (P_of_P i3)) hf3 in + (hf4, ha3, i4). + +Definition to_smt_or hf ha p p1 p2 := + let '(a1, ha1) := AH.hash_value 1%positive (Abop BO_Zlt p1 p2) ha in + let '(a2, ha2) := AH.hash_value 1%positive (Abop (BO_eq Typ.TZ) p p2) ha1 in + let '(a3, ha3) := AH.hash_value 1%positive (Abop (BO_eq Typ.TZ) p p1) ha2 in + let '(i1, hf1) := FH.hash_value 1%positive (LFatom (of_P a1)) hf in + let '(i2, hf2) := FH.hash_value 1%positive (LFatom (of_P a2)) hf1 in + let '(i3, hf3) := FH.hash_value 1%positive (LFatom (of_P a3)) hf2 in + let '(i4, hf4) := FH.hash_value 1%positive (LFite (P_of_P i1) (P_of_P i2) (P_of_P i3)) hf3 in + (hf4, ha3, i4). + +Definition to_smt_imp hf ha p p1 p2 := + let '(a1, ha1) := AH.hash_value 1%positive (Abop BO_Zminus (of_P 3) p1) ha in + let '(a2, ha2) := AH.hash_value 1%positive (Abop BO_Zplus (of_P a1) p2) ha1 in + let '(a3, ha3) := AH.hash_value 1%positive (Abop BO_Zlt (of_P 3) (of_P a2)) ha2 in + let '(a4, ha4) := AH.hash_value 1%positive (Abop (BO_eq Typ.TZ) p (of_P 3)) ha3 in + let '(a5, ha5) := AH.hash_value 1%positive (Abop (BO_eq Typ.TZ) p (of_P a2)) ha4 in + let '(i1, hf1) := FH.hash_value 1%positive (LFatom (of_P a3)) hf in + let '(i2, hf2) := FH.hash_value 1%positive (LFatom (of_P a4)) hf1 in + let '(i3, hf3) := FH.hash_value 1%positive (LFatom (of_P a5)) hf2 in + let '(i4, hf4) := FH.hash_value 1%positive (LFite (P_of_P i1) (P_of_P i2) (P_of_P i3)) hf3 in + (hf4, ha5, i4). + +Definition to_smt_not hf ha p p1 := + let '(a1, ha1) := AH.hash_value 1%positive (Auop UO_Zopp p1) ha in + let '(a2, ha2) := AH.hash_value 1%positive (Abop (BO_eq Typ.TZ) p (of_P a1)) ha1 in + let '(i1, hf1) := FH.hash_value 1%positive (LFatom (of_P a2)) hf in + (hf1, ha2, i1). + +Definition to_smt_l (el: positive * predicate) (state: option (list Int63.int * FH.hash_tree * AH.hash_tree)) := + match state with + | Some (r, f, a) => + let to_smt_i := to_smt_i a in + match el with + | (p, Pand p1 p2) => + match to_smt_i (Pbase p), to_smt_i p1, to_smt_i p2 with + | Some p', Some p1', Some p2' => + let '(f', a', r') := to_smt_and f a p' p1' p2' in + Some (r++(P_of_P r'::nil), f', a') + | _, _, _ => None + end + | (p, Por p1 p2) => + match to_smt_i (Pbase p), to_smt_i p1, to_smt_i p2 with + | Some p', Some p1', Some p2' => + let '(f', a', r') := to_smt_or f a p' p1' p2' in + Some (r++(P_of_P r'::nil), f', a') + | _, _, _ => None + end + | (p, Pimp p1 p2) => + match to_smt_i (Pbase p), to_smt_i p1, to_smt_i p2 with + | Some p', Some p1', Some p2' => + let '(f', a', r') := to_smt_imp f a p' p1' p2' in + Some (r++(P_of_P r'::nil), f', a') + | _, _, _ => None + end + | (p, Pnot p1) => + match to_smt_i (Pbase p), to_smt_i p1 with + | Some p', Some p1' => + let '(f', a', r') := to_smt_not f a p' p1' in + Some (r++(P_of_P r'::nil), f', a') + | _, _ => None + end + | _ => None + end + | _ => None + end. + +Definition declare_atoms_with_bounds + (a: Z * list Int63.int * FH.hash_tree * AH.hash_tree) := + let '(num, cr, cf, ca) := a in + let (happ, ca_inter) := AH.hash_value 1%positive (Aapp (Int63.of_Z num) nil) ca in + let '((hlt1, hlt2), ca') := + AH.hash_value2 1%positive + (Abop BO_Zle (of_P 4) (of_P happ), + Abop BO_Zge (of_P 3) (of_P happ)) ca_inter in + let '((halt1, halt2), cf_inter) := + FH.hash_value2 1%positive (LFatom (of_P hlt1), LFatom (of_P hlt2)) cf in + let (hlfand, cf') := + FH.hash_value 1%positive (LFand (P_of_P halt1 :: P_of_P halt2 :: nil)) cf_inter in + (num + 1, cr ++ (P_of_P hlfand :: nil), cf', ca'). + +Definition check_tree_f_wf (f: PTree.t lform) := + let max := Z.pos (max_key f) in + (1 <? max) + && ((max - 2) * 2 + 1 <? wB) + && match f ! 1 with None => true | _ => false end + && match f ! 2 with Some LFtrue => true | _ => false end + && match f ! 3 with Some LFfalse => true | _ => false end. + +Definition check_tree_a_wf (a: PTree.t atom) := + let max := Z.pos (max_key a) in + (1 <? max) + && (max <? wB) + && match a ! 1 with None => true | _ => false end + && match a ! 2 with Some (Acop CO_xH) => true | _ => false end + && match a ! 3 with Some (Auop UO_Zpos v) => (v =? 0)%int63 | _ => false end + && match a ! 4 with Some (Auop UO_Zopp v) => (v =? 1)%int63 | _ => false end. + +Definition to_smt (max: positive) (p: pred_list): option (root_t * form_t * atom_t) := + let (p', pl) := p in + let '(_, init_forms) := FH.hash_value2 1%positive (LFtrue, LFfalse) FH.empty in + let (hpone, ia_inter1) := AH.hash_value 1%positive (Acop CO_xH) AH.empty in + let (hzone, ia_inter2) := AH.hash_value 1%positive (Auop UO_Zpos (of_P hpone)) ia_inter1 in + let (hzmone, init_atoms) := AH.hash_value 1%positive (Auop UO_Zopp (of_P hzone)) ia_inter2 in + let '(v, r_defs, form_defs, atom_defs) := + fold_right (fun _ => declare_atoms_with_bounds) + (0, nil, init_forms, init_atoms) + (repeat tt (Pos.to_nat max - 1)) + in + match + fold_right to_smt_l (Some (r_defs, form_defs, atom_defs)) pl, + to_smt_i atom_defs p' + with + | Some (r, f, a), Some p'' => + let '(a1, fin_a) := AH.hash_value 1%positive (Abop (BO_eq Typ.TZ) p'' (Int63.of_Z 1)) a in + let '(f1, fin_f) := FH.hash_value 1%positive (LFatom (of_P a1)) f in + let fin_r := r ++ (N_of_P f1)::nil in + match + of_tree Ftrue (PTree.map (fun _ => lform_to_form) fin_f), + of_tree (Acop CO_xH) fin_a + with + | Some fin_f', Some fin_a' => + if check_form fin_f' + && check_atom fin_a' + && check_tree_f_wf fin_f + && check_tree_a_wf fin_a + && (v <? wB) + && (Zlength fin_r <? wB) + then Some (of_list (Int63.of_Z 0) fin_r, fin_f', fin_a') + else None + | _, _ => None + end + | _, _ => None + end. + +Fixpoint check_list {A} (comp: A -> A -> bool) (a b: list A) := + match b, a with + | _, nil => true + | x::xs, y::ys => comp x y && check_list comp ys xs + | _, _ => false + end. + +Definition check_array {A} (comp: A -> A -> bool) a b := + check_list comp (Misc.to_list a) (Misc.to_list b). + +Definition single_form_not_bv f := + match f with + | Fatom _ | Ftrue | Ffalse | Fnot2 _ _ | Fand _ + | For _ | Fxor _ _ | Fiff _ _ | Fite _ _ _ => true + | FbbT _ _ | Fimp _ => false + end. + +Definition check_atoms_in_form (l: Int63.int) (f: form) := + match f with + | Fatom a => Int63.ltb a l + | _ => true + end. + +Definition check_local_form la f := + Form.check_form f + && Misc.aforallbi (fun i f' => single_form_not_bv f' + && check_atoms_in_form la f') f. + +Definition bool_typ_compdec := SMT_classes.Typ_compdec bool SMT_classes_instances.bool_compdec. +Definition Z_typ_compdec := SMT_classes.Typ_compdec Z SMT_classes_instances.Z_compdec. + +Definition t_i_empty := PArray.make (Int63.of_Z 0) Z_typ_compdec. + +Definition Ztrue := 1. +Definition Zfalse := -1. +Definition Zundef := 0. + +Definition Zand (a b: Z) := + if a <? b then a else b. + +Definition Zor (a b: Z) := + if a <? b then b else a. + +Definition Zimp (a b: Z) := + if 1 <? (1 - a + b) then 1 else (1 - a + b). + +Definition Znot (a: Z) := - a. + +Definition Zval (v: option bool): Z := + match v with + | Some true => 1 + | Some false => -1 + | None => 0 + end. + +Definition t_func_mk: Z -> tval t_i_empty := fun b => Tval t_i_empty (nil, Typ.TZ) b. + +(* Definition asgn_transl' (max: positive) (a: PTree.t bool) := *) +(* Misc.foldi (fun i na => PArray.set na i (t_func_mk (Zval (a!(to_P i))))) *) +(* (Int63.of_Z 0) (of_P max) *) +(* (PArray.make (of_P max) (t_func_mk 0)). *) + +Inductive asgn_transl_spec (a: PTree.t bool) (res: PArray.array (tval t_i_empty)) := +| asgn_transl_rule : + (forall i, 0 <= Z.pos i - 2 < wB -> PArray.get res (of_P i) = t_func_mk (Zval (a!i))) -> + PArray.default res = t_func_mk 0 /\ (forall i, exists n, PArray.get res i = t_func_mk n) -> + asgn_transl_spec a res. + +Definition asgn_transl (a: PTree.t bool) := + let max := max_key a in + PTree.fold (fun na i el => + PArray.set na (of_P i) (t_func_mk (Zval (Some el))) + ) a (PArray.make (of_P (max + 1)) (t_func_mk 0)). + +Lemma PArray_set_out_of_bound: forall A (a: PArray.array A) k d i, + (k <? PArray.length a = false)%int63 -> + PArray.get (PArray.set a k d) i = PArray.get a i. +Proof. + intros. + unfold PArray.set in *. + destruct a. simpl PArray.length in *. + destruct_match. + - auto. + - rewrite Misc.ltb_negb_geb in H. + rewrite negb_false_iff in H. + congruence. +Qed. + +Lemma PArray_default_fold_set : forall A B (m: PTree.t A) f (g: A -> B) array, + PArray.default (PTree.fold (fun st i a => PArray.set st (f i) (g a)) m array) = + PArray.default array. +Proof. + intros. + eapply PTree_Properties.fold_rec; eauto. + intros. rewrite PArray.default_set; auto. +Qed. + +Lemma asgn_transl_OOB: forall a k, + (k <? PArray.length (asgn_transl a) = false)%int63 -> + PArray.get (asgn_transl a) k = t_func_mk 0. +Proof. + unfold asgn_transl. + intros. + rewrite PArray.get_outofbound; auto. + rewrite PArray_default_fold_set; auto. +Qed. + +Lemma exists_asgn: + forall a, + forall (MAX: Z.pos (max_key a) - 2 < wB - 1), + (forall k x, a ! k = Some x -> (0 <= Z.pos k - 2 < wB)%int63) -> + exists res, asgn_transl_spec a res. +Proof. + intros. exists (asgn_transl a). + constructor. + intros i. + case_eq (of_P i <? PArray.length (asgn_transl a))%int63. + - unfold asgn_transl in *. + revert H. + eapply PTree_Properties.fold_rec; eauto. + + intros. rewrite <- H. + eapply H0; eauto. + intros. eapply H1; eauto. + rewrite <- H. eauto. + + intros. simpl. unfold Zval. rewrite PTree.gempty. + destruct_match; auto. + + unfold Zval. intros. + rewrite PArray.length_set in H3. + destruct (peq i k). + * subst. + rewrite PTree.gss. + rewrite PArray.get_set_same; auto. + * rewrite PTree.gso; auto. + rewrite PArray.get_set_other; auto. + -- eapply H1; eauto. + intros. eapply H2; eauto. + rewrite PTree.gso; eauto. + congruence. + -- intro. eapply of_P_inj in H5; eauto. + exploit (H2 k); eauto. + rewrite PTree.gss; eauto. + - intros. + rewrite PArray.get_outofbound; auto. + unfold asgn_transl. + rewrite PArray_default_fold_set. + rewrite PArray.default_make. + case_eq (a ! i); auto. intros b Haib. + unfold asgn_transl in H0. + rewrite PArray_length_fold_set in H0. + rewrite PArray.length_make in H0. + assert (0 <= Z.pos (max_key a) - 2 < wB). + { case_eq (PTree.bempty a); intros. + - rewrite PTree.bempty_correct in H2. + specialize (H2 i). + congruence. + - eapply max_key_in in H2. + destruct H2 as [x Hmax]. + eapply H in Hmax; eauto. + } + assert (i <= max_key a)%positive by (eapply max_key_correct; eauto). + case_eq (of_P (max_key a + 1) ≤? PArray.max_length)%int63; intros. + + rewrite H4 in H0. + rewrite Misc.ltb_negb_geb in H0. + rewrite negb_false_iff in H0. + unfold PArray.max_length in *. + unfold of_P in *. + rewrite ! leb_spec in *. + rewrite ! of_Z_spec in *. + rewrite Misc.max_int_wB in *. + rewrite ! Z.mod_small in *; try lia. + + rewrite H4 in *. + rewrite Misc.leb_negb_gtb in H4. + rewrite negb_false_iff in H4. + unfold PArray.max_length in *. + rewrite ! ltb_spec in *. + rewrite Misc.max_int_wB in *. + unfold of_P in *. + rewrite of_Z_spec in *. + rewrite Z.mod_small in *; try lia. + - unfold asgn_transl; intros. + eapply PTree_Properties.fold_rec; eauto; split; intros. + + auto. + + econstructor. rewrite PArray.get_make. auto. + + rewrite PArray.default_set. tauto. + + inv H2. + * destruct (int_dec (of_P k) i); subst. + ** destruct (of_P k <? PArray.length a0)%int63 eqn:?. + *** econstructor. rewrite PArray.get_set_same; eauto. + *** econstructor. rewrite PArray.get_outofbound. rewrite PArray.default_set. eassumption. + rewrite PArray.length_set. auto. + ** destruct (i <? PArray.length a0)%int63 eqn:?. + *** specialize (H4 i). inv H4. econstructor. rewrite PArray.get_set_other; eassumption. + *** econstructor. rewrite PArray.get_outofbound. rewrite PArray.default_set. eassumption. + rewrite PArray.length_set. auto. +Qed. + +Definition empty_tfunc := + PArray.make 0 (t_func_mk 0). + +Definition check_root_index (f: PArray.array form) (r: PArray.array Int63.int) := + forallb (fun x => to_Z (State.Lit.blit x) <? to_Z (PArray.length f)) (Misc.to_list r). + +Definition check_wt_extra a := + Misc.aforallbi (fun i a' => + match a' with + | Anop _ _ => false + | Aapp _ (_::_) => false + | _ => true + end) a. + +Fixpoint pred_in c p := + match p with + | Ptrue | Pfalse | Pundef => false + | Pbase c2 => (c =? c2)%positive + | Pand p1 p2 | Por p1 p2 | Pimp p1 p2 => pred_in c p1 || pred_in c p2 + | Pnot p' => pred_in c p' + end. + +Definition check_smt (p: predicate): bool := + let a_preds1 := + (max_hash_pred p + 1)%positive + in + let '(p', a_preds) := unnest_predicate a_preds1 p in + match to_smt a_preds p' with + | Some (d, forms, atoms) => + let '(nf, na, roots, used_roots, cert) := pred_verit_unsat atoms forms d in + check_array form_eqb forms nf + && check_array Atom.eqb atoms na + && check_local_form (PArray.length atoms) forms + && Atom.check_atom na + && Atom.wt _ empty_tfunc na + && check_wt_extra na + && (check_root_index forms d && negb (pred_in 1 p) && (Z.pos a_preds <? wB)) + && Checker_Ext.checker_ext na nf d used_roots cert + | None => false + end. + +Definition mutate1_p (p: predicate): predicate := + match p with + | Pand p1 p2 => Pand p2 p1 + | Por p1 p2 => Por p2 p1 + | Pimp (Pand p1 p2) b => Pimp (Pand p2 p1) b + | Pimp (Por p1 p2) b => Pimp (Por p2 p1) b + | Pimp b (Pand p1 p2) => Pimp b (Pand p2 p1) + | Pimp b (Por p1 p2) => Pimp b (Por p2 p1) + | Pnot (Pand p1 p2) => Pnot (Pand p2 p1) + | Pnot (Por p1 p2) => Pnot (Por p2 p1) + | p => p + end. + +Fixpoint has_Pbase (p: predicate) := + match p with + | Ptrue | Pfalse | Pundef => false + | Pbase _ => true + | Pand a b | Por a b | Pimp a b => has_Pbase a || has_Pbase b + | Pnot a => has_Pbase a + end. + +Definition check_smt_total (p: predicate): bool := + if check_smt p then true + else if check_smt (mutate1_p p) then true + else false. + + (** We need a higher level specification for this function so that everything is easier to prove. *) + + Definition upd_asgn := + fold_left + (fun a' el => + match eval_hash_pred (snd el) a' with + | Some p_b => PTree.set (fst el) p_b a' + | _ => a' + end). + + Inductive upd_asgn_spec: list (positive * predicate) -> PTree.t bool -> PTree.t bool -> Prop := + | upd_asgn_intros x : + upd_asgn_spec nil x x + | upd_asgn_cons_Some x xs t1 t2 b: + eval_hash_pred (snd x) t1 = Some b -> + upd_asgn_spec xs (PTree.set (fst x) b t1) t2 -> + upd_asgn_spec (x :: xs) t1 t2 + | upd_asgn_cons_None x xs t1 t2: + eval_hash_pred (snd x) t1 = None -> + upd_asgn_spec xs t1 t2 -> + upd_asgn_spec (x :: xs) t1 t2. + + (* #[local] Open Scope positive. *) + (* Compute PTree.get 3 (upd_asgn ((3, Pbase 2)::(2, Pbase 1)::nil) (PTree.set 1 false (PTree.empty _))). *) + + (* Lemma upd_asgn_cons : *) + (* forall p form r a, *) + (* (forall pb, pred_In pb form -> ~ In pb (map fst r)) -> *) + (* (upd_asgn ((p, form)::r) a) ! p = eval_hash_pred form a. *) + (* Proof. *) + + (* Lemma upd_asgn_cons_form : *) + (* forall p form r a, *) + (* eval_hash_pred (Pbase p) (upd_asgn ((p, form)::r) a) = eval_hash_pred form a. *) + (* Proof. *) + + (* Lemma wt_correct' : *) + (* v_type Typ.type (Typ.interp t_i_empty) (PArray.get (Atom.t_interp t_i_empty a' atoms) i) *) + + Ltac ldestruct_match := + match goal with + | [ H: context[match ?x with | _ => _ end] |- _ ] => destruct x eqn:? + end. + + Lemma wt_correct' : + forall a' a (atoms: atom_t) + (ATOMSNONOP: forall i x l, PArray.get atoms i <> Anop x l) + (ATOMSAPP: forall i n l, PArray.get atoms i = Aapp n l -> l = nil) + (GETA1: forall i, exists n, PArray.get a' i = t_func_mk n) + (GETA2: forall i, exists n, PArray.get a i = t_func_mk n), + forall i, + v_type Typ.type (Typ.interp t_i_empty) + (PArray.get (Atom.t_interp t_i_empty a atoms) i) = + v_type Typ.type (Typ.interp t_i_empty) (PArray.get (Atom.t_interp t_i_empty a' atoms) i). + Proof. + intros. generalize dependent i. + pose proof (Atom.length_t_interp t_i_empty a atoms) as Y1. + pose proof (Atom.length_t_interp t_i_empty a' atoms) as Y2. + generalize dependent Y2. + generalize dependent Y1. + apply Misc.foldi_ind2 with + (P := (fun x b b' => + PArray.length b = PArray.length atoms -> + PArray.length b' = PArray.length atoms -> + (forall i, + v_type Typ.type (Typ.interp t_i_empty) (PArray.get b i) + = v_type Typ.type (Typ.interp t_i_empty) (PArray.get b' i)))). + + apply Misc.leb_0. + + intros. rewrite PArray.get_make in *. crush. + + intros. + rewrite PArray.length_set in *. + pose proof H0 as LT1. apply Int63.ltb_spec in LT1. + pose proof H as LE2. apply Int63.leb_spec in LE2. + destruct (int_dec i i0); subst. + rewrite !PArray.get_set_same by (apply Int63.ltb_spec; try (rewrite H3); try rewrite H2; lia); auto. + specialize (H1 H2 H3). assert (EQ1:=H2). assert (EQ2:=H3). clear H2. clear H3. + destruct (PArray.get atoms i0) eqn:ATOMS; subst; simplify. + - unfold interp_cop; repeat destruct_match; subst; auto. + - assert (HEQ := H1 i). unfold interp_uop; destruct_match; unfold apply_unop; + repeat destruct_match; auto; + setoid_rewrite Heqb0 in HEQ; simplify; + setoid_rewrite Heqb in HEQ; simplify; subst; rewrite Heqc in Heqc0; discriminate. + - assert (HEQ1 := H1 i). assert (HEQ2 := H1 i1). + unfold interp_bop. destruct_match; + unfold apply_binop; repeat destruct_match; auto; + try (setoid_rewrite Heqb4 in HEQ2; setoid_rewrite Heqb2 in HEQ2; + setoid_rewrite Heqb3 in HEQ1; setoid_rewrite Heqb1 in HEQ1; + simplify; subst; try rewrite Heqc0 in *; try rewrite Heqc1 in *; + try rewrite Heqc2 in *; try rewrite Heqc3 in *; discriminate). + - assert (HEQ1 := H1 i). assert (HEQ2 := H1 i1). assert (HEQ3 := H1 i2). + unfold interp_top. destruct_match; subst. unfold apply_terop. + repeat destruct_match; auto; + setoid_rewrite Heqb4 in HEQ3; setoid_rewrite Heqb2 in HEQ1; setoid_rewrite Heqb3 in HEQ2; + setoid_rewrite Heqb1 in HEQ3; setoid_rewrite Heqb in HEQ1; setoid_rewrite Heqb0 in HEQ2; + simplify; subst; try rewrite Heqc4 in *; try rewrite Heqc3 in *; try rewrite Heqc2 in *; + try rewrite Heqc1 in *; try rewrite Heqc0 in *; try rewrite Heqc in *; discriminate. + - exfalso. eapply ATOMSNONOP; eassumption. + - assert (HEQ1 := H1 i). specialize (GETA1 i). specialize (GETA2 i). + inv GETA1. inv GETA2. + repeat destruct_match. assert (l = nil) by (eapply ATOMSAPP; eassumption); subst. + cbn [map]. unfold t_func_mk in *. unfold Tval in *. inv H2. simpl. + inv H3. auto. + - rewrite !PArray.get_set_other + by (auto; apply Int63.ltb_spec; try (rewrite H3); try rewrite H2; lia); + eauto. + Qed. + + Lemma wt_correct2 : + forall atoms a a' + (ATOMSNONOP: forall i x l, PArray.get atoms i <> Anop x l) + (ATOMSAPP: forall i n l, PArray.get atoms i = Aapp n l -> l = nil) + (GETA1: forall i, exists n, PArray.get a' i = t_func_mk n) + (GETA2: forall i, exists n, PArray.get a i = t_func_mk n), + is_true (wt t_i_empty a atoms) -> + is_true (wt t_i_empty a' atoms). + Proof. + unfold is_true, wt; intros. + apply Misc.aforallbi_spec; intros i LTB. + eapply Misc.aforallbi_spec in H; [|eassumption]. + unfold check_aux in *. + destruct (PArray.get atoms i) eqn:?; unfold get_type' in *. + - erewrite wt_correct'; eassumption. + - destruct_match. simplify. + setoid_rewrite wt_correct' with (a':=a); eauto. + rewrite H0. rewrite H1. auto. + - repeat destruct_match; simplify. + setoid_rewrite wt_correct' with (a':=a); eauto. + rewrite H1. rewrite H. rewrite H2. auto. + - repeat destruct_match; simplify. + setoid_rewrite wt_correct' with (a':=a); eauto. + rewrite H1. rewrite H0. rewrite H2. rewrite H3. auto. + - exfalso; eapply ATOMSNONOP; eauto. + - repeat destruct_match; crush. eapply ATOMSAPP in Heqa0; subst. simplify. + assert (GETB1:=GETA1). assert (GETB2:=GETA2). + specialize (GETA1 i0). specialize (GETA2 i0). inv GETA1. inv GETA2. + rewrite H in Heqf. rewrite H2 in Heqf0. unfold t_func_mk, Tval in *. + simplify. erewrite wt_correct'; eauto. + Qed. + + Lemma wf_empty_tfunc : + forall i, + exists n, PArray.get empty_tfunc i = t_func_mk n. + Proof. unfold empty_tfunc. intros. eexists. rewrite PArray.get_make. eauto. Qed. + + Lemma to_P_of_P : + forall p, 1 < Z.pos p < wB -> to_P (of_P p) = p. + Proof. + unfold to_P, of_P; intros * LT. + rewrite of_Z_spec. rewrite !Z.mod_small by lia. + assert (forall x, x - 2 + 2 = x) by lia. + rewrite H; auto. + Qed. + +Lemma not_int63_lt_0 : forall i, 0 <= Int63.to_Z i. +Proof. + intro i. + assert (~ Int63.to_Z i < 0). + { unfold not; intros. rewrite <- Int63.to_Z_0 in H. + apply Int63.ltb_spec in H. now apply Misc.ltb_0 in H. } + lia. +Qed. + + Lemma of_P_to_P : + forall p, of_P (to_P p) = p. + Proof. + unfold to_P, of_P; intros. rewrite Z2Pos.id. + assert (forall x, x + 2 - 2 = x) by lia. rewrite H. + now rewrite of_to_Z. + pose proof (not_int63_lt_0 p). lia. + Qed. + + Lemma wf_asgn_spec : + forall a a', + asgn_transl_spec a a' -> + forall i, exists n, PArray.get a' i = t_func_mk n. + Proof. inversion 1; intros. inv H1. eauto. Qed. + + Lemma wt_correct : + forall atoms a a' + (ATOMSNONOP: forall i x l, PArray.get atoms i <> Anop x l) + (ATOMSAPP: forall i n l, PArray.get atoms i = Aapp n l -> l = nil), + is_true (wt _ empty_tfunc atoms) -> + asgn_transl_spec a a' -> + is_true (wt _ a' atoms). + Proof. + intros. + pose proof (wf_asgn_spec _ _ H0). + pose proof wf_empty_tfunc. + eapply wt_correct2; eauto. + Qed. + +Section SMT_PROOF. + + Fixpoint Zeval (a: PTree.t bool) (p: predicate) := + match p with + | Ptrue => 1 + | Pfalse => -1 + | Pundef => 0 + | Pbase p' => Zval (a ! p') + | Pnot p' => Znot (Zeval a p') + | Pand p1 p2 => Zand (Zeval a p1) (Zeval a p2) + | Por p1 p2 => Zor (Zeval a p1) (Zeval a p2) + | Pimp p1 p2 => Zimp (Zeval a p1) (Zeval a p2) + end. + + Lemma Zeval_correct : + forall a p, + Zeval a p = Zval (eval_hash_pred p a). + Proof. + induction p; crush; + repeat (destruct_match; simplify); + try (rewrite IHp1; rewrite IHp2); + auto; rewrite IHp; auto; discriminate. + Qed. + +End SMT_PROOF. + +Lemma check_local_form_spec : + forall l f, + is_true (check_local_form l f) -> + (forall i a b, PArray.get f i <> FbbT a b) + /\ (forall i a, PArray.get f i <> Fimp a) + /\ (forall i a, PArray.get f i = Fatom a -> is_true (Int63.ltb a l)) + /\ is_true (check_form f). +Proof. + unfold check_local_form, is_true; simplify; auto. + - unfold not; intros. + destruct (Int63.ltb i (PArray.length f)) eqn:Heqb. + + eapply Misc.aforallbi_spec in H1; try eassumption. now rewrite H in H1. + + unfold check_form in H0. simplify. unfold is_Ffalse, is_Ftrue in *. + repeat (destruct_match; try discriminate). + rewrite PArray.get_outofbound in H; auto. now rewrite Heqf1 in H. + - unfold not; intros. + destruct (Int63.ltb i (PArray.length f)) eqn:Heqb. + + eapply Misc.aforallbi_spec in H1; try eassumption. now rewrite H in H1. + + unfold check_form in H0. simplify. unfold is_Ffalse, is_Ftrue in *. + repeat (destruct_match; try discriminate). + rewrite PArray.get_outofbound in H; auto. now rewrite Heqf1 in H. + - intros. + destruct (Int63.ltb i (PArray.length f)) eqn:Heqb. + + eapply Misc.aforallbi_spec in H1; try eassumption. simplify. + unfold check_atoms_in_form in H3. now rewrite H in H3. + + unfold check_form in H0. simplify. unfold is_Ffalse, is_Ftrue in *. + repeat (destruct_match; try discriminate). + rewrite PArray.get_outofbound in H by auto. now rewrite Heqf1 in H. +Qed. + +Lemma to_list_corr' : + forall A a, + (fun f b => + (forall i, (Int63.to_Z i < Int63.to_Z f) -> + PArray.get a i = nth (Z.to_nat (Int63.to_Z i)) (rev b) (PArray.default a)) + /\ Int63.to_Z f = Zlength b) + (PArray.length a) + (Misc.foldi (fun i (l : list A) => PArray.get a i :: l) + (Int63.of_Z 0) + (PArray.length a) nil). +Proof. + intros. apply Misc.foldi_ind; intros. + apply Int63.leb_spec. simplify; auto. + pose proof (not_int63_lt_0 (PArray.length a)). + rewrite Int63.to_Z_0. auto. + split; intros. simpl in H. rewrite Int63.to_Z_0 in H. + rewrite <- Int63.to_Z_0 in H. apply Int63.ltb_spec in H. + now apply Misc.ltb_0 in H. auto. + split; [|erewrite Misc.to_Z_add_1 by eassumption; + rewrite Zlength_cons; lia]. + cbn [rev]. intros. erewrite Misc.to_Z_add_1 in H2 by eassumption. + assert (Int63.to_Z i0 < Int63.to_Z i \/ Int63.to_Z i0 = Int63.to_Z i) by lia. + inv H3. rewrite app_nth1; auto. apply H1; lia. apply Nat2Z.inj_lt. + rewrite rev_length. + rewrite <- Zlength_correct. + rewrite Z_to_nat_max. + apply Int63.leb_spec in H. + apply Int63.ltb_spec in H0. + pose proof (not_int63_lt_0 i0). lia. + apply Int63.leb_spec in H. + apply Int63.ltb_spec in H0. simpl in H. + rewrite app_nth2; rewrite rev_length. + replace (Datatypes.length a0) with (Z.to_nat (Z.of_nat (Datatypes.length a0))) + by (now apply Nat2Z.id). + rewrite <- Zlength_correct. + rewrite <- Z2Nat.inj_sub. rewrite Zeq_minus by lia. + simpl. apply Int63.to_Z_inj in H4. + now subst. + rewrite Zlength_correct. lia. + zify. rewrite <- Zlength_correct. lia. +Qed. + +Lemma to_list_corr : + forall A (a: PArray.array A) i, + Int63.to_Z i < Int63.to_Z (PArray.length a) -> + PArray.get a i = nth (Z.to_nat (Int63.to_Z i)) (Misc.to_list a) (PArray.default a). +Proof. + intros; pose proof (to_list_corr' A a). + inv H0; auto. +Qed. + +Lemma check_list_correct : + forall A comp (a: list A) b i da db + (CHK : is_true (check_list comp a b)) + (LT : (i < (length a))%nat), + is_true (comp (nth i b db) (nth i a da)). +Proof. + unfold is_true; induction a; try solve[crush]. + intros. + simpl in CHK. destruct_match; try discriminate. + apply andb_prop in CHK. inv CHK. + simplify. destruct i. auto. + apply IHa; auto. lia. +Qed. + +Lemma Zlength_aux_concat: forall A (l1 l2: list A) (acc: Z), + Zlength_aux acc _ (l1 ++ l2) = Zlength_aux (Zlength_aux acc _ l1) _ l2. +Proof. + induction l1 ; intros; eauto. + simpl. + rewrite IHl1; eauto. +Qed. + +Lemma length_to_list : + forall A (a: PArray.array A), + Int63.to_Z (PArray.length a) = Zlength (Misc.to_list a). +Proof. + intros. unfold Misc.to_list. + eapply Misc.foldi_ind; eauto. + - apply Misc.leb_0. + - intros. unfold Zlength in *. simpl. + rewrite Zlength_aux_concat. + rewrite <- H1. simpl. + erewrite Misc.to_Z_add_1; eauto. +Qed. + +Lemma check_list_length : + forall A comp (l1: list A) l2, + is_true (check_list comp l1 l2) -> + Zlength l1 <= Zlength l2. +Proof. + induction l1; crush. rewrite Zlength_nil. rewrite Zlength_correct. lia. + destruct l2; crush. rewrite !Zlength_cons. + unfold is_true in H. simplify. eapply IHl1 in H1. lia. +Qed. + +Lemma check_array_length : + forall A comp (a: PArray.array A) b, + is_true (check_array comp a b) -> + Int63.to_Z (PArray.length a) <= Int63.to_Z (PArray.length b). +Proof. + intros. unfold check_array in *. + eapply check_list_length in H. rewrite !length_to_list; auto. +Qed. + +Lemma check_array_correct : + forall A comp (a: PArray.array A) b, + is_true (check_array comp a b) -> + forall i, + Int63.to_Z i < Int63.to_Z (PArray.length a) -> + is_true (comp (PArray.get b i) (PArray.get a i)). +Proof. + unfold check_array, is_true; intros * CHK i LT. + pose proof (not_int63_lt_0 i). + repeat rewrite to_list_corr. + apply check_list_correct; auto. zify. rewrite <- Zlength_correct. + rewrite <- length_to_list. lia. + lia. apply check_array_length in CHK. lia. +Qed. + +Section PROOF. + + Context (t_i: PArray.array SMT_classes.typ_compdec). + Context (t_func: PArray.array (tval t_i)). + + Definition bo_dec: + forall (a b: option bool), + {a = b} + {a <> b}. + Proof. pose proof bool_dec. decide equality. Qed. + + Lemma of_list'_length : forall A, + forall (b: list A) acc array, + PArray.length (of_list' acc array b) = PArray.length array. + Proof. + induction b; intros. + - reflexivity. + - simpl of_list'. + erewrite IHb; eauto. + rewrite PArray.length_set. auto. + Qed. + + Lemma of_list_Zlength : forall A (d: A) b, + Zlength b < wB -> + PArray.length (of_list d b) = of_Z (Z.of_nat (length b)). + Proof. + unfold of_list. + intros. rewrite of_list'_length; eauto. + rewrite PArray.length_make. + unfold PArray.max_length. + destruct_match. + - rewrite Zlength_correct. auto. + - rewrite Misc.leb_negb_gtb in Heqb0. + rewrite negb_false_iff in Heqb0. + eapply ltb_spec in Heqb0. + rewrite of_Z_spec in Heqb0. + rewrite Z.mod_small in Heqb0; try lia. + + rewrite Misc.max_int_wB in Heqb0. lia. + + split; auto. rewrite Zlength_correct. lia. + Qed. + + Opaque PArray.set. + + Lemma Zlength_aux_pos : forall A (b: list A) acc, + acc >= 0 -> + Zlength_aux acc A b >= 0. + Proof. + induction b; intros. + - auto. + - simpl. eapply IHb; eauto. + lia. + Qed. + + Lemma Zlength_pos : forall A (b: list A), + Zlength b >= 0. + Proof. + unfold Zlength. + intros. eapply Zlength_aux_pos; eauto. + lia. + Qed. + + Lemma of_list'_idx_preserve : forall A (b: list A) idx array, + forall i, + (0 <= idx)%Z -> + (i <? of_Z idx = true)%int63 -> + (0 <= idx + (Zlength b) < wB)%Z -> + (PArray.get (of_list' idx array b) i) = PArray.get array i. + Proof. + induction b; intros. + - auto. + - simpl of_list'. + rewrite Zlength_cons in *. + erewrite IHb; eauto. + + rewrite PArray.get_set_other; eauto. + intro ; subst. eelim Misc.not_ltb_refl; eauto. + + lia. + + rewrite ltb_spec in *. rewrite of_Z_spec in *. + assert (Zlength b >= 0) by (eapply Zlength_pos; eauto). + rewrite ! Z.mod_small in *; try lia. + + lia. + Qed. + + Opaque PArray.set. + + Lemma to_list_of_list'3 : + forall A (l: list A) arr x y, + y + Zlength l < wB -> + to_Z x < y -> + to_Z x < to_Z (PArray.length arr) -> + PArray.get (of_list' y arr l) x = PArray.get arr x. + Proof. + induction l; crush. + erewrite IHl. rewrite PArray.get_set_other; auto. + - unfold not; intros. assert (forall x y, x = y -> to_Z x = to_Z y) by (intros; now subst). + apply H3 in H2. rewrite of_Z_spec in H2. rewrite Z.mod_small in H2; subst. lia. + pose proof (not_int63_lt_0 x). split. lia. rewrite Zlength_cons in H. + pose proof (Zlength_pos _ l). lia. + - rewrite Zlength_cons in H. lia. + - lia. + - rewrite PArray.length_set. auto. + Qed. + + Lemma to_list_of_list'2 : + forall A l (d: A) x0 v a + (PARRR: to_Z x0 < to_Z (PArray.length a)) + (XXXXX: v + Zlength l < wB), + PArray.default a = d -> + to_Z (PArray.length a) <= Zlength l + v -> + v <= to_Z x0 -> + PArray.get (of_list' v a l) x0 = nth ((Z.to_nat (to_Z x0 - v))) l d. + Proof. + induction l; intros. + - simpl. assert ((x0 <? 0)%int63 = false). + { destruct ((x0 <? 0)%int63) eqn:?; auto. pose proof (not_int63_lt_0 x0). apply Int63.ltb_spec in Heqb. + replace (to_Z 0) with 0 in Heqb by auto. lia. + } rewrite Zlength_nil in H0. pose proof (not_int63_lt_0 (PArray.length a)). + rewrite PArray.get_outofbound. rewrite H. destruct_match; auto. + apply not_true_is_false. unfold not; intros. apply Int63.ltb_spec in H4. lia. + - simpl. destruct_match. + assert (forall x y, x = y -> Z.of_nat x = Z.of_nat y) by (intros; subst; auto). + apply H2 in Heqn. rewrite Z_to_nat_max in Heqn. + rewrite Zlength_cons in H0. unfold Z.succ in H0. + { replace (Z.max (to_Z x0 - v) 0) with (to_Z x0 - v) in Heqn by lia. + assert (to_Z x0 = v) by lia. subst. + rewrite of_to_Z. erewrite to_list_of_list'3; try lia. + rewrite PArray.get_set_same; auto. + apply Int63.ltb_spec. auto. + rewrite Zlength_cons in XXXXX. lia. + rewrite PArray.length_set; auto. + } + rewrite IHl with (d:=d). + assert (Z.to_nat (to_Z x0 - (v + 1)) = n). + { assert (forall a b, a = b -> (a - 1)%nat = (b - 1)%nat) by lia. + apply H2 in Heqn. + assert ((S n - 1)%nat = n) by lia. rewrite H3 in Heqn. rewrite <- Heqn. + lia. + } rewrite H2. auto. + rewrite PArray.length_set. lia. rewrite Zlength_cons in XXXXX. lia. + rewrite PArray.default_set. auto. + rewrite PArray.length_set. rewrite Zlength_cons in H0. lia. + lia. + Qed. + Transparent PArray.set. + + Lemma to_list_of_list' : + forall A l (d: A) x0, + Zlength l < wB -> + to_Z x0 < Zlength l -> + PArray.get (of_list d l) x0 = nth (Z.to_nat (Int63.to_Z x0)) l d. + Proof. + unfold of_list. intros. + replace (to_Z x0) with (to_Z x0 - 0) by lia. + pose proof (Zlength_pos _ l). + apply to_list_of_list'2. + rewrite PArray.length_make. + assert ((of_Z (Zlength l) <=? PArray.max_length)%int63 = true). + apply Int63.leb_spec. rewrite of_Z_spec. rewrite Z.mod_small by lia. + unfold PArray.max_length. rewrite Misc.max_int_wB. lia. rewrite H2. + rewrite of_Z_spec. rewrite Z.mod_small by lia. auto. + lia. + rewrite PArray.default_make; auto. + rewrite PArray.length_make. + assert ((of_Z (Zlength l) <=? PArray.max_length)%int63 = true). + apply Int63.leb_spec. rewrite of_Z_spec. rewrite Z.mod_small by lia. + unfold PArray.max_length. rewrite Misc.max_int_wB. lia. rewrite H2. + rewrite of_Z_spec. rewrite Z.mod_small by lia. auto. lia. + pose proof (not_int63_lt_0 x0). lia. + Qed. + + Lemma to_list_of_list2 : (* lemmes nth of_list' *) + forall A (d: A) (b: list A) x, + Zlength b < wB -> + In x (Misc.to_list (of_list d b)) -> + In x b. + Proof. + intros. apply Misc.In_to_list in H0. simplify. + rewrite to_list_of_list'. apply nth_In. + apply Int63.ltb_spec in H0. rewrite of_list_Zlength in H0 by auto. + rewrite of_Z_spec in H0. rewrite Z.mod_small in H0. + assert (Z.of_nat (Z.to_nat φ (x0)%int63) < Z.of_nat (Datatypes.length b)). + { rewrite Z_to_nat_max. + pose proof (not_int63_lt_0 x0). + lia. + } + lia. + simplify. lia. + rewrite <- Zlength_correct; auto. + auto. + apply Int63.ltb_spec in H0. rewrite of_list_Zlength in H0; auto. + rewrite <- Zlength_correct in H0. rewrite Int63.of_Z_spec in H0. + rewrite Z.mod_small in H0; auto. + pose proof (Zlength_pos _ b). lia. + Qed. + + Lemma of_pos_rec_unfold_xO : forall n p, + of_pos_rec (S n) (xO p) = (of_pos_rec n p << 1)%int63. + Proof. + intros. reflexivity. + Qed. + + Lemma is_even_of_pos : + forall p a, + (Z.pos a - 2) * 2 = Z.pos p -> + is_even (of_pos p) = true. + Proof. + destruct p. + - lia. + - intros. + unfold of_pos, size. + rewrite of_pos_rec_unfold_xO. + eapply is_even_lsl_1. + - lia. + Qed. + + Lemma is_pos_P_of_P: + forall a, + 0 <= (Zpos a - 2) * 2 < wB -> + State.Lit.is_pos (P_of_P a) = true. + Proof. + unfold State.Lit.is_pos, P_of_P. + intros. + unfold of_Z. case_eq ((Z.pos a - 2) * 2); intros; try lia. + - apply Bva_checker.is_even_0. + - eapply is_even_of_pos; eauto. + Qed. + + Lemma of_pos_rec_unfold_xI : forall n p, + of_pos_rec (S n) (xI p) = (of_pos_rec n p << 1 lor 1)%int63. + Proof. + intros. reflexivity. + Qed. + + Lemma is_even_of_pos_false : + forall p a, + (Z.pos a - 2) * 2 + 1 = Z.pos p -> + is_even (of_pos p) = false. + Proof. + destruct p. + - intros. unfold of_pos, size. + rewrite of_pos_rec_unfold_xI. + rewrite Misc.is_even_or. + rewrite andb_false_r; auto. + - lia. + - intros. auto. + Qed. + + Lemma is_neg_N_of_P: + forall a, + 0 <= (Zpos a - 2) * 2 + 1 < wB -> + State.Lit.is_pos (N_of_P a) = false. + Proof. + unfold State.Lit.is_pos, N_of_P. + intros. + unfold of_Z. case_eq ((Z.pos a - 2) * 2 + 1); intros; try lia. + eapply is_even_of_pos_false; eauto. + Qed. + + Lemma blit_N_of_P: + forall a, + 1 < Z.pos a -> + (Z.pos a - 2) * 2 + 1 < wB -> + State.Lit.blit (N_of_P a) = of_P a. + Proof. + unfold State.Lit.blit, of_P, N_of_P; intros. + apply to_Z_inj. + rewrite lsr_spec. + replace (to_Z (1)%int63) with 1 by crush. + replace (2 ^ 1) with 2 by lia. + rewrite !of_Z_spec. + rewrite !Z.mod_small by lia. + rewrite Z.div_add_l by lia. + assert (X: 1 / 2 = 0) by auto. + rewrite X. rewrite Z.add_0_r. auto. + Qed. + + Lemma blit_P_of_P: + forall a, + 1 < Z.pos a -> + (Z.pos a - 2) * 2 < wB -> + State.Lit.blit (P_of_P a) = of_P a. + Proof. + unfold State.Lit.blit, of_P, P_of_P; intros. + apply to_Z_inj. + rewrite lsr_spec. + replace (to_Z (1)%int63) with 1 by crush. + replace (2 ^ 1) with 2 by lia. + rewrite !of_Z_spec. + rewrite !Z.mod_small by lia. + rewrite Z_div_mult by lia. auto. + Qed. + + Lemma in_tree_lt_array : + forall d tree f x y, + tree_key_wf _ tree -> + of_tree d (PTree.map (fun _ : positive => lform_to_form) tree) = Some f -> + tree ! x = Some y -> + is_true (of_P x <? PArray.length f)%int63. + Proof. + intros. + unfold of_tree in *. + destruct_match; try congruence. + inv H0. rewrite PArray_length_fold_set. + rewrite PArray.length_make. + unfold PArray.max_length in *. + erewrite <- max_key_map in *; eauto. + destruct_match. + + pose proof (max_key_correct _ _ _ _ H1). + unfold of_P, is_true, tree_key_wf, gt_1 in *. + rewrite ltb_spec. + rewrite Z.ltb_lt in *. + rewrite ! of_Z_spec. + inv H. inv H3. specialize (H4 x y H1). + rewrite ! Z.mod_small; try lia. + + pose proof (max_key_correct _ _ _ _ H1). + unfold of_P, is_true, tree_key_wf, gt_1 in *. + rewrite ltb_spec. + rewrite ! of_Z_spec. + rewrite Z.ltb_lt in *. + rewrite Misc.max_int_wB. + rewrite ! Z.mod_small; try lia. + inv H. inv H3. specialize (H4 x y H1). lia. + Qed. + + Lemma of_tree_default : + forall A (d: A) f y, + of_tree d f = Some y -> + PArray.default y = d. + Proof. + intros. unfold of_tree in *. destruct_match; try discriminate; simplify. + rewrite PArray_default_fold_set; auto. + Qed. + + Lemma hash_value_in : + forall max f ht h ht', + hash_value max f ht = (h, ht') -> + ht' ! h = Some f. + Proof. + unfold hash_value. intros. + destruct_match. + - inv H. eapply find_tree_correct; eauto. + - inv H. rewrite PTree.gss. auto. + Qed. + + Lemma AHhash_value_in : + forall max f ht h ht', + AH.hash_value max f ht = (h, ht') -> + ht' ! h = Some f. + Proof. + unfold AH.hash_value; intros. + destruct_match. + - inv H. eapply AH.find_tree_correct; eauto. + - inv H. rewrite PTree.gss. auto. + Qed. + + Lemma pos_of_tree_max_key : + forall A (d: A) h11 h6 x h, + tree_a_wf h11 -> + (max_key h6 <= max_key h11)%positive -> + h11 ! h = h6 ! h -> + AH.find_tree x h6 = Some h -> + 1 < Z.pos h < wB. + Proof. + intros. eapply AH.find_tree_Some in H2. + pose proof H2. rewrite <- H1 in H3. + eapply max_key_correct in H2. + unfold tree_a_wf in *. inversion H as [RANGE [PROP [SOME1 [SOME2 SOME3]]]]. + apply PROP in H3. lia. + Qed. + + Definition match_one {A} (f: PTree.t A) lf := + forall y z, lf ! y = Some z -> f ! y = Some z. + + Definition match_all {A} (f: PTree.t A) lf := + Forall (match_one f) lf. + +Lemma match_one_refl : + forall A x, @match_one A x x. +Proof. unfold match_one; auto. Qed. + +Instance match_one_Reflexive {A} : Reflexive (@match_one A). +Proof. unfold Reflexive. auto using match_one_refl. Qed. + +Lemma match_one_trans : + forall A x y z, @match_one A x y -> match_one y z -> match_one x z. +Proof. unfold match_one; auto. Qed. + +Instance match_one_Transitive {A} : Transitive (@match_one A). +Proof. unfold Transitive. eauto using match_one_trans. Qed. + + Lemma tree_a_wf_lt : + forall h x y, + tree_a_wf h -> + h ! x = Some y -> + 0 <= Z.pos x - 2 < wB. + Proof. + unfold tree_a_wf; intros. pose proof H0 as X. apply max_key_correct in H0. + unfold gt_1 in *. inv H. inv H2. apply H in X. + all: lia. + Qed. + + Section PROOF_MORE. + + Context (f: form_t) (a: atom_t) (f_t: PTree.t lform) (a_t: PTree.t atom). + + Context (CHKF: is_true (check_form f)). + Context (CHKA: is_true (check_atom a)). + Context (WFF: tree_f_wf f_t). + Context (WFA: tree_a_wf a_t). + + Context (GENF: of_tree Ftrue (PTree.map (fun _ => lform_to_form) f_t) = Some f). + Context (GENA: of_tree (Acop CO_xH) a_t = Some a). + + Context (la: PTree.t bool). + Context (la_arr: PArray.array (tval t_i_empty)). + + Context (MATCH_LA: forall i : positive, 0 <= Z.pos i - 2 < wB -> + PArray.get la_arr (of_P i) = t_func_mk (Zval la ! i)). + + Lemma match_one_hash_value : + forall max x h h' y y', + hash_value max y h = (y', h') -> + match_one x h' -> + match_one x h. + Proof. + unfold match_one. + intros. + eapply H0; eauto. + eapply hash_constant; eauto. + Qed. + + Lemma AHmatch_one_hash_value : + forall max x h h' y y', + AH.hash_value max y h = (y', h') -> + match_one x h' -> + match_one x h. + Proof. + unfold match_one. + intros. + eapply H0; eauto. + eapply AH.hash_constant; eauto. + Qed. + + Lemma gt_1_map : + gt_1 (PTree.map (fun _ : positive => lform_to_form) f_t). + Proof. + inversion WFF; unfold gt_1 in *; simplify. + rewrite PTree.gmap in H1. unfold option_map in *. + destruct (f_t ! x) eqn:?. eapply H0. eauto. easy. + Qed. + + Lemma gt_1_a : + gt_1 a_t. + Proof. inversion WFA; crush. Qed. + + Lemma in_tree_bounded : + forall t p y, + t ! p = Some y -> + match_one f_t t -> + 0 <= (Z.pos p - 2) * 2 + 1 < wB. + Proof. + intros. unfold match_one in *. apply H0 in H. + pose proof H. apply max_key_correct in H. + unfold tree_f_wf in WFF. inversion WFF as [X1 [X2 [X3 X4]]]. + unfold gt_1 in X3. + apply X3 in H1. + lia. + Qed. + + Lemma in_tree_bounded2 : + forall t p y, + t ! p = Some y -> + match_one f_t t -> + 0 <= (Z.pos p - 2) * 2 < wB. + Proof. + intros. unfold match_one in *. apply H0 in H. + pose proof H. apply max_key_correct in H. + unfold tree_f_wf in WFF. inversion WFF as [X1 [X2 [X3 X4]]]. + unfold gt_1 in X3. + apply X3 in H1. + lia. + Qed. + + Lemma in_tree_bounded3 : + forall t p y, + t ! p = Some y -> + match_one f_t t -> + 0 <= Z.pos p - 2 < wB. + Proof. + intros. unfold match_one in *. apply H0 in H. + pose proof H. apply max_key_correct in H. + unfold tree_f_wf in WFF. inversion WFF as [X1 [X2 [X3 X4]]]. + unfold gt_1 in X3. + apply X3 in H1. + lia. + Qed. + + Lemma in_tree_bounded4 : + forall t p y, + t ! p = Some y -> + match_one a_t t -> + 0 <= Z.pos p - 2 < wB. + Proof. + intros. unfold match_one in *. apply H0 in H. + pose proof H. apply max_key_correct in H. + unfold tree_a_wf in WFA. inversion WFA as [X1 [X2 [X3 X4]]]. + unfold gt_1 in X2. + apply X2 in H1. + lia. + Qed. + + Lemma tree_f_wf_tree_key_wf: forall f_t, + tree_f_wf f_t -> + tree_key_wf _ f_t. + Proof. + clear. intros. + inversion H as [X1 [X2 [X3 [X4 X5]]]]. + unfold tree_key_wf. + repeat split; try lia. + auto. + Qed. + + Lemma tree_a_wf_tree_key_wf: forall f_t, + tree_a_wf f_t -> + tree_key_wf _ f_t. + Proof. + clear. intros. + inversion H as [X1 [X2 [X3 [X4 X5]]]]. + unfold tree_key_wf. + repeat split; try lia. + auto. + Qed. + + Lemma tree_key_wf_map : forall A B f_t (f: positive -> A -> B), + tree_key_wf _ f_t -> + tree_key_wf _ (PTree.map f f_t). + Proof. + clear. intros. + inversion H as [X1 [X2 X3]]. + unfold tree_key_wf. + repeat split; try (erewrite <- max_key_map; eauto). + rewrite <- gt_1_map_iff; eauto. + Qed. + + #[local] Hint Resolve gt_1_map : to_smt. + #[local] Hint Resolve gt_1_a : to_smt. + #[local] Hint Resolve in_tree_bounded : to_smt. + #[local] Hint Resolve in_tree_bounded2 : to_smt. + #[local] Hint Resolve in_tree_bounded3 : to_smt. + #[local] Hint Resolve in_tree_bounded4 : to_smt. + + Lemma of_tree_of_P_2 : + forall t p v, + t ! p = Some v -> + match_one f_t t -> + PArray.get f (of_P p) = (lform_to_form v). + Proof. + unfold match_one; intros. + eapply of_tree_of_P; eauto with to_smt. + - eapply tree_key_wf_map; eauto. + eapply tree_f_wf_tree_key_wf; eauto. + - rewrite PTree.gmap. apply H0 in H. rewrite H. auto. + Qed. + + Lemma of_tree_of_P_2_hv : + forall t p v x h, + hash_value x v h = (p, t) -> + match_one f_t t -> + PArray.get f (of_P p) = (lform_to_form v). + Proof. + intros; apply hash_value_in in H; auto. + eapply of_tree_of_P_2; eassumption. + Qed. + + Lemma AHof_tree_of_P_2 : + forall t p v, + t ! p = Some v -> + match_one a_t t -> + PArray.get a (of_P p) = v. + Proof. + intros. pose proof H0. unfold match_one in H0; intros. + pose proof H. apply H0 in H. + eapply of_tree_of_P; eauto with to_smt. + eapply tree_a_wf_tree_key_wf; eauto. + Qed. + + Lemma AHof_tree_of_P_2_hv : + forall t p v x h, + AH.hash_value x v h = (p, t) -> + match_one a_t t -> + PArray.get a (of_P p) = v. + Proof. + unfold match_one; intros. apply AHhash_value_in in H. + pose proof H. apply H0 in H. + eapply of_tree_of_P; eauto with to_smt. + eapply tree_a_wf_tree_key_wf; eauto. + Qed. + + Lemma is_true_Atom_wf : + is_true (Atom.wf a). + Proof. + unfold is_true in *. unfold check_atom in CHKA. repeat (destruct_match; try discriminate; []); auto. + Qed. + + Lemma is_true_Form_wf : + is_true (wf f). + Proof. + unfold is_true in *. unfold check_form in CHKF. repeat (destruct_match; try discriminate; []); crush. + Qed. + + Lemma is_true_default_a : + PArray.default a = Acop CO_xH. + Proof. + unfold is_true in *. unfold check_atom in CHKA. repeat (destruct_match; try discriminate; []); auto. + Qed. + + Lemma gt_1_in : + forall h h1 y, + h ! h1 = Some y -> + match_one f_t h -> + 1 < Z.pos h1. + Proof. + intros. unfold match_one in H0. apply H0 in H. + inv WFF. unfold gt_1 in *. + inv H2. inv H4. inv H5. + apply H2 in H. lia. + Qed. + + Lemma gt_1_in2 : + forall h h1 y, + h ! h1 = Some y -> + match_one f_t h -> + (Z.pos h1 - 2) * 2 < wB. + Proof. + intros. unfold match_one in H0. apply H0 in H. + inv WFF. apply max_key_correct in H. lia. + Qed. + + Lemma gt_1_in3 : + forall h h1 y, + h ! h1 = Some y -> + match_one f_t h -> + (Z.pos h1 - 2) * 2 + 1 < wB. + Proof. + intros. unfold match_one in H0. apply H0 in H. + inv WFF. apply max_key_correct in H. lia. + Qed. + + #[local] Hint Resolve gt_1_in : to_smt. + #[local] Hint Resolve gt_1_in2: to_smt. + #[local] Hint Resolve gt_1_in3: to_smt. + + Lemma blit_lt_length : + forall h1 h y, + h ! h1 = Some y -> + match_one f_t h -> + is_true (State.Lit.blit (P_of_P h1) <? PArray.length f)%int63. + Proof. + intros. rewrite blit_P_of_P; eauto with to_smt. + eapply in_tree_lt_array; eauto with to_smt. + eapply tree_f_wf_tree_key_wf; eauto. + Qed. + + Lemma blit_lt_length2 : + forall h1 h y max h', + hash_value max y h' = (h1, h) -> + match_one f_t h -> + is_true (State.Lit.blit (P_of_P h1) <? PArray.length f)%int63. + Proof. + intros. eapply hash_value_in in H; eauto using blit_lt_length. + Qed. + + Lemma blit_lt_length_N : + forall h1 h y, + h ! h1 = Some y -> + match_one f_t h -> + is_true (State.Lit.blit (N_of_P h1) <? PArray.length f)%int63. + Proof. + intros. rewrite blit_N_of_P; eauto with to_smt. + eapply in_tree_lt_array; eauto with to_smt. + eapply tree_f_wf_tree_key_wf; eauto. + Qed. + + Lemma blit_lt_length_N2 : + forall h1 h y max h', + hash_value max y h' = (h1, h) -> + match_one f_t h -> + is_true (State.Lit.blit (N_of_P h1) <? PArray.length f)%int63. + Proof. + intros. eapply hash_value_in in H; eauto using blit_lt_length_N. + Qed. + + Lemma lt_pos_in : + forall h1 y h, + h ! h1 = Some y -> + match_one f_t h -> + 0 <= (Z.pos h1 - 2) * 2 < wB. + Proof. + intros. inversion WFF as [BOUNDS [MAXKEY [GT [S2 S3]]]]. unfold gt_1 in GT. + unfold match_one in *. apply H0 in H. pose proof H as X. apply GT in H. + apply max_key_correct in X. lia. + Qed. + + Lemma lt_pos_in2 : + forall h1 y h max h', + hash_value max y h' = (h1, h) -> + match_one f_t h -> + 0 <= (Z.pos h1 - 2) * 2 < wB. + Proof. + intros. eapply hash_value_in in H. eapply lt_pos_in; eauto. + Qed. + + #[local] Hint Resolve is_true_Atom_wf : to_smt. + #[local] Hint Resolve is_true_Form_wf : to_smt. + #[local] Hint Resolve is_true_default_a : to_smt. + #[local] Hint Resolve blit_lt_length2 : to_smt. + #[local] Hint Resolve lt_pos_in2 : to_smt. + #[local] Hint Resolve blit_lt_length_N2 : to_smt. + #[local] Hint Resolve tree_a_wf_tree_key_wf : to_smt. + #[local] Hint Resolve tree_f_wf_tree_key_wf : to_smt. + + #[local] Opaque Z.sub. + + Lemma to_smt_i_correct : + forall h p i, + match_one a_t h -> + to_smt_i h p = Some i -> + Atom.interp_aux t_i_empty la_arr (PArray.get (Atom.t_interp t_i_empty la_arr a)) (PArray.get a i) + = Bval _ Typ.TZ (Zval (eval_hash_pred p la)). + Proof. + intros * MATCH TO_SMT. + unfold to_smt_i in TO_SMT. + destruct_match; try discriminate; simplify. + unfold tree_a_wf in WFA. inversion WFA as [BOUND [PROP [AT1 [AT2 AT3]]]]. + replace (of_pos 1) with (of_P 3) by auto. + erewrite ! of_tree_of_P; try eassumption; try lia; simpl. + rewrite Atom.t_interp_wf by auto with to_smt. + erewrite ! of_tree_of_P; try eassumption; try lia; auto. + repeat split; auto; try lia. + repeat split; auto; try lia. + unfold tree_a_wf in WFA. inversion WFA as [BOUND [PROP [AT1 [AT2 AT3]]]]. + replace (of_pos 2) with (of_P 4) by auto. + erewrite ! of_tree_of_P; try eassumption; try lia; simpl. + rewrite Atom.t_interp_wf by auto with to_smt. + erewrite ! of_tree_of_P; try eassumption; try lia; simpl. + rewrite Atom.t_interp_wf by auto with to_smt. + erewrite ! of_tree_of_P; try eassumption; try lia; simpl. auto. + repeat split; auto; try lia. + repeat split; auto; try lia. + repeat split; auto; try lia. + + repeat (destruct_match; try discriminate; []). simplify. + apply AH.find_tree_correct in Heqo. + erewrite ! AHof_tree_of_P_2; try eassumption; try lia; simpl. + destruct_match. + rewrite MATCH_LA in Heqt. + destruct (la ! p0) eqn:?; [destruct b|]; simplify; unfold t_func_mk, Tval in Heqt; + symmetry in Heqt; inv Heqt; eapply Eqdep.EqdepTheory.inj_pair2 in H5; subst; auto. + lia. + Qed. + + Lemma interp_eval_pred : + forall h13 h11 h6 p0 i h8 h10 h9 h12, + match_one f_t h13 -> + match_one a_t h11 -> + match_one a_t h6 -> + eval_hash_pred p0 la <> Some true -> + to_smt_i h6 p0 = Some i -> + AH.hash_value 1%positive (Abop (BO_eq Typ.TZ) i (Int63.of_pos 1)) h8 = (h10, h11) -> + hash_value 1%positive (LFatom (of_P h10)) h9 = (h12, h13) -> + State.Lit.interp (interp_state_var (interp_form_hatom t_i_empty la_arr a) + (interp_form_hatom_bv t_i_empty la_arr a) f) + (N_of_P h12) = true. + Proof. + intros * M1 M2 M3 EVAL TOSMT HASH1 HASH2. unfold State.Lit.interp. + unfold State.Var.interp, interp_state_var. + pose proof HASH1 as IN1. eapply AHhash_value_in in IN1. + pose proof HASH2 as IN2. eapply hash_value_in in IN2. + rewrite is_neg_N_of_P by eauto with to_smt. + rewrite t_interp_wf by eauto with to_smt. + rewrite blit_N_of_P by eauto with to_smt. + erewrite ! of_tree_of_P_2 by eauto with to_smt; simpl. + unfold interp_form_hatom, interp_hatom. + rewrite Atom.t_interp_wf by eauto with to_smt. + erewrite ! AHof_tree_of_P_2 by eauto with to_smt; simpl. + rewrite !Atom.t_interp_wf by eauto with to_smt. + erewrite to_smt_i_correct by eauto with to_smt. + replace (of_pos 1) with (of_P 3) by auto. + inversion WFA as [X1 [X2 [X3 [X4 X5]]]]. + erewrite of_tree_of_P; + [idtac|idtac|eauto with to_smt|eauto with to_smt|eauto with to_smt]. + simpl. rewrite !Atom.t_interp_wf by eauto with to_smt. + erewrite of_tree_of_P; + [idtac|idtac|eauto with to_smt|eauto with to_smt|eauto with to_smt]. + simpl. + destruct (eval_hash_pred p0 la) eqn:?; [destruct b|]; try discriminate; auto. + eapply tree_a_wf_tree_key_wf; eauto. + eapply tree_a_wf_tree_key_wf; eauto. + Qed. + + Lemma eval_hash_pred_Pand_lt : + forall la p1 p2, + Zval (eval_hash_pred (p1 ∧ p2) la) = + if Zval (eval_hash_pred p1 la) <? Zval (eval_hash_pred p2 la) + then Zval (eval_hash_pred p1 la) else Zval (eval_hash_pred p2 la). + Proof. + intros. destruct (eval_hash_pred p1 la0) eqn:A; [destruct b|]; + destruct (eval_hash_pred p2 la0) eqn:B; try destruct b; + simplify; rewrite !A; rewrite !B; auto. + Qed. + + Lemma eval_hash_pred_Por_lt : + forall la p1 p2, + Zval (eval_hash_pred (p1 ∨ p2) la) = + if Zval (eval_hash_pred p1 la) <? Zval (eval_hash_pred p2 la) + then Zval (eval_hash_pred p2 la) else Zval (eval_hash_pred p1 la). + Proof. + intros. destruct (eval_hash_pred p1 la0) eqn:A; [destruct b|]; + destruct (eval_hash_pred p2 la0) eqn:B; try destruct b; + simplify; rewrite !A; rewrite !B; auto. + Qed. + + Lemma eval_hash_pred_Pimp_lt : + forall la p1 p2, + Zval (eval_hash_pred (p1 → p2) la) = + if 1 <? 1 - Zval (eval_hash_pred p1 la) + Zval (eval_hash_pred p2 la) + then 1 else 1 - Zval (eval_hash_pred p1 la) + Zval (eval_hash_pred p2 la). + Proof. + intros. destruct (eval_hash_pred p1 la0) eqn:A; [destruct b|]; + destruct (eval_hash_pred p2 la0) eqn:B; try destruct b; + simplify; rewrite !A; rewrite !B; auto. + Qed. + + Lemma eval_hash_pred_Pnot_lt : + forall la p1, + Zval (eval_hash_pred (¬ p1) la) = - Zval (eval_hash_pred p1 la). + Proof. + intros. destruct (eval_hash_pred p1 la0) eqn:A; [destruct b|]; + simplify; rewrite !A; auto. + Qed. + + Lemma to_smt_and_correct : + forall h fa0 i p1_1 i0 p1_2 i1 h0 h9 h8 h1, + to_smt_and h0 h i i0 i1 = (h9, h8, h1) -> + match_one f_t h9 -> + match_one a_t h8 -> + to_smt_i h (Pbase fa0) = Some i -> + to_smt_i h p1_1 = Some i0 -> + to_smt_i h p1_2 = Some i1 -> + eval_hash_pred (equiv (Pbase fa0) (p1_1 ∧ p1_2)) la = Some true -> + State.Lit.interp (interp_state_var (interp_form_hatom t_i_empty la_arr a) + (interp_form_hatom_bv t_i_empty la_arr a) f) + (P_of_P h1) = true. + Proof. + intros * SMTAND M1 M2 SMT1 SMT2 SMT3 EVAL. unfold State.Lit.interp. + unfold to_smt_and in SMTAND. repeat ldestruct_match; []. inversion SMTAND; clear SMTAND; subst. + assert (match_one f_t h15) by (eapply match_one_hash_value; eauto). + assert (match_one f_t h13) by (eapply match_one_hash_value; eauto). + assert (match_one f_t h11) by (eapply match_one_hash_value; eauto). + assert (match_one f_t h0) by (eapply match_one_hash_value; eauto). + assert (match_one a_t h5) by (eapply AHmatch_one_hash_value; eauto). + assert (match_one a_t h3) by (eapply AHmatch_one_hash_value; eauto). + assert (match_one a_t h) by (eapply AHmatch_one_hash_value; eauto). + rewrite is_pos_P_of_P by eauto with to_smt. + unfold State.Var.interp. unfold interp_state_var. + rewrite t_interp_wf by eauto with to_smt. + rewrite blit_P_of_P. + erewrite of_tree_of_P_2_hv; try eassumption. simpl. + unfold State.Lit.interp, State.Var.interp. + rewrite !is_pos_P_of_P by eauto with to_smt. + rewrite !t_interp_wf by eauto with to_smt. + rewrite !blit_P_of_P. + erewrite !of_tree_of_P_2_hv; try eassumption; simpl. + unfold interp_form_hatom, interp_hatom. + rewrite !Atom.t_interp_wf by auto with to_smt. + erewrite !AHof_tree_of_P_2_hv; try eassumption; simpl. + rewrite !Atom.t_interp_wf by auto with to_smt. + erewrite !to_smt_i_correct by eassumption. simpl. + apply eval_equiv in EVAL. + replace (eval_hash_pred (Pbase fa0) la) with (la ! fa0) in EVAL by auto. + rewrite EVAL. + rewrite !eval_hash_pred_Pand_lt. + destruct_match; apply Typ.i_eqb_refl. + all: match goal with + | H: hash_value _ _ _ = (?v, _) |- _ < Z.pos ?v => + apply hash_value_in in H; eauto with to_smt + | H: hash_value _ _ _ = (?v, _) |- (Z.pos ?v - _) * _ < _ => + apply hash_value_in in H; eauto with to_smt + end. + Qed. + + Lemma to_smt_and_match_one : + forall h i i0 i1 h0 h9 h8 h1, + to_smt_and h0 h i i0 i1 = (h9, h8, h1) -> + match_one f_t h9 -> + match_one f_t h0. + Proof. + unfold to_smt_and; intros * SMTAND M1. + repeat ldestruct_match; []. inversion SMTAND; clear SMTAND; subst. + repeat (eapply match_one_hash_value; try eassumption). + Qed. + + Lemma to_smt_and_match_one2 : + forall h i i0 i1 h0 h9 h8 h1, + to_smt_and h0 h i i0 i1 = (h9, h8, h1) -> + match_one a_t h8 -> + match_one a_t h. + Proof. + unfold to_smt_and; intros * SMTAND M1. + repeat ldestruct_match; []. inversion SMTAND; clear SMTAND; subst. + repeat (eapply AHmatch_one_hash_value; try eassumption). + Qed. + + Lemma to_smt_or_correct : + forall h fa0 i p1_1 i0 p1_2 i1 h0 h9 h8 h1, + to_smt_or h0 h i i0 i1 = (h9, h8, h1) -> + match_one f_t h9 -> + match_one a_t h8 -> + to_smt_i h (Pbase fa0) = Some i -> + to_smt_i h p1_1 = Some i0 -> + to_smt_i h p1_2 = Some i1 -> + eval_hash_pred (equiv (Pbase fa0) (p1_1 ∨ p1_2)) la = Some true -> + State.Lit.interp (interp_state_var (interp_form_hatom t_i_empty la_arr a) + (interp_form_hatom_bv t_i_empty la_arr a) f) + (P_of_P h1) = true. + Proof. + intros * SMTOR M1 M2 SMT1 SMT2 SMT3 EVAL. unfold State.Lit.interp. + unfold to_smt_or in SMTOR. repeat ldestruct_match; []. inversion SMTOR; clear SMTOR; subst. + assert (match_one f_t h15) by (eapply match_one_hash_value; eauto). + assert (match_one f_t h13) by (eapply match_one_hash_value; eauto). + assert (match_one f_t h11) by (eapply match_one_hash_value; eauto). + assert (match_one f_t h0) by (eapply match_one_hash_value; eauto). + assert (match_one a_t h5) by (eapply AHmatch_one_hash_value; eauto). + assert (match_one a_t h3) by (eapply AHmatch_one_hash_value; eauto). + assert (match_one a_t h) by (eapply AHmatch_one_hash_value; eauto). + rewrite is_pos_P_of_P by eauto with to_smt. + unfold State.Var.interp. unfold interp_state_var. + rewrite t_interp_wf by eauto with to_smt. + rewrite blit_P_of_P. + erewrite of_tree_of_P_2_hv; try eassumption. simpl. + unfold State.Lit.interp, State.Var.interp. + rewrite !is_pos_P_of_P by eauto with to_smt. + rewrite !t_interp_wf by eauto with to_smt. + rewrite !blit_P_of_P. + erewrite !of_tree_of_P_2_hv; try eassumption; simpl. + unfold interp_form_hatom, interp_hatom. + rewrite !Atom.t_interp_wf by auto with to_smt. + erewrite !AHof_tree_of_P_2_hv; try eassumption; simpl. + rewrite !Atom.t_interp_wf by auto with to_smt. + erewrite !to_smt_i_correct by eassumption. simpl. + apply eval_equiv in EVAL. + replace (eval_hash_pred (Pbase fa0) la) with (la ! fa0) in EVAL by auto. + rewrite EVAL. + rewrite !eval_hash_pred_Por_lt. + destruct_match; apply Typ.i_eqb_refl. + all: match goal with + | H: hash_value _ _ _ = (?v, _) |- _ < Z.pos ?v => + apply hash_value_in in H; eauto with to_smt + | H: hash_value _ _ _ = (?v, _) |- (Z.pos ?v - _) * _ < _ => + apply hash_value_in in H; eauto with to_smt + end. + Qed. + + Lemma to_smt_or_match_one : + forall h i i0 i1 h0 h9 h8 h1, + to_smt_or h0 h i i0 i1 = (h9, h8, h1) -> + match_one f_t h9 -> + match_one f_t h0. + Proof. + unfold to_smt_or; intros * SMTOR M1. + repeat ldestruct_match; []. inversion SMTOR; clear SMTOR; subst. + repeat (eapply match_one_hash_value; try eassumption). + Qed. + + Lemma to_smt_or_match_one2 : + forall h i i0 i1 h0 h9 h8 h1, + to_smt_or h0 h i i0 i1 = (h9, h8, h1) -> + match_one a_t h8 -> + match_one a_t h. + Proof. + unfold to_smt_or; intros * SMTOR M1. + repeat ldestruct_match; []. inversion SMTOR; clear SMTOR; subst. + repeat (eapply AHmatch_one_hash_value; try eassumption). + Qed. + + Lemma to_smt_imp_correct : + forall h fa0 i p1_1 i0 p1_2 i1 h0 h9 h8 h1, + to_smt_imp h0 h i i0 i1 = (h9, h8, h1) -> + match_one f_t h9 -> + match_one a_t h8 -> + to_smt_i h (Pbase fa0) = Some i -> + to_smt_i h p1_1 = Some i0 -> + to_smt_i h p1_2 = Some i1 -> + eval_hash_pred (equiv (Pbase fa0) (p1_1 → p1_2)) la = Some true -> + State.Lit.interp (interp_state_var (interp_form_hatom t_i_empty la_arr a) + (interp_form_hatom_bv t_i_empty la_arr a) f) + (P_of_P h1) = true. + Proof. + intros * SMTIMP M1 M2 SMT1 SMT2 SMT3 EVAL. unfold State.Lit.interp. + unfold to_smt_imp in SMTIMP. repeat ldestruct_match; []. inversion SMTIMP; clear SMTIMP; subst. + assert (match_one f_t h19) by (eapply match_one_hash_value; eauto). + assert (match_one f_t h17) by (eapply match_one_hash_value; eauto). + assert (match_one f_t h15) by (eapply match_one_hash_value; eauto). + assert (match_one f_t h0) by (eapply match_one_hash_value; eauto). + assert (match_one a_t h11) by (eapply AHmatch_one_hash_value; eauto). + assert (match_one a_t h7) by (eapply AHmatch_one_hash_value; eauto). + assert (match_one a_t h5) by (eapply AHmatch_one_hash_value; eauto). + assert (match_one a_t h3) by (eapply AHmatch_one_hash_value; eauto). + assert (match_one a_t h) by (eapply AHmatch_one_hash_value; eauto). + rewrite is_pos_P_of_P by eauto with to_smt. + unfold State.Var.interp. unfold interp_state_var. + rewrite t_interp_wf by eauto with to_smt. + rewrite blit_P_of_P. + erewrite of_tree_of_P_2_hv by eassumption. simpl. + unfold State.Lit.interp, State.Var.interp. + rewrite !is_pos_P_of_P by eauto with to_smt. + rewrite !t_interp_wf by eauto with to_smt. + rewrite !blit_P_of_P. + erewrite !of_tree_of_P_2_hv by eassumption; simpl. + unfold interp_form_hatom, interp_hatom. + rewrite !Atom.t_interp_wf by auto with to_smt. + erewrite !AHof_tree_of_P_2_hv by eassumption; simpl. + rewrite !Atom.t_interp_wf by auto with to_smt. + inversion WFA as [WFA1 [WFA2 [WFA3 [WFA4 WFA5]]]]. + erewrite !of_tree_of_P by + (try eassumption; try (eapply tree_a_wf_tree_key_wf; eauto); lia). + simpl. + rewrite !Atom.t_interp_wf by auto with to_smt. + erewrite !of_tree_of_P by + (try eassumption; try (eapply tree_a_wf_tree_key_wf; eauto); lia). + erewrite !AHof_tree_of_P_2_hv by eassumption. + erewrite !to_smt_i_correct by eassumption. cbn [Atom.interp_aux]. + rewrite !Atom.t_interp_wf by auto with to_smt. + erewrite !AHof_tree_of_P_2_hv by eassumption. + erewrite !to_smt_i_correct by eassumption. cbn [Atom.interp_aux]. + rewrite !Atom.t_interp_wf by auto with to_smt. + erewrite !of_tree_of_P by + (try eassumption; try (eapply tree_a_wf_tree_key_wf; eauto); lia). + cbn [Atom.interp_aux]. + rewrite !Atom.t_interp_wf by auto with to_smt. + erewrite !of_tree_of_P by + (try eassumption; try (eapply tree_a_wf_tree_key_wf; eauto); lia). + erewrite !to_smt_i_correct by eassumption. + cbn -[Z.sub]. + apply eval_equiv in EVAL. + replace (eval_hash_pred (Pbase fa0) la) with (la ! fa0) in EVAL by auto. + rewrite EVAL. + rewrite !eval_hash_pred_Pimp_lt. + destruct_match; auto using Z.eqb_refl. + all: match goal with + | H: hash_value _ _ _ = (?v, _) |- _ < Z.pos ?v => + apply hash_value_in in H; eauto with to_smt + | H: hash_value _ _ _ = (?v, _) |- (Z.pos ?v - _) * _ < _ => + apply hash_value_in in H; eauto with to_smt + end. + Qed. + + Lemma to_smt_imp_match_one : + forall h i i0 i1 h0 h9 h8 h1, + to_smt_imp h0 h i i0 i1 = (h9, h8, h1) -> + match_one f_t h9 -> + match_one f_t h0. + Proof. + unfold to_smt_imp; intros * SMTIMP M1. + repeat ldestruct_match; []. inversion SMTIMP; clear SMTIMP; subst. + repeat (eapply match_one_hash_value; try eassumption). + Qed. + + Lemma to_smt_imp_match_one2 : + forall h i i0 i1 h0 h9 h8 h1, + to_smt_imp h0 h i i0 i1 = (h9, h8, h1) -> + match_one a_t h8 -> + match_one a_t h. + Proof. + unfold to_smt_imp; intros * SMTIMP M1. + repeat ldestruct_match; []. inversion SMTIMP; clear SMTIMP; subst. + repeat (eapply AHmatch_one_hash_value; try eassumption). + Qed. + + Lemma to_smt_not_correct : + forall h fa0 i p1_1 i0 h0 h9 h8 h1, + to_smt_not h0 h i i0 = (h9, h8, h1) -> + match_one f_t h9 -> + match_one a_t h8 -> + to_smt_i h (Pbase fa0) = Some i -> + to_smt_i h p1_1 = Some i0 -> + eval_hash_pred (equiv (Pbase fa0) (¬ p1_1)) la = Some true -> + State.Lit.interp (interp_state_var (interp_form_hatom t_i_empty la_arr a) + (interp_form_hatom_bv t_i_empty la_arr a) f) + (P_of_P h1) = true. + Proof. + intros * SMTNOT M1 M2 SMT1 SMT2 EVAL. unfold State.Lit.interp. + unfold to_smt_not in SMTNOT. repeat ldestruct_match; []. inversion SMTNOT; clear SMTNOT; subst. + assert (match_one f_t h0) by (eapply match_one_hash_value; eauto). + assert (match_one a_t h3) by (eapply AHmatch_one_hash_value; eauto). + assert (match_one a_t h) by (eapply AHmatch_one_hash_value; eauto). + rewrite is_pos_P_of_P by eauto with to_smt. + unfold State.Var.interp. unfold interp_state_var. + rewrite t_interp_wf by eauto with to_smt. + rewrite blit_P_of_P. + erewrite of_tree_of_P_2_hv by eassumption. cbn. + unfold interp_form_hatom. unfold interp_hatom. + rewrite Atom.t_interp_wf by eauto with to_smt. + erewrite !AHof_tree_of_P_2_hv by eassumption. cbn. + rewrite Atom.t_interp_wf by eauto with to_smt. + inversion WFA as [WFA1 [WFA2 [WFA3 [WFA4 WFA5]]]]. + erewrite !to_smt_i_correct by eassumption. + rewrite Atom.t_interp_wf by eauto with to_smt. + erewrite !AHof_tree_of_P_2_hv by eassumption. cbn. + rewrite Atom.t_interp_wf by eauto with to_smt. + erewrite !to_smt_i_correct by eassumption. cbn. + apply eval_equiv in EVAL. + replace (eval_hash_pred (Pbase fa0) la) with (la ! fa0) in EVAL by auto. + rewrite EVAL. + rewrite !eval_hash_pred_Pnot_lt. + auto using Z.eqb_refl. + all: match goal with + | H: hash_value _ _ _ = (?v, _) |- _ < Z.pos ?v => + apply hash_value_in in H; eauto with to_smt + | H: hash_value _ _ _ = (?v, _) |- (Z.pos ?v - _) * _ < _ => + apply hash_value_in in H; eauto with to_smt + end. + Qed. + + Lemma to_smt_not_match_one : + forall h i i0 h0 h9 h8 h1, + to_smt_not h0 h i i0 = (h9, h8, h1) -> + match_one f_t h9 -> + match_one f_t h0. + Proof. + unfold to_smt_not; intros * SMTNOT M1. + repeat ldestruct_match; []. inversion SMTNOT; clear SMTNOT; subst. + repeat (eapply match_one_hash_value; try eassumption). + Qed. + + Lemma to_smt_not_match_one2 : + forall h i i0 h0 h9 h8 h1, + to_smt_not h0 h i i0 = (h9, h8, h1) -> + match_one a_t h8 -> + match_one a_t h. + Proof. + unfold to_smt_not; intros * SMTNOT M1. + repeat ldestruct_match; []. inversion SMTNOT; clear SMTNOT; subst. + repeat (eapply AHmatch_one_hash_value; try eassumption). + Qed. + + Lemma fold_to_smt_l_correct : + forall l l0 h7 h6 l1 h9 h8 x, + match_one f_t h9 -> match_one a_t h8 -> + fold_right to_smt_l (Some (l0, h7, h6)) l = Some (l1, h9, h8) -> + (forall y, In y l0 -> State.Lit.interp (interp_state_var + (interp_form_hatom t_i_empty la_arr a) + (interp_form_hatom_bv t_i_empty la_arr a) f) y = true) -> + eval_hash_pred (fold_left Pand (to_equiv l) T) la = Some true -> + In x l1 -> + State.Lit.interp (interp_state_var + (interp_form_hatom t_i_empty la_arr a) + (interp_form_hatom_bv t_i_empty la_arr a) f) x = true. + Proof. + induction l as [|a0 l' IHl]; intros * M1 M2 TO_SMT_L PREV_LIST EVAL_FOLD IN. simplify; eauto. + simpl in EVAL_FOLD. + apply eval_hash_pred_fold_Pand4 in EVAL_FOLD. inversion EVAL_FOLD as [EVAL_FOLD' EVAL_A0]. + destruct a0 as [fa0 sa0]. cbn [fst snd] in *. + simpl in TO_SMT_L. unfold to_smt_l in TO_SMT_L. fold to_smt_l in TO_SMT_L. + repeat (destruct_match; try discriminate; []). + destruct_match; try discriminate; + repeat (destruct_match; try discriminate; []); inversion TO_SMT_L; subst; + [ assert (match_one f_t h0) by (eapply to_smt_and_match_one; eauto); + assert (match_one a_t h) by (eapply to_smt_and_match_one2; eauto) + | assert (match_one f_t h0) by (eapply to_smt_or_match_one; eauto); + assert (match_one a_t h) by (eapply to_smt_or_match_one2; eauto) + | assert (match_one f_t h0) by (eapply to_smt_imp_match_one; eauto); + assert (match_one a_t h) by (eapply to_smt_imp_match_one2; eauto) + | assert (match_one f_t h0) by (eapply to_smt_not_match_one; eauto); + assert (match_one a_t h) by (eapply to_smt_not_match_one2; eauto) ]. + all: apply in_app_or in IN; inversion IN as [INL | INP]; clear IN; [solve [eauto]|]; + inversion INP as [EQ | NIL]; clear INP; [|solve [inversion NIL]]; subst. + - eapply to_smt_and_correct; eauto. now rewrite <- eval_hash_pred_T_Pand. + - eapply to_smt_or_correct; eauto. now rewrite <- eval_hash_pred_T_Pand. + - eapply to_smt_imp_correct; eauto. now rewrite <- eval_hash_pred_T_Pand. + - eapply to_smt_not_correct; eauto. now rewrite <- eval_hash_pred_T_Pand. + Qed. + + Lemma gt_1_fold_init : + forall (lx: list unit) v' l0 init_forms init_atoms v l1 form_defs atom_defs, + fold_right (fun _ => declare_atoms_with_bounds) + (v', l0, init_forms, init_atoms) + lx = (v, l1, form_defs, atom_defs) -> + 0 <= v' -> 0 <= v. + Proof. + induction lx; crush. + remember (fold_right (fun _ : unit => declare_atoms_with_bounds) + (v', l0, init_forms, init_atoms) lx) as folds. + symmetry in Heqfolds. destruct folds as [[[A1 A2] A3] A4]. + eapply IHlx in Heqfolds; eauto. unfold declare_atoms_with_bounds in H. + repeat (destruct_match; []). simplify. lia. + Qed. + + Lemma fold_init_correct : + forall (lx: list unit) l1 x init_forms init_atoms form_defs atom_defs l0 v v', + match_one f_t form_defs -> match_one a_t atom_defs -> + fold_right (fun _ => declare_atoms_with_bounds) + (v', l0, init_forms, init_atoms) + lx = (v, l1, form_defs, atom_defs) -> + (forall y, In y l0 -> + State.Lit.interp (interp_state_var + (interp_form_hatom t_i_empty la_arr a) + (interp_form_hatom_bv t_i_empty la_arr a) f) y = true) -> + In x l1 -> 0 <= v' -> v < wB -> + State.Lit.interp (interp_state_var + (interp_form_hatom t_i_empty la_arr a) + (interp_form_hatom_bv t_i_empty la_arr a) f) x = true. + Proof. + induction lx; crush. assert (VPRIME := H4). clear H4. assert (VPRIME2 := H5). clear H5. + remember (fold_right (fun _ : unit => declare_atoms_with_bounds) (v', l0, init_forms, init_atoms) lx) as folds. + symmetry in Heqfolds. destruct folds as [[[V R] F] A]. simpl in H1. + repeat (destruct_match; []). simplify. + eapply in_app in H3. inv H3. eapply IHlx in Heqfolds; eauto; try lia. + assert (match_one f_t h4) by (eapply match_one_hash_value; eauto). + assert (match_one f_t h10) by (eapply match_one_hash_value; eauto). + eapply match_one_hash_value; eauto. + assert (match_one a_t h14) by (eapply AHmatch_one_hash_value; eauto). + assert (match_one a_t h0) by (eapply AHmatch_one_hash_value; eauto). + eapply AHmatch_one_hash_value; eauto. + assert (F1: match_one f_t h4) by (eapply match_one_hash_value; eauto). + assert (F2: match_one f_t h10) by (eapply match_one_hash_value; eauto). + assert (A1: match_one a_t h14) by (eapply AHmatch_one_hash_value; eauto). + assert (A2: match_one a_t h0) by (eapply AHmatch_one_hash_value; eauto). + inv H1; [|inv H3]. + unfold State.Lit.interp. + rewrite is_pos_P_of_P by eauto with to_smt. + unfold State.Var.interp. unfold interp_state_var. + rewrite t_interp_wf by eauto with to_smt. + rewrite blit_P_of_P. + erewrite of_tree_of_P_2_hv by eassumption. cbn. + unfold State.Lit.interp. + rewrite !is_pos_P_of_P by eauto with to_smt. + unfold State.Var.interp. rewrite !t_interp_wf by eauto with to_smt. + rewrite !blit_P_of_P. + erewrite !of_tree_of_P_2_hv by eassumption. cbn. + unfold interp_form_hatom, interp_hatom. + rewrite !Atom.t_interp_wf by eauto with to_smt. + erewrite !AHof_tree_of_P_2_hv by eassumption. cbn. + inversion WFA as [WFA1 [WFA2 [WFA3 [WFA4 WFA5]]]]. + rewrite !Atom.t_interp_wf by eauto with to_smt. + erewrite !AHof_tree_of_P_2 by (try eassumption; reflexivity). cbn. + rewrite !Atom.t_interp_wf by eauto with to_smt. + erewrite !AHof_tree_of_P_2 by (try eassumption; reflexivity). cbn. + rewrite !Atom.t_interp_wf by eauto with to_smt. + erewrite !AHof_tree_of_P_2 by (try eassumption; reflexivity). cbn. + erewrite !AHof_tree_of_P_2_hv by eassumption. cbn. + repeat (destruct_match; []). repeat (ldestruct_match; []). + assert (0 <= V). + { eapply gt_1_fold_init; eauto. } + replace (of_Z V) with (of_P (Z.to_pos (V + 2))) in Heqt by + (unfold of_P; f_equal; rewrite Z2Pos.id by lia; lia). + rewrite MATCH_LA in Heqt. + destruct (la ! (Z.to_pos (V + 2))). destruct b. + unfold t_func_mk, Tval in Heqt. cbn in Heqt. + symmetry in Heqt. inv Heqt. eapply Eqdep.EqdepTheory.inj_pair2 in H5. subst. + simpl in Heqb. unfold Bval in Heqb. symmetry in Heqb. inv Heqb. + eapply Eqdep.EqdepTheory.inj_pair2 in H5. subst. auto. + unfold t_func_mk, Tval in Heqt. cbn in Heqt. + symmetry in Heqt. inv Heqt. eapply Eqdep.EqdepTheory.inj_pair2 in H5. subst. + simpl in Heqb. unfold Bval in Heqb. symmetry in Heqb. inv Heqb. + eapply Eqdep.EqdepTheory.inj_pair2 in H5. subst. auto. + unfold t_func_mk, Tval in Heqt. cbn in Heqt. + symmetry in Heqt. inv Heqt. eapply Eqdep.EqdepTheory.inj_pair2 in H5. subst. + simpl in Heqb. unfold Bval in Heqb. symmetry in Heqb. inv Heqb. + eapply Eqdep.EqdepTheory.inj_pair2 in H5. subst. auto. + all: try match goal with + | H: hash_value _ _ _ = (?v, _) |- _ < Z.pos ?v => + apply hash_value_in in H; eauto with to_smt + | H: hash_value _ _ _ = (?v, _) |- (Z.pos ?v - _) * _ < _ => + apply hash_value_in in H; eauto with to_smt + end. + rewrite Z2Pos.id; lia. + Qed. + +End PROOF_MORE. + + Definition pl_leq {A} (a b: positive * A): Prop := + ((fst a) <= (fst b))%positive. + + Definition pl_lt {A} (a b: positive * A): Prop := + ((fst a) < (fst b))%positive. + + Definition pl_eq {A} (a b: positive * A): Prop := + fst a = fst b. + + Lemma pl_lt_trans : + forall A (a b c: positive * A), pl_lt a b -> pl_lt b c -> pl_lt a c. + Proof. unfold pl_lt; lia. Qed. + + Lemma pl_lt_irrefl : + forall A (a: positive * A), ~ pl_lt a a. + Proof. unfold pl_lt; lia. Qed. + + Lemma pl_lt_asymm : + forall A (a b: positive * A), pl_lt a b -> pl_lt b a -> False. + Proof. unfold pl_lt; lia. Qed. + + Definition Sorted := Sorted (@pl_lt predicate). + + Lemma pl_leq_refl : + forall A (a: positive * A), pl_leq a a. + Proof. unfold pl_leq; reflexivity. Qed. + + Lemma pl_leq_trans : + forall A (a b c: positive * A), pl_leq a b -> pl_leq b c -> pl_leq a c. + Proof. unfold pl_leq; intros; lia. Qed. + + Lemma pl_eq_refl : + forall A (a: positive * A), pl_eq a a. + Proof. unfold pl_leq; reflexivity. Qed. + + Lemma pl_eq_symm : + forall A (a b: positive * A), pl_eq a b -> pl_eq b a. + Proof. unfold pl_eq; intros; lia. Qed. + + Lemma pl_eq_trans : + forall A (a b c: positive * A), pl_eq a b -> pl_eq b c -> pl_eq a c. + Proof. unfold pl_eq; intros; lia. Qed. + + #[global] Instance Reflexive_pl_leq {A} : Reflexive (@pl_leq A). + Proof. unfold Reflexive. apply pl_leq_refl. Qed. + + #[global] Instance Transitive_pl_leq {A} : Transitive (@pl_leq A). + Proof. unfold Transitive. apply pl_leq_trans. Qed. + + #[global] Instance PreOrder_pl_leq {A} : PreOrder (@pl_leq A) := {}. + + #[global] Instance Reflexive_pl_eq {A} : Reflexive (@pl_eq A). + Proof. unfold Reflexive. apply pl_eq_refl. Qed. + + #[global] Instance Symmetric_pl_eq {A} : Symmetric (@pl_eq A). + Proof. unfold Symmetric. apply pl_eq_symm. Qed. + + #[global] Instance Transitive_pl_eq {A} : Transitive (@pl_eq A). + Proof. unfold Transitive. apply pl_eq_trans. Qed. + + #[global] Instance Equivalence_pl_eq {A} : Equivalence (@pl_eq A) := {}. + + #[global] Instance Antisymmetric_pl_leq {A} : Antisymmetric (positive * A) pl_eq pl_leq. + Proof. unfold Antisymmetric, pl_eq, pl_leq; lia. Qed. + + #[global] Instance PartialOrder_pl_leq {A} : PartialOrder (@pl_eq A) (@pl_leq A). + Proof. + unfold PartialOrder, relation_equivalence, predicate_equivalence. crush. + unfold relation_conjunction, predicate_intersection. simplify. unfold pl_eq, pl_leq, Basics.flip. + split. lia. lia. + Qed. + + #[global] Instance Irreflexive_pl_lt {A} : Irreflexive (@pl_lt A). + Proof. unfold Irreflexive, Reflexive, complement. apply pl_lt_irrefl. Qed. + + #[global] Instance Asymmetric_pl_lt {A} : Asymmetric (@pl_lt A). + Proof. unfold Asymmetric. apply pl_lt_asymm. Qed. + + #[global] Instance Transitive_pl_lt {A} : Transitive (@pl_lt A). + Proof. unfold Transitive. apply pl_lt_trans. Qed. + + #[global] Instance StrictOrder_pl_lt {A} : StrictOrder (@pl_lt A) := {}. + + Lemma eval_hash_pred_upd_asgn : + forall l p a, + Forall (fun x => (max_hash_pred p < fst x)%positive) l -> + eval_hash_pred p (upd_asgn l a) = eval_hash_pred p a. + Proof. + induction l as [| c l IHl]; try solve [crush]. + intros * FORALL. + inversion FORALL as [|? ? EL FORALL2]; subst; clear FORALL. + unfold upd_asgn. cbn [fold_left]. destruct_match; + replace ((fold_left + (fun (a' : PTree.t bool) (el : positive * hash_pred) => + match eval_hash_pred (snd el) a' with + | Some p_b => PTree.set (fst el) p_b a' + | None => a' + end) l a)) with (upd_asgn l a) in * by auto; eauto. + rewrite IHl; auto. + rewrite eval_hash_pred_gso; auto. + Qed. + + Lemma upd_asgn_not_in : + forall l c1 a, + ~ In c1 (map fst l) -> + (upd_asgn l a) ! c1 = a ! c1. + Proof. + induction l as [| c l IHl ]; try solve [crush]. + intros * NOTIN. simplify. + assert (fst c <> c1). + { unfold not; intros. subst. apply NOTIN; tauto. } + assert (~ In c1 (map fst l)). + { unfold not; intros. apply NOTIN; tauto. } + destruct_match; auto. + erewrite IHl; eauto. rewrite PTree.gso; auto. + Qed. + + Definition equiv_list_wf l := + Sorted l /\ Forall (fun x => (max_hash_pred (snd x) < fst x)%positive) l. + + Lemma equiv_list_wf_inv : + forall l a, + equiv_list_wf (a :: l) -> + equiv_list_wf l /\ Forall (pl_lt a) l /\ (max_hash_pred (snd a) < fst a)%positive. + Proof. + unfold equiv_list_wf; intros * S. inversion S as [A B]. + split; split. + - inversion A; auto. + - inversion B; auto. + - apply Sorted_StronglySorted in A. inversion A; auto. + unfold Relations_1.Transitive. apply pl_lt_trans. + - inversion B; auto. + Qed. + + Lemma equiv_list_wf_nil : + equiv_list_wf nil. + Proof. + unfold equiv_list_wf; split; auto; apply Sorted_nil. + Qed. + + Lemma sorted_not_in : + forall A l c1 (c2: A), + Forall (pl_lt (c1, c2)) l -> + ~ In c1 (map fst l). + Proof. + unfold not; intros. + eapply list_in_map_inv in H0. inv H0. inv H1. + eapply Forall_forall in H; eauto. + destruct x. simpl in H. unfold pl_lt in H. simpl in H. lia. + Qed. + + Lemma upd_asgn_set_sorted : + forall l a x, + Forall (Pos.lt x) (map fst l) -> + (upd_asgn l a) ! x = a ! x. + Proof. + induction l; auto. + intros. cbn. inv H. erewrite IHl; auto. + destruct_match; auto; now rewrite PTree.gso by lia. + Qed. + + Lemma eval_hash_pred_equiv_asgn : + forall l p a, + eval_hash_pred p a = Some true -> + equiv_list_wf l -> + Forall (fun x => a ! (fst x) = None) l -> + Forall (fun x => (max_hash_pred p < fst x)%positive) l -> + eval_hash_pred (fold_left Pand (to_equiv l) p) (upd_asgn l a) = Some true. + Proof. + induction l as [|c l IHl]; [crush|]. intros p a EVAL WF FORALL MAX. + destruct c as [c1 c2]. simpl. + inversion FORALL as [| ? ? NONE FORALL2]; subst; cbn [fst snd] in *; clear FORALL. + inversion MAX as [| ? ? MAXC MAX2]; subst; cbn [fst snd] in *; clear MAX. + eapply equiv_list_wf_inv in WF. inversion WF as [WF2 [FRL MHP]]; subst; clear WF. + cbn [snd fst] in *. destruct_match. + - rewrite IHl; eauto. + apply pand_true2; [|rewrite eval_hash_pred_gso by lia; auto]. + apply eval_equiv2. symmetry; rewrite eval_hash_pred_gso by crush. + rewrite Heqo. crush. symmetry; apply PTree.gss. + apply Forall_forall; intros. repeat match goal with H: Forall _ _ |- _ => eapply Forall_forall in H; eauto end. + unfold pl_lt in *. simplify. rewrite PTree.gso by lia. auto. + apply Forall_forall; intros. repeat match goal with H: Forall _ _ |- _ => eapply Forall_forall in H; eauto end. + unfold pl_lt in *. simplify. lia. + - eapply IHl; eauto. apply pand_true2. + apply eval_equiv2. rewrite Heqo. + simpl. auto. auto. + apply Forall_forall; intros. repeat match goal with H: Forall _ _ |- _ => eapply Forall_forall in H; eauto end. + unfold pl_lt in *. simplify. lia. + Qed. + +#[local] Opaque max_key. +#[local] Opaque Z.sub. +#[local] Opaque PTree.get. + +Lemma check_tree_f_wf_correct : + forall f, + check_tree_f_wf f = true -> tree_f_wf f. +Proof. + unfold check_tree_f_wf, tree_f_wf. + simplify; auto. + unfold gt_1; intros. + assert ((x = 1 \/ 1 < x)%positive) by lia. + inv H5; auto. rewrite H0 in H3. discriminate. + repeat (destruct_match; try discriminate; []); auto. + repeat (destruct_match; try discriminate; []); auto. +Qed. + +Lemma check_tree_a_wf_correct : + forall a, + check_tree_a_wf a = true -> tree_a_wf a. +Proof. + unfold check_tree_a_wf, tree_a_wf. + simplify; auto. + unfold gt_1; intros. + assert ((x = 1 \/ 1 < x)%positive) by lia. + inv H6; auto. rewrite H in H4. discriminate. + repeat (destruct_match; try discriminate; []); auto. + repeat (destruct_match; try discriminate; []); auto. + subst. apply Int63.eqb_spec in H2. apply Int63.eqb_spec in H1. subst. + auto. + repeat (destruct_match; try discriminate; []); auto. + subst. apply Int63.eqb_spec in H2. apply Int63.eqb_spec in H1. subst. + auto. +Qed. + +#[local] Transparent max_key. +#[local] Transparent Z.sub. +#[local] Transparent PTree.get. + +Lemma to_smt_l_match_one2 : + forall a y l0 h0 h r' f' a', + to_smt_l a (Some (l0, h0, h)) = Some (r', f', a') -> + match_one y a' -> + match_one y h. +Proof. + destruct a. destruct p0; simplify; try discriminate; + repeat (destruct_match; try discriminate; []); simplify; + eauto using to_smt_and_match_one2, to_smt_or_match_one2, + to_smt_imp_match_one2, to_smt_not_match_one2. +Qed. + +Lemma to_smt_l_match_one : + forall a y l0 h0 h r' f' a', + to_smt_l a (Some (l0, h0, h)) = Some (r', f', a') -> + match_one y f' -> + match_one y h0. +Proof. + destruct a. destruct p0; simplify; try discriminate; + repeat (destruct_match; try discriminate; []); simplify; + eauto using to_smt_and_match_one, to_smt_or_match_one, + to_smt_imp_match_one, to_smt_not_match_one. +Qed. + +Lemma to_smt_l_fold_match_one2 : + forall l y r f a r' f' a', + fold_right to_smt_l (Some (r, f, a)) l = Some (r', f', a') -> + match_one y a' -> + match_one y a. +Proof. + induction l; crush. remember (fold_right to_smt_l (Some (r, f, a0)) l) as folds. + destruct folds; [|crush]; []. + symmetry in Heqfolds. + destruct p. destruct p. eapply IHl; eauto. + eapply to_smt_l_match_one2; eauto. +Qed. + +Lemma to_smt_l_fold_match_one : + forall l y r f a r' f' a', + fold_right to_smt_l (Some (r, f, a)) l = Some (r', f', a') -> + match_one y f' -> + match_one y f. +Proof. + induction l; crush. remember (fold_right to_smt_l (Some (r, f, a0)) l) as folds. + destruct folds; [|crush]; []. + symmetry in Heqfolds. + destruct p. destruct p. eapply IHl; eauto. + eapply to_smt_l_match_one; eauto. +Qed. + +#[local] Opaque Z.sub. + +Lemma valid_to_smt : + forall p r f a la max la_arr, + ~ (is_true (Euf_Checker.valid la_arr a f r)) -> + asgn_transl_spec la la_arr -> + to_smt max p = Some (r, f, a) -> + eval_hash_pred (fold_left Pand (to_equiv (snd p)) T) la = Some true -> + eval_pred_list p la = Some true. +Proof. + intros. + destruct (bo_dec (eval_pred_list p la) (Some true)); auto. + exfalso. eapply H. inv H0. inv H4. assert (DEFAULT1:=H0). + assert (DEFAULT2:=H5). clear H5. clear H0. + unfold to_smt in H1. repeat (destruct_match; try discriminate; []). + simplify. + unfold is_true. + unfold eval_pred_list in n. cbn [fst snd] in *. + apply pand_false in n; auto; []. + unfold Euf_Checker.valid. + rewrite Misc.afold_left_and. + apply forallb_forall. intros. + eapply to_list_of_list2 in H4; [|lia]. + apply in_app_or in H4. + (* These are all easy properties that need a bit of extra checking code inside of to_smt. *) + assert (tree_a_wf h11) by (now apply check_tree_a_wf_correct). + assert (tree_f_wf h13) by (now apply check_tree_f_wf_correct). + assert (match_one h11 h8) by (now (eapply AHmatch_one_hash_value; eauto)). + assert (match_one h13 h9) by (now (eapply match_one_hash_value; eauto)). + inv H4. + - eapply fold_to_smt_l_correct; eauto. + (* This is a proof that all the variables will only be between -1 and 1, which are the only + values allowed by Zval anyways, so there can't be larger values in the context. *) + assert (forall y : Int63.int, + In y l0 -> + State.Lit.interp + (interp_state_var (interp_form_hatom t_i_empty la_arr a) + (interp_form_hatom_bv t_i_empty la_arr a) f) y = + true). + { intros. eapply fold_init_correct with (form_defs := h7) (atom_defs := h6). + auto. auto. eassumption. eassumption. auto. auto. eauto. + eapply to_smt_l_fold_match_one; eauto. eapply to_smt_l_fold_match_one2; eauto. + eapply Heqp5. inversion 1. auto. lia. lia. + } auto. + - inv H13; [|inv H4]. eapply interp_eval_pred with (h6 := h6) (h11 := h11) (h13 := h13); eauto; + try reflexivity; eapply to_smt_l_fold_match_one2; eauto. +Qed. + +#[local] Transparent Z.sub. + + Lemma upd_asgn_get : + forall l y x a, + (upd_asgn l a) ! y = None -> + (upd_asgn (l ++ (y, x) :: nil) a) ! y = eval_hash_pred x (upd_asgn l a). + Proof. + intros. + unfold upd_asgn; rewrite fold_left_app; cbn [fold_left]. + cbn [fst snd]; destruct_match; auto; apply PTree.gss. + Qed. + + Lemma list_app_cons : + forall A l1 l2 (a: A), + (l1 ++ a :: nil) ++ l2 = l1 ++ a :: l2. + Proof. induction l1; crush. Qed. + + Lemma upd_asgn_app : + forall l1 l2 a, + upd_asgn (l1 ++ l2) a = upd_asgn l2 (upd_asgn l1 a). + Proof. intros; unfold upd_asgn; now rewrite fold_left_app. Qed. + + Lemma upd_asgn_pred_In : + forall p a l1 a0, + ~ pred_In (fst a) p -> + eval_hash_pred p (upd_asgn (l1 ++ a :: nil) a0) = eval_hash_pred p (upd_asgn l1 a0). + Proof. + induction p; try solve [crush]. + + intros. simpl. rewrite upd_asgn_app. simpl. destruct_match; auto. + destruct (peq (fst a0) a); subst. + - exfalso; apply H. constructor. + - apply PTree.gso; auto. + + simplify. rewrite IHp2. rewrite IHp1; auto. + unfold not; intros; apply H. constructor; auto. + unfold not; intros; apply H. apply pred_In_Pand2; auto. + + simplify. rewrite IHp2. rewrite IHp1; auto. + unfold not; intros; apply H. constructor; auto. + unfold not; intros; apply H. apply pred_In_Por2; auto. + + simplify. rewrite IHp2. rewrite IHp1; auto. + unfold not; intros; apply H. constructor; auto. + unfold not; intros; apply H. apply pred_In_Pimp2; auto. + + simplify. rewrite IHp; auto. + unfold not; intros; apply H. constructor; auto. + Qed. + + Lemma upd_asgn_app1 : + forall l2 l1 p a, + Forall (fun x => ~ pred_In (fst x) p) l2 -> + eval_hash_pred p (upd_asgn (l1 ++ l2) a) = eval_hash_pred p (upd_asgn l1 a). + Proof. + induction l2. + - intros. now rewrite app_nil_r. + - intros. rewrite <- list_app_cons. inv H. + rewrite IHl2; auto. + rewrite upd_asgn_pred_In; auto. + Qed. + + Lemma eval_hash_pred_upd_asgn_set : + forall l2 p a a0 a0', + ~ pred_In a p -> + Forall (fun x => a <> (fst x) /\ ~ pred_In a (snd x)) l2 -> + (forall x, x <> a -> a0 ! x = a0' ! x) -> + eval_hash_pred p (upd_asgn l2 a0) = eval_hash_pred p (upd_asgn l2 a0'). + Proof. + induction l2; intros. + crush. eapply eval_hash_pred_except; eauto. + inversion H0 as [| ? ? [NEQ PRED] FRL ]; subst. + simpl. + assert (eval_hash_pred (snd a) a0' = eval_hash_pred (snd a) a1). + { eapply eval_hash_pred_except; eauto. intros. symmetry. eapply H1. auto. } + setoid_rewrite H2. + destruct_match; setoid_rewrite Heqo; eauto. + eapply IHl2; eauto; intros. + destruct (peq x (fst a)); subst. + now rewrite !PTree.gss. + rewrite !PTree.gso by auto; eauto. + Qed. + + Lemma eval_hash_pred_upd_asgn_set2 : + forall l2 p a a0 y, + ~ pred_In a p -> + Forall (fun x => a <> (fst x) /\ ~ pred_In a (snd x)) l2 -> + eval_hash_pred p (upd_asgn l2 (PTree.set a y a0)) + = eval_hash_pred p (upd_asgn l2 a0). + Proof. + intros; eapply eval_hash_pred_upd_asgn_set; eauto. + intros; now apply PTree.gso. + Qed. + + Lemma upd_asgn_app2 : + forall l1 l2 p a, + Forall (fun y => Forall (fun x => fst y <> fst x /\ ~ pred_In (fst y) (snd x)) l2) l1 -> + (forall x, In x l1 -> ~ pred_In (fst x) p) -> + eval_hash_pred p (upd_asgn (l1 ++ l2) a) = eval_hash_pred p (upd_asgn l2 a). + Proof. + induction l1; auto. + intros; simpl. + inv H. + rewrite IHl1; auto. + destruct_match; auto. + apply eval_hash_pred_upd_asgn_set2; auto. + Qed. + + Lemma sorted_rev : + forall A R l (b: A) a, + LocallySorted (Basics.flip R) (l ++ (b :: a :: nil)) -> + R a b. + Proof. + induction l; crush. + inv H. auto. inv H. + assert (forall A (a b: list A), nil = a ++ b -> a = nil /\ b = nil). + { induction a1; crush. } + apply H in H2. crush. + eapply IHl. rewrite <- H1. auto. + Qed. + + Lemma sorted_rev2 : + forall A R l (b: A) a, + LocallySorted (Basics.flip R) (l ++ b :: nil) -> + R a b -> + LocallySorted (Basics.flip R) (l ++ (b :: a :: nil)). + Proof. + induction l; crush. + constructor. constructor. auto. + inv H. + assert (forall A (a b: list A), nil = a ++ b -> a = nil /\ b = nil). + { induction a1; crush. } + apply H in H3. crush. + replace (a :: l ++ b :: a0 :: nil) with ((a :: l ++ b :: nil) ++ a0 :: nil). + rewrite <- H2. + replace ((a :: b0 :: l0) ++ a0 :: nil) with (a :: b0 :: (l0 ++ a0 :: nil)). + constructor; auto. + replace (b0 :: l0 ++ a0 :: nil) with ((b0 :: l0) ++ a0 :: nil). + rewrite H2. + replace ((l ++ b :: nil) ++ a0 :: nil) with (l ++ b :: a0 :: nil). + eapply IHl; eauto. rewrite <- H2. auto. + rewrite list_app_cons. auto. + rewrite app_comm_cons. auto. + rewrite app_comm_cons. auto. + rewrite <- app_comm_cons. + rewrite list_app_cons. auto. + Qed. + + Lemma sorted_rev3 : + forall A R l, + Sorted.LocallySorted R l -> + Sorted.LocallySorted (Basics.flip R) (@rev A l). + Proof. + induction l; crush. inv H. constructor. + inv H. crush. constructor. + crush. + replace ((rev l0 ++ b :: nil) ++ a :: nil) with (rev l0 ++ b :: a :: nil). + eapply sorted_rev2; eauto. + now rewrite list_app_cons. + Qed. + + Lemma sorted_rev4 : + forall A R l, + Sorted.Sorted R l -> + Sorted.Sorted (Basics.flip R) (@rev A l). + Proof. + intros. apply Sorted_LocallySorted_iff. + apply sorted_rev3. apply Sorted_LocallySorted_iff. auto. + Qed. + + Lemma unnest_predicate_lt_fresh : + forall p p' fresh fresh' lp, + unnest_predicate fresh p = (p', lp, fresh') -> + (fresh <= fresh')%positive. + Proof. + induction p; crush; repeat (destruct_match; []); simplify; + try (pose proof (IHp1 _ _ _ _ Heqp); pose proof (IHp2 _ _ _ _ Heqp4); lia). + apply IHp in Heqp0; lia. + Qed. + + Lemma unnest_predicate_lt_in_list : + forall p p' fresh fresh' lp, + unnest_predicate fresh p = (p', lp, fresh') -> + Forall (fun x => (fresh <= fst x < fresh')%positive) lp. + Proof. + induction p; crush; repeat (destruct_match; []); simplify. + - pose proof (IHp1 _ _ _ _ Heqp). + pose proof (IHp2 _ _ _ _ Heqp4). + constructor; cbn. + + apply unnest_predicate_lt_fresh in Heqp4. + apply unnest_predicate_lt_fresh in Heqp. lia. + + apply Forall_app; split; apply Forall_forall; intros. + * eapply Forall_forall in H0; eauto. + apply unnest_predicate_lt_fresh in Heqp. lia. + * eapply Forall_forall in H; eauto. + apply unnest_predicate_lt_fresh in Heqp4. lia. + - pose proof (IHp1 _ _ _ _ Heqp). + pose proof (IHp2 _ _ _ _ Heqp4). + constructor; cbn. + + apply unnest_predicate_lt_fresh in Heqp4. + apply unnest_predicate_lt_fresh in Heqp. lia. + + apply Forall_app; split; apply Forall_forall; intros. + * eapply Forall_forall in H0; eauto. + apply unnest_predicate_lt_fresh in Heqp. lia. + * eapply Forall_forall in H; eauto. + apply unnest_predicate_lt_fresh in Heqp4. lia. + - pose proof (IHp1 _ _ _ _ Heqp). + pose proof (IHp2 _ _ _ _ Heqp4). + constructor; cbn. + + apply unnest_predicate_lt_fresh in Heqp4. + apply unnest_predicate_lt_fresh in Heqp. lia. + + apply Forall_app; split; apply Forall_forall; intros. + * eapply Forall_forall in H0; eauto. + apply unnest_predicate_lt_fresh in Heqp. lia. + * eapply Forall_forall in H; eauto. + apply unnest_predicate_lt_fresh in Heqp4. lia. + - pose proof (IHp _ _ _ _ Heqp0). + constructor; cbn. + + apply unnest_predicate_lt_fresh in Heqp0. lia. + + apply Forall_forall; intros. eapply Forall_forall in H; eauto. + lia. + Qed. + + Lemma unnest_predicate_lt_in_list5 : + forall p p' fresh fresh' lp, + unnest_predicate fresh p = (p', lp, fresh') -> + (max_hash_pred p < fresh)%positive -> + (max_hash_pred p' < fresh')%positive. + Proof. induction p; crush; repeat (destruct_match; []); crush. Qed. + + Lemma unnest_predicate_lt_in_list2 : + forall p p' fresh fresh' lp, + unnest_predicate fresh p = (p', lp, fresh') -> + (max_hash_pred p < fresh)%positive -> + Forall (fun x => (max_hash_pred (snd x) < fst x)%positive) lp. + Proof. + induction p; crush; repeat (destruct_match; []); simplify. + - pose proof (unnest_predicate_lt_fresh _ _ _ _ _ Heqp). + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ Heqp4). + pose proof (IHp1 _ _ _ _ Heqp ltac:(lia)) as X. + pose proof (IHp2 _ _ _ _ Heqp4 ltac:(lia)) as Y. + pose proof (unnest_predicate_lt_in_list5 _ _ _ _ _ Heqp ltac:(lia)). + pose proof (unnest_predicate_lt_in_list5 _ _ _ _ _ Heqp4 ltac:(lia)). + constructor; cbn; [lia|]. + apply Forall_app; split; eapply Forall_forall; intros. + + eapply Forall_forall in Y; eauto. + + eapply Forall_forall in X; eauto. + - pose proof (unnest_predicate_lt_fresh _ _ _ _ _ Heqp). + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ Heqp4). + pose proof (IHp1 _ _ _ _ Heqp ltac:(lia)) as X. + pose proof (IHp2 _ _ _ _ Heqp4 ltac:(lia)) as Y. + pose proof (unnest_predicate_lt_in_list5 _ _ _ _ _ Heqp ltac:(lia)). + pose proof (unnest_predicate_lt_in_list5 _ _ _ _ _ Heqp4 ltac:(lia)). + constructor; cbn; [lia|]. + apply Forall_app; split; eapply Forall_forall; intros. + + eapply Forall_forall in Y; eauto. + + eapply Forall_forall in X; eauto. + - pose proof (unnest_predicate_lt_fresh _ _ _ _ _ Heqp). + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ Heqp4). + pose proof (IHp1 _ _ _ _ Heqp ltac:(lia)) as X. + pose proof (IHp2 _ _ _ _ Heqp4 ltac:(lia)) as Y. + pose proof (unnest_predicate_lt_in_list5 _ _ _ _ _ Heqp ltac:(lia)). + pose proof (unnest_predicate_lt_in_list5 _ _ _ _ _ Heqp4 ltac:(lia)). + constructor; cbn; [lia|]. + apply Forall_app; split; eapply Forall_forall; intros. + + eapply Forall_forall in Y; eauto. + + eapply Forall_forall in X; eauto. + - pose proof (unnest_predicate_lt_fresh _ _ _ _ _ Heqp0). + pose proof (IHp _ _ _ _ Heqp0 ltac:(lia)) as X. + pose proof (unnest_predicate_lt_in_list5 _ _ _ _ _ Heqp0 ltac:(lia)). + constructor; cbn; [lia|auto]. + Qed. + + Lemma unnest_predicate_lt_in_list4 : + forall p p' fresh fresh' lp y, + unnest_predicate fresh p = (p', lp, fresh') -> + pred_In y p' -> + In y (map fst lp) \/ pred_In y p. + Proof. + induction p; crush; repeat (destruct_match; []); simplify. + - inv H0. tauto. + - inv H0. tauto. + - inv H0. tauto. + - inv H0. tauto. + Qed. + + Lemma unnest_predicate_lt_in_list3 : + forall p p' fresh fresh' lp, + unnest_predicate fresh p = (p', lp, fresh') -> + Forall (fun x => forall y, pred_In y (snd x) -> In y (map fst lp) \/ pred_In y p) lp. + Proof. + induction p; crush; repeat (destruct_match; []); simplify. + - pose proof (IHp1 _ _ _ _ Heqp). + pose proof (IHp2 _ _ _ _ Heqp4). + constructor; intros; cbn [snd] in *. + + inv H1. + * pose proof (unnest_predicate_lt_in_list4 _ _ _ _ _ _ Heqp H4). + inv H1. left. right. rewrite list_append_map. rewrite in_app. tauto. + right. now apply pred_In_Pand1. + * pose proof (unnest_predicate_lt_in_list4 _ _ _ _ _ _ Heqp4 H4). + inv H1. left. right. rewrite list_append_map. rewrite in_app. tauto. + right. now apply pred_In_Pand2. + + apply Forall_app; split. + * apply Forall_forall; intros. eapply Forall_forall in H0; eauto. + rewrite map_app. apply H0 in H2. inv H2. + left. right. apply in_app; tauto. + right. now apply pred_In_Pand2. + * apply Forall_forall; intros. eapply Forall_forall in H; eauto. + rewrite map_app. apply H in H2. inv H2. + left. right. apply in_app; tauto. + right. now apply pred_In_Pand1. + - pose proof (IHp1 _ _ _ _ Heqp). + pose proof (IHp2 _ _ _ _ Heqp4). + constructor; intros; cbn [snd] in *. + + inv H1. + * pose proof (unnest_predicate_lt_in_list4 _ _ _ _ _ _ Heqp H4). + inv H1. left. right. rewrite list_append_map. rewrite in_app. tauto. + right. now apply pred_In_Por1. + * pose proof (unnest_predicate_lt_in_list4 _ _ _ _ _ _ Heqp4 H4). + inv H1. left. right. rewrite list_append_map. rewrite in_app. tauto. + right. now apply pred_In_Por2. + + apply Forall_app; split. + * apply Forall_forall; intros. eapply Forall_forall in H0; eauto. + rewrite map_app. apply H0 in H2. inv H2. + left. right. apply in_app; tauto. + right. now apply pred_In_Por2. + * apply Forall_forall; intros. eapply Forall_forall in H; eauto. + rewrite map_app. apply H in H2. inv H2. + left. right. apply in_app; tauto. + right. now apply pred_In_Por1. + - pose proof (IHp1 _ _ _ _ Heqp). + pose proof (IHp2 _ _ _ _ Heqp4). + constructor; intros; cbn [snd] in *. + + inv H1. + * pose proof (unnest_predicate_lt_in_list4 _ _ _ _ _ _ Heqp H4). + inv H1. left. right. rewrite list_append_map. rewrite in_app. tauto. + right. now apply pred_In_Pimp1. + * pose proof (unnest_predicate_lt_in_list4 _ _ _ _ _ _ Heqp4 H4). + inv H1. left. right. rewrite list_append_map. rewrite in_app. tauto. + right. now apply pred_In_Pimp2. + + apply Forall_app; split. + * apply Forall_forall; intros. eapply Forall_forall in H0; eauto. + rewrite map_app. apply H0 in H2. inv H2. + left. right. apply in_app; tauto. + right. now apply pred_In_Pimp2. + * apply Forall_forall; intros. eapply Forall_forall in H; eauto. + rewrite map_app. apply H in H2. inv H2. + left. right. apply in_app; tauto. + right. now apply pred_In_Pimp1. + - pose proof (IHp _ _ _ _ Heqp0). + constructor; intros; cbn [snd] in *. + + inv H0. + pose proof (unnest_predicate_lt_in_list4 _ _ _ _ _ _ Heqp0 H3). + inv H0. tauto. + right. now apply pred_In_Pnot. + + apply Forall_forall; intros. eapply Forall_forall in H0; eauto. + apply H0 in H1. inv H1; [tauto|]. + right. now apply pred_In_Pnot. + Qed. + + Lemma unnest_predicate_sorted : + forall p p' lp fresh' fresh, + unnest_predicate fresh p = (p', lp, fresh') -> + Sorted.Sorted (Basics.flip pl_lt) lp. + Proof. + induction p; crush; try apply Sorted_nil; + repeat (destruct_match; []); simplify. + - constructor. + eapply SetoidList.SortA_app with (eqA := eq); auto. + eapply IHp2; eassumption. + eapply IHp1; eassumption. + intros. apply SetoidList.InA_alt in H; simplify. + apply SetoidList.InA_alt in H0; simplify. + unfold Basics.flip. + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ Heqp). + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ Heqp4). + eapply Forall_forall in H; [|eassumption]. + eapply Forall_forall in H0; [|eassumption]. + destruct x, x0; unfold pl_lt; cbn [snd fst] in *; lia. + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ Heqp). + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ Heqp4). + apply SetoidList.InfA_app. + + apply SetoidList.InA_InfA with (eqA := eq); auto; intros. + apply SetoidList.InA_alt in H1; simplify. + eapply Forall_forall in H0; [|eassumption]. + unfold Basics.flip, pl_lt; destruct x; cbn [snd fst] in *. lia. + + apply SetoidList.InA_InfA with (eqA := eq); auto; intros. + apply SetoidList.InA_alt in H1; simplify. + eapply Forall_forall in H; [|eassumption]. + unfold Basics.flip, pl_lt; destruct x; cbn [snd fst] in *. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ Heqp4). lia. + - constructor. + eapply SetoidList.SortA_app with (eqA := eq); auto. + eapply IHp2; eassumption. + eapply IHp1; eassumption. + intros. apply SetoidList.InA_alt in H; simplify. + apply SetoidList.InA_alt in H0; simplify. + unfold Basics.flip. + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ Heqp). + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ Heqp4). + eapply Forall_forall in H; [|eassumption]. + eapply Forall_forall in H0; [|eassumption]. + destruct x, x0; unfold pl_lt; cbn [snd fst] in *; lia. + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ Heqp). + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ Heqp4). + apply SetoidList.InfA_app. + + apply SetoidList.InA_InfA with (eqA := eq); auto; intros. + apply SetoidList.InA_alt in H1; simplify. + eapply Forall_forall in H0; [|eassumption]. + unfold Basics.flip, pl_lt; destruct x; cbn [snd fst] in *. lia. + + apply SetoidList.InA_InfA with (eqA := eq); auto; intros. + apply SetoidList.InA_alt in H1; simplify. + eapply Forall_forall in H; [|eassumption]. + unfold Basics.flip, pl_lt; destruct x; cbn [snd fst] in *. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ Heqp4). lia. + - constructor. + eapply SetoidList.SortA_app with (eqA := eq); auto. + eapply IHp2; eassumption. + eapply IHp1; eassumption. + intros. apply SetoidList.InA_alt in H; simplify. + apply SetoidList.InA_alt in H0; simplify. + unfold Basics.flip. + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ Heqp). + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ Heqp4). + eapply Forall_forall in H; [|eassumption]. + eapply Forall_forall in H0; [|eassumption]. + destruct x, x0; unfold pl_lt; cbn [snd fst] in *; lia. + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ Heqp). + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ Heqp4). + apply SetoidList.InfA_app. + + apply SetoidList.InA_InfA with (eqA := eq); auto; intros. + apply SetoidList.InA_alt in H1; simplify. + eapply Forall_forall in H0; [|eassumption]. + unfold Basics.flip, pl_lt; destruct x; cbn [snd fst] in *. lia. + + apply SetoidList.InA_InfA with (eqA := eq); auto; intros. + apply SetoidList.InA_alt in H1; simplify. + eapply Forall_forall in H; [|eassumption]. + unfold Basics.flip, pl_lt; destruct x; cbn [snd fst] in *. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ Heqp4). lia. + - constructor. + eapply IHp; eassumption. + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ Heqp0). + apply SetoidList.InA_InfA with (eqA := eq); auto; intros. + apply SetoidList.InA_alt in H0; simplify. + eapply Forall_forall in H; [|eassumption]. + unfold Basics.flip, pl_lt; destruct x; cbn [snd fst] in *. lia. + Qed. + + Lemma to_equiv_rev : + forall l, rev (to_equiv l) = to_equiv (rev l). + Proof. unfold to_equiv; intros. rewrite map_rev. auto. Qed. + + Lemma unnest_predicate_sorted_app : + forall p1 p l p0 p3 p4 l0 fresh p2, + unnest_predicate fresh p1 = (p, l, p0) -> + unnest_predicate p0 p2 = (p3, l0, p4) -> + Sorted.Sorted (Basics.flip pl_lt) l -> + Sorted.Sorted (Basics.flip pl_lt) l0 -> + Sorted.Sorted (Basics.flip pl_lt) (l0 ++ l). + Proof. + intros * NEST1 NEST2 SORT1 SORT2. + apply SetoidList.SortA_app with (eqA := eq); auto. + intros. + apply SetoidList.InA_alt in H. + apply SetoidList.InA_alt in H0. simplify. + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ NEST1). + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ NEST2). + eapply Forall_forall in H; [|eassumption]. + eapply Forall_forall in H1; [|eassumption]. + unfold Basics.flip, pl_lt. lia. + Qed. + + Lemma unnest_predicate_equiv_list : + forall p p0 p1 p2 p3 p4 l l0 fresh, + unnest_predicate fresh p1 = (p, l, p0) -> + unnest_predicate p0 p2 = (p3, l0, p4) -> + (Pos.max (max_hash_pred p1) (max_hash_pred p2) < fresh)%positive -> + equiv_list_wf (rev (l0 ++ l)). + Proof. + intros * NEST1 NEST2 MAX. + unfold equiv_list_wf. split. unfold Sorted. + pose proof (unnest_predicate_sorted _ _ _ _ _ NEST1). + pose proof (unnest_predicate_sorted _ _ _ _ _ NEST2). + replace pl_lt with (Basics.flip (Basics.flip (@pl_lt predicate))) by (unfold Basics.flip; auto). + apply sorted_rev4. + eapply unnest_predicate_sorted_app; eauto. + apply Forall_rev; apply Forall_app; split; + eapply unnest_predicate_lt_in_list2; eauto. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST1). + lia. lia. + Qed. + + Lemma pred_in_fold : + forall A l p y, + @pred_In A p (fold_left Pand l y) -> + (exists i, In i l /\ pred_In p i) \/ pred_In p y. + Proof. + induction l. + intros. simplify. tauto. + intros. simpl in *. apply IHl in H. + inv H; simplify. left. econstructor. + constructor. right. eauto. auto. + inv H0; try tauto. + do 2 econstructor; split; try eassumption; tauto. + Qed. + + Lemma max_hash_pred_lt : + forall p l, + pred_In l p -> + (l <= max_hash_pred p)%positive. + Proof. + induction p; intros; try inv H; crush; + try (apply IHp1 in H2; lia); + apply IHp2 in H2; lia. + Qed. + + Lemma unnest_predicate_not_in : + forall p p0 p1 p2 p3 p4 l l0 fresh y, + unnest_predicate fresh p1 = (p, l, p0) -> + unnest_predicate p0 p2 = (p3, l0, p4) -> + (Pos.max (max_hash_pred p1) (max_hash_pred p2) < fresh)%positive -> + Forall (fun x : positive * hash_pred => + ~ pred_In (fst x) (fold_left Pand (to_equiv (l0 ++ l)) T)) + ((p4, y) :: nil). + Proof. + intros * NEST1 NEST2 MAX. + constructor; [|constructor]. cbn [fst]. unfold not; intros. + apply pred_in_fold in H. inv H; inv H0; + simplify. unfold to_equiv in H0. apply map_in_some in H0. simplify. + apply in_app_or in H0; inv H0. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST1) as TEMP. + pose proof (unnest_predicate_lt_in_list2 _ _ _ _ _ NEST2 ltac:(lia)). + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ NEST2). + eapply Forall_forall in H0; [|eassumption]. + eapply Forall_forall in H2; [|eassumption]. destruct x0. cbn [snd fst] in *. + apply max_hash_pred_lt in H1. unfold equiv in H1. + unfold max_hash_pred in H1. fold max_hash_pred in H1. lia. + pose proof (unnest_predicate_lt_in_list2 _ _ _ _ _ NEST1 ltac:(lia)). + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ NEST1). + eapply Forall_forall in H0; [|eassumption]. + eapply Forall_forall in H2; [|eassumption]. destruct x0. cbn [snd fst] in *. + apply max_hash_pred_lt in H1. unfold equiv in H1. + unfold max_hash_pred in H1. fold max_hash_pred in H1. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST2). lia. + Qed. + + Lemma unnest_predicate_not_in4 : + forall p p0 p1 l fresh y, + unnest_predicate fresh p1 = (p, l, p0) -> + (max_hash_pred p1 < fresh)%positive -> + Forall (fun x : positive * hash_pred => + ~ pred_In (fst x) (fold_left Pand (to_equiv l) T)) + ((p0, y) :: nil). + Proof. + intros * NEST1 MAX. + constructor; [|constructor]. cbn [fst]. unfold not; intros. + apply pred_in_fold in H. inv H; inv H0; + simplify. unfold to_equiv in H0. apply map_in_some in H0. simplify. + pose proof (unnest_predicate_lt_in_list2 _ _ _ _ _ NEST1 ltac:(lia)). + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ NEST1). + eapply Forall_forall in H; [|eassumption]. + eapply Forall_forall in H2; [|eassumption]. destruct x0. cbn [snd fst] in *. + apply max_hash_pred_lt in H1. unfold equiv in H1. + unfold max_hash_pred in H1. fold max_hash_pred in H1. lia. + Qed. + + Lemma unnest_predicate_in_pred : + forall fresh p1 p l p0, + unnest_predicate fresh p1 = (p, l, p0) -> + (max_hash_pred p1 < fresh)%positive -> + ~ pred_In p0 p. + Proof. + destruct p1; unfold not; intros. + - inv H. inv H1. + - inv H. inv H1. + - inv H. inv H1. + - inv H. crush. inv H1. lia. + - simplify. repeat (destruct_match; []). simplify. + inv H1. lia. + - simplify. repeat (destruct_match; []). simplify. + inv H1. lia. + - simplify. repeat (destruct_match; []). simplify. + inv H1. lia. + - simplify. repeat (destruct_match; []). simplify. + inv H1. lia. + Qed. + + Lemma unnest_predicate_in_pred2 : + forall fresh p1 p l p0, + unnest_predicate fresh p1 = (p, l, p0) -> + (max_hash_pred p1 < fresh)%positive -> + (max_hash_pred p < p0)%positive. + Proof. + intros. + destruct p1; crush. + - repeat (destruct_match; []); simplify; lia. + - repeat (destruct_match; []); simplify; lia. + - repeat (destruct_match; []); simplify; lia. + - repeat (destruct_match; []); simplify; lia. + Qed. + + Lemma unnest_predicate_forall_none : + forall p1 p2 p0 p3 l0 l p4 fresh p a, + unnest_predicate fresh p1 = (p, l, p0) -> + unnest_predicate p0 p2 = (p3, l0, p4) -> + eval_hash_pred p1 a = eval_pred_list (p, l) (upd_asgn (rev l) a) -> + eval_hash_pred p2 a = eval_pred_list (p3, l0) (upd_asgn (rev l0) a) -> + (forall y : positive, (fresh <= y)%positive -> a ! y = None) -> + (max_hash_pred (p1 ∧ p2) < fresh)%positive -> + Forall (fun x : positive * predicate => a ! (fst x) = None) (rev (l0 ++ l)). + Proof. + intros * NEST1 NEST2 EVAL1 EVAL2 GTMAX MAXHASH. + apply Forall_forall; intros. apply GTMAX. + unfold max_hash_pred in *; fold max_hash_pred in *. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST2) as X3. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST1) as X4. + pose proof (unnest_predicate_lt_in_list2 _ _ _ _ _ NEST2 ltac:(lia)) as X1. + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ NEST2) as X2. + pose proof (unnest_predicate_lt_in_list2 _ _ _ _ _ NEST1 ltac:(lia)) as Y1. + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ NEST1) as Y2. + apply in_rev in H. apply in_app in H. inv H. + eapply Forall_forall in X1; eauto; []. + eapply Forall_forall in X2; eauto; []. lia. + eapply Forall_forall in Y1; eauto; []. + eapply Forall_forall in Y2; eauto; []. lia. + Qed. + + Lemma unnest_predicate_forall_gt1 : + forall p1 p2 p0 p3 l0 l p4 fresh p a, + unnest_predicate fresh p1 = (p, l, p0) -> + unnest_predicate p0 p2 = (p3, l0, p4) -> + eval_hash_pred p1 a = eval_pred_list (p, l) (upd_asgn (rev l) a) -> + eval_hash_pred p2 a = eval_pred_list (p3, l0) (upd_asgn (rev l0) a) -> + (forall y : positive, (fresh <= y)%positive -> a ! y = None) -> + (max_hash_pred (p1 ∧ p2) < fresh)%positive -> + Forall (fun x : positive * predicate => (1 < fst x)%positive) (rev (l0 ++ l)). + Proof. + intros * NEST1 NEST2 EVAL1 EVAL2 GTMAX MAXHASH. + apply Forall_forall; intros. + unfold max_hash_pred in *; fold max_hash_pred in *. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST2) as X3. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST1) as X4. + pose proof (unnest_predicate_lt_in_list2 _ _ _ _ _ NEST2 ltac:(lia)) as X1. + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ NEST2) as X2. + pose proof (unnest_predicate_lt_in_list2 _ _ _ _ _ NEST1 ltac:(lia)) as Y1. + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ NEST1) as Y2. + apply in_rev in H. apply in_app in H. inv H. + eapply Forall_forall in X1; eauto; []. + eapply Forall_forall in X2; eauto; []. lia. + eapply Forall_forall in Y1; eauto; []. + eapply Forall_forall in Y2; eauto; []. lia. + Qed. + + Lemma unnest_predicate_not_in3 : + forall p1 p0 l fresh p a, + unnest_predicate fresh p1 = (p, l, p0) -> + eval_hash_pred p1 a = eval_pred_list (p, l) (upd_asgn (rev l) a) -> + (forall y : positive, (fresh <= y)%positive -> a ! y = None) -> + (max_hash_pred p1 < fresh)%positive -> + ~ In p0 (map fst (rev l)). + Proof. + intros * NEST1 EVAL1 GTMAX MAXHASH. + unfold not; intros. + pose proof (unnest_predicate_lt_in_list2 _ _ _ _ _ NEST1). + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ NEST1). + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST1). + apply map_in_some in H. simplify. + eapply in_rev in H. + eapply Forall_forall in H0; eauto. + eapply Forall_forall in H1; eauto. + destruct x; cbn [fst snd max_hash_pred] in *. + lia. + Qed. + + Lemma unnest_predicate_not_in2 : + forall p1 p2 p0 p3 l0 l p4 fresh p a, + unnest_predicate fresh p1 = (p, l, p0) -> + unnest_predicate p0 p2 = (p3, l0, p4) -> + eval_hash_pred p1 a = eval_pred_list (p, l) (upd_asgn (rev l) a) -> + eval_hash_pred p2 a = eval_pred_list (p3, l0) (upd_asgn (rev l0) a) -> + (forall y : positive, (fresh <= y)%positive -> a ! y = None) -> + (max_hash_pred (p1 ∧ p2) < fresh)%positive -> + ~ In p4 (map fst (rev l ++ rev l0)). + Proof. + intros * NEST1 NEST2 EVAL1 EVAL2 GTMAX MAXHASH. + unfold not; intros. + + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST1) as TMP. cbn [max_hash_pred] in *. + pose proof (unnest_predicate_lt_in_list2 _ _ _ _ _ NEST2 ltac:(lia)). + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ NEST2). + pose proof (unnest_predicate_lt_in_list2 _ _ _ _ _ NEST1 ltac:(lia)). + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ NEST1). + apply map_in_some in H. simplify. + apply in_app_or in H. inv H. + eapply in_rev in H4. + eapply Forall_forall in H2; eauto. + eapply Forall_forall in H3; eauto. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST2). + destruct x; cbn [fst snd] in *. lia. + eapply in_rev in H4. + eapply Forall_forall in H0; eauto. + eapply Forall_forall in H1; eauto. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST1). + destruct x; cbn [fst snd] in *. lia. + Qed. + + Lemma eval_equiv_added : + forall p1 p2 p0 p3 l0 l p4 fresh p a, + unnest_predicate fresh p1 = (p, l, p0) -> + unnest_predicate p0 p2 = (p3, l0, p4) -> + eval_hash_pred p1 a = eval_pred_list (p, l) (upd_asgn (rev l) a) -> + eval_hash_pred p2 a = eval_pred_list (p3, l0) (upd_asgn (rev l0) a) -> + (forall y : positive, (fresh <= y)%positive -> a ! y = None) -> + (max_hash_pred (p1 ∧ p2) < fresh)%positive -> + eval_hash_pred (equiv (Pbase p4) (p ∧ p3)) (upd_asgn (rev l ++ rev l0 ++ (p4, p ∧ p3) :: nil) a) = Some true. + Proof. + intros * NEST1 NEST2 EVAL1 EVAL2 GTMAX MAXHASH. + apply eval_equiv2. rewrite eval_hash_pred_get. + rewrite app_assoc. + rewrite upd_asgn_get. + symmetry. rewrite upd_asgn_app1; auto. + constructor; [|constructor]; simpl. + pose proof NEST1 as X1. pose proof NEST2 as X2. + eapply unnest_predicate_in_pred2 in X1. + eapply unnest_predicate_in_pred2 in X2. + + 2: { unfold max_hash_pred in *; fold max_hash_pred in *. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST1). + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST2). lia. + } + + 2: { unfold max_hash_pred in *; fold max_hash_pred in *. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST1). + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST2). lia. + } + + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST1). + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST2). + unfold not; intros. apply max_hash_pred_lt in H1. + cbn [max_hash_pred] in H1. lia. + + rewrite upd_asgn_not_in. eapply GTMAX. unfold max_hash_pred in *. fold max_hash_pred in *. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST1). + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST2). lia. + eapply unnest_predicate_not_in2; eauto. + Qed. + + Lemma eval_equiv_added_Por : + forall p1 p2 p0 p3 l0 l p4 fresh p a, + unnest_predicate fresh p1 = (p, l, p0) -> + unnest_predicate p0 p2 = (p3, l0, p4) -> + eval_hash_pred p1 a = eval_pred_list (p, l) (upd_asgn (rev l) a) -> + eval_hash_pred p2 a = eval_pred_list (p3, l0) (upd_asgn (rev l0) a) -> + (forall y : positive, (fresh <= y)%positive -> a ! y = None) -> + (max_hash_pred (p1 ∨ p2) < fresh)%positive -> + eval_hash_pred (equiv (Pbase p4) (p ∨ p3)) (upd_asgn (rev l ++ rev l0 ++ (p4, p ∨ p3) :: nil) a) = Some true. + Proof. + intros * NEST1 NEST2 EVAL1 EVAL2 GTMAX MAXHASH. + apply eval_equiv2. rewrite eval_hash_pred_get. + rewrite app_assoc. + rewrite upd_asgn_get. + symmetry. rewrite upd_asgn_app1; auto. + constructor; [|constructor]; simpl. + pose proof NEST1 as X1. pose proof NEST2 as X2. + eapply unnest_predicate_in_pred2 in X1. + eapply unnest_predicate_in_pred2 in X2. + + 2: { unfold max_hash_pred in *; fold max_hash_pred in *. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST1). + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST2). lia. + } + + 2: { unfold max_hash_pred in *; fold max_hash_pred in *. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST1). + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST2). lia. + } + + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST1). + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST2). + unfold not; intros. apply max_hash_pred_lt in H1. + cbn [max_hash_pred] in H1. lia. + + rewrite upd_asgn_not_in. eapply GTMAX. unfold max_hash_pred in *. fold max_hash_pred in *. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST1). + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST2). lia. + eapply unnest_predicate_not_in2; eauto. + Qed. + + Lemma eval_equiv_added_Pimp : + forall p1 p2 p0 p3 l0 l p4 fresh p a, + unnest_predicate fresh p1 = (p, l, p0) -> + unnest_predicate p0 p2 = (p3, l0, p4) -> + eval_hash_pred p1 a = eval_pred_list (p, l) (upd_asgn (rev l) a) -> + eval_hash_pred p2 a = eval_pred_list (p3, l0) (upd_asgn (rev l0) a) -> + (forall y : positive, (fresh <= y)%positive -> a ! y = None) -> + (max_hash_pred (p1 → p2) < fresh)%positive -> + eval_hash_pred (equiv (Pbase p4) (p → p3)) (upd_asgn (rev l ++ rev l0 ++ (p4, p → p3) :: nil) a) = Some true. + Proof. + intros * NEST1 NEST2 EVAL1 EVAL2 GTMAX MAXHASH. + apply eval_equiv2. rewrite eval_hash_pred_get. + rewrite app_assoc. + rewrite upd_asgn_get. + symmetry. rewrite upd_asgn_app1; auto. + constructor; [|constructor]; simpl. + pose proof NEST1 as X1. pose proof NEST2 as X2. + eapply unnest_predicate_in_pred2 in X1. + eapply unnest_predicate_in_pred2 in X2. + + 2: { unfold max_hash_pred in *; fold max_hash_pred in *. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST1). + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST2). lia. + } + + 2: { unfold max_hash_pred in *; fold max_hash_pred in *. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST1). + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST2). lia. + } + + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST1). + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST2). + unfold not; intros. apply max_hash_pred_lt in H1. + cbn [max_hash_pred] in H1. lia. + + rewrite upd_asgn_not_in. eapply GTMAX. unfold max_hash_pred in *. fold max_hash_pred in *. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST1). + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST2). lia. + eapply unnest_predicate_not_in2; eauto. + Qed. + + Lemma unnest_predicate_correct_Pand : + forall p1 p2 p0 p3 l0 l p4 fresh p a, + unnest_predicate fresh p1 = (p, l, p0) -> + unnest_predicate p0 p2 = (p3, l0, p4) -> + eval_hash_pred p1 a = eval_pred_list (p, l) (upd_asgn (rev l) a) -> + eval_hash_pred p2 a = eval_pred_list (p3, l0) (upd_asgn (rev l0) a) -> + (forall y, (fresh <= y)%positive -> a ! y = None) -> + (max_hash_pred (p1 ∧ p2) < fresh)%positive -> + eval_hash_pred (p1 ∧ p2) a = + eval_hash_pred (Pbase p4 ∧ fold_left Pand (to_equiv (l0 ++ l)) (T ∧ equiv (Pbase p4) (p ∧ p3))) + (upd_asgn (rev (l0 ++ l) ++ (p4, p ∧ p3) :: nil) a). + Proof. + intros * NEST1 NEST2 EVAL1 EVAL2 GTMAX MAXHASH. + assert (eval_hash_pred (fold_left Pand (to_equiv (l0 ++ l)) (T ∧ equiv (Pbase p4) (p ∧ p3))) + (upd_asgn (rev l ++ rev l0 ++ (p4, p ∧ p3) :: nil) a) = Some true). + { apply eval_hash_pred_fold_Pand2 with (x := T). + rewrite app_assoc. + rewrite upd_asgn_app1; [|eapply unnest_predicate_not_in; eauto]. + replace (rev l ++ rev l0) with (rev (l0 ++ l)) by (rewrite rev_app_distr; auto). + apply fold_left_Pand_rev. + rewrite to_equiv_rev. + apply eval_hash_pred_equiv_asgn; auto. + eapply unnest_predicate_equiv_list; eauto. + eapply unnest_predicate_forall_none; eauto. + eapply unnest_predicate_forall_gt1; eauto. + rewrite eval_hash_pred_T_Pand. + eapply eval_equiv_added; eauto. + } + assert (eval_hash_pred (p1 ∧ p2) a = eval_hash_pred (Pbase p4) + (upd_asgn ((rev l ++ rev l0) ++ (p4, p ∧ p3) :: nil) a)). + { rewrite eval_hash_pred_get. + rewrite upd_asgn_get. + 2: { rewrite upd_asgn_not_in by (eapply unnest_predicate_not_in2; eauto). + eapply GTMAX. unfold max_hash_pred in *. fold max_hash_pred in *. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST1). + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST2). lia. + } + apply eval_hash_pred_pand. + { rewrite EVAL1. unfold eval_pred_list. cbn [snd fst]. + rewrite upd_asgn_app1. + { rewrite eval_hash_pred_T_Pand2; auto. + apply fold_left_Pand_rev. rewrite to_equiv_rev. + apply eval_hash_pred_equiv_asgn; auto. + { unfold equiv_list_wf, Sorted; split. + { replace pl_lt with (Basics.flip (Basics.flip (@pl_lt predicate))) by auto. + apply sorted_rev4. eapply unnest_predicate_sorted; eauto. } + { apply Forall_rev. eapply unnest_predicate_lt_in_list2; eauto. cbn [max_hash_pred] in *; lia. } } + { exploit unnest_predicate_forall_none. apply NEST1. apply NEST2. + all: eauto. intros. cbn. rewrite rev_app_distr in H0. + apply Forall_app in H0; crush. } + { exploit unnest_predicate_forall_gt1. apply NEST1. apply NEST2. + all: eauto. intros. cbn. rewrite rev_app_distr in H0. + apply Forall_app in H0; crush. } } + { apply Forall_rev. apply Forall_forall; intros. + cbn [max_hash_pred] in *. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST1) as TMP. + pose proof (unnest_predicate_lt_in_list2 _ _ _ _ _ NEST2 ltac:(lia)). + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ NEST2). + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST2). + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST1). + eapply Forall_forall in H1; eauto. eapply Forall_forall in H2; eauto. + unfold not; intros. apply max_hash_pred_lt in H5. destruct x; cbn [fst snd] in *. + unfold max_hash_pred in *; fold max_hash_pred in *. + pose proof (unnest_predicate_in_pred2 _ _ _ _ _ NEST1). + pose proof (unnest_predicate_in_pred2 _ _ _ _ _ NEST2). + lia. + } } + { rewrite EVAL2. unfold eval_pred_list. cbn [snd fst]. + rewrite upd_asgn_app2. + { rewrite eval_hash_pred_T_Pand2; auto. + apply fold_left_Pand_rev. rewrite to_equiv_rev. + apply eval_hash_pred_equiv_asgn; auto. + { unfold equiv_list_wf, Sorted; split. + { replace pl_lt with (Basics.flip (Basics.flip (@pl_lt predicate))) by auto. + apply sorted_rev4. eapply unnest_predicate_sorted; eauto. } + { apply Forall_rev. eapply unnest_predicate_lt_in_list2; eauto. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST1). + cbn [max_hash_pred] in *; lia. } } + { exploit unnest_predicate_forall_none. apply NEST1. apply NEST2. + all: eauto. intros. cbn. rewrite rev_app_distr in H0. + apply Forall_app in H0; crush. } + { exploit unnest_predicate_forall_gt1. apply NEST1. apply NEST2. + all: eauto. intros. cbn. rewrite rev_app_distr in H0. + apply Forall_app in H0; crush. } } + { apply Forall_rev. apply Forall_forall; intros. + apply Forall_rev. apply Forall_forall; intros. split. + cbn [max_hash_pred] in *. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST1) as TMP. + pose proof (unnest_predicate_lt_in_list2 _ _ _ _ _ NEST2 ltac:(lia)) as X2. + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ NEST2) as Y2. + pose proof (unnest_predicate_lt_in_list2 _ _ _ _ _ NEST1 ltac:(lia)) as X1. + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ NEST1) as Y1. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST2) as Z2. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST1) as Z1. + destruct x, x0. cbn [fst snd] in *. + eapply Forall_forall in X2; eauto. eapply Forall_forall in Y2; eauto. + eapply Forall_forall in X1; eauto. eapply Forall_forall in Y1; eauto. + cbn [fst snd] in *. lia. + + pose proof (unnest_predicate_lt_in_list3 _ _ _ _ _ NEST2) as X. + unfold not; intros Y. eapply Forall_forall in X; eauto. + cbn [max_hash_pred] in *. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST1) as TMP. + pose proof (unnest_predicate_lt_in_list2 _ _ _ _ _ NEST2 ltac:(lia)) as X2. + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ NEST2) as Y2. + pose proof (unnest_predicate_lt_in_list2 _ _ _ _ _ NEST1 ltac:(lia)) as X1. + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ NEST1) as Y1. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST2) as Z2. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST1) as Z1. + eapply Forall_forall in X2; eauto. eapply Forall_forall in Y2; eauto. + eapply Forall_forall in X1; eauto. eapply Forall_forall in Y1; eauto. + destruct x, x0. cbn [fst snd] in *. + unfold max_hash_pred in *; fold max_hash_pred in *. + exploit unnest_predicate_in_pred2; eauto. lia; intros. + exploit unnest_predicate_in_pred2. eapply NEST1. lia. intros. + pose proof Y as YY. apply X in YY. inv YY. + eapply map_in_some in H4; simplify. destruct x; simplify. + eapply max_hash_pred_lt in Y. + pose proof (unnest_predicate_lt_in_list2 _ _ _ _ _ NEST2) as X22. + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ NEST2) as Y22. + eapply Forall_forall in X22; eauto. eapply Forall_forall in Y22; eauto. + cbn [fst snd] in *. + cbn [max_hash_pred] in *. + lia. lia. + eapply max_hash_pred_lt in H4. lia. + } + { intros. apply in_rev in H0. destruct x; cbn [fst snd] in *. + unfold not; intros Y. + cbn [max_hash_pred] in *. + pose proof (unnest_predicate_lt_in_list2 _ _ _ _ _ NEST1 ltac:(lia)) as X1. + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ NEST1) as Y1. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST1) as Z1. + eapply Forall_forall in X1; eauto. eapply Forall_forall in Y1; eauto. + cbn [max_hash_pred fst snd] in *. + exploit unnest_predicate_in_pred2; eauto. lia; intros. intros. + exploit unnest_predicate_in_pred2. eapply NEST1. lia. intros. + exploit unnest_predicate_lt_in_list4. eapply NEST2. eauto. intros. inv H3. + eapply map_in_some in H4; simplify. + destruct x. + cbn [max_hash_pred fst snd] in *. + pose proof (unnest_predicate_lt_in_list2 _ _ _ _ _ NEST2 ltac:(lia)) as X2. + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ NEST2) as Y2. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST2) as Z2. + eapply Forall_forall in X2; eauto. eapply Forall_forall in Y2; eauto. + cbn [max_hash_pred fst snd] in *. + eapply max_hash_pred_lt in Y. lia. + eapply max_hash_pred_lt in H4. lia. + } } } + symmetry; rewrite eval_hash_pred_T_Pand2; rewrite rev_app_distr; + try solve [rewrite <- app_assoc; auto]; auto. + Qed. + + Lemma unnest_predicate_correct_Por : + forall p1 p2 p0 p3 l0 l p4 fresh p a, + unnest_predicate fresh p1 = (p, l, p0) -> + unnest_predicate p0 p2 = (p3, l0, p4) -> + eval_hash_pred p1 a = eval_pred_list (p, l) (upd_asgn (rev l) a) -> + eval_hash_pred p2 a = eval_pred_list (p3, l0) (upd_asgn (rev l0) a) -> + (forall y, (fresh <= y)%positive -> a ! y = None) -> + (max_hash_pred (p1 ∨ p2) < fresh)%positive -> + eval_hash_pred (p1 ∨ p2) a = + eval_hash_pred (Pbase p4 ∧ fold_left Pand (to_equiv (l0 ++ l)) (T ∧ equiv (Pbase p4) (p ∨ p3))) + (upd_asgn (rev (l0 ++ l) ++ (p4, p ∨ p3) :: nil) a). + Proof. + intros * NEST1 NEST2 EVAL1 EVAL2 GTMAX MAXHASH. + assert (eval_hash_pred (fold_left Pand (to_equiv (l0 ++ l)) (T ∧ equiv (Pbase p4) (p ∨ p3))) + (upd_asgn (rev l ++ rev l0 ++ (p4, p ∨ p3) :: nil) a) = Some true). + { apply eval_hash_pred_fold_Pand2 with (x := T). + rewrite app_assoc. + rewrite upd_asgn_app1; [|eapply unnest_predicate_not_in; eauto]. + replace (rev l ++ rev l0) with (rev (l0 ++ l)) by (rewrite rev_app_distr; auto). + apply fold_left_Pand_rev. + rewrite to_equiv_rev. + apply eval_hash_pred_equiv_asgn; auto. + eapply unnest_predicate_equiv_list; eauto. + eapply unnest_predicate_forall_none; eauto. + eapply unnest_predicate_forall_gt1; eauto. + rewrite eval_hash_pred_T_Pand. + eapply eval_equiv_added_Por; eauto. + } + assert (eval_hash_pred (p1 ∨ p2) a = eval_hash_pred (Pbase p4) + (upd_asgn ((rev l ++ rev l0) ++ (p4, p ∨ p3) :: nil) a)). + { rewrite eval_hash_pred_get. + rewrite upd_asgn_get. + 2: { rewrite upd_asgn_not_in by (eapply unnest_predicate_not_in2; eauto). + eapply GTMAX. unfold max_hash_pred in *. fold max_hash_pred in *. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST1). + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST2). lia. + } + apply eval_hash_pred_por. + { rewrite EVAL1. unfold eval_pred_list. cbn [snd fst]. + rewrite upd_asgn_app1. + { rewrite eval_hash_pred_T_Pand2; auto. + apply fold_left_Pand_rev. rewrite to_equiv_rev. + apply eval_hash_pred_equiv_asgn; auto. + { unfold equiv_list_wf, Sorted; split. + { replace pl_lt with (Basics.flip (Basics.flip (@pl_lt predicate))) by auto. + apply sorted_rev4. eapply unnest_predicate_sorted; eauto. } + { apply Forall_rev. eapply unnest_predicate_lt_in_list2; eauto. cbn [max_hash_pred] in *; lia. } } + { exploit unnest_predicate_forall_none. apply NEST1. apply NEST2. + all: eauto. intros. cbn. rewrite rev_app_distr in H0. + apply Forall_app in H0; crush. } + { exploit unnest_predicate_forall_gt1. apply NEST1. apply NEST2. + all: eauto. intros. cbn. rewrite rev_app_distr in H0. + apply Forall_app in H0; crush. } } + { apply Forall_rev. apply Forall_forall; intros. + cbn [max_hash_pred] in *. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST1) as TMP. + pose proof (unnest_predicate_lt_in_list2 _ _ _ _ _ NEST2 ltac:(lia)). + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ NEST2). + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST2). + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST1). + eapply Forall_forall in H1; eauto. eapply Forall_forall in H2; eauto. + unfold not; intros. apply max_hash_pred_lt in H5. destruct x; cbn [fst snd] in *. + unfold max_hash_pred in *; fold max_hash_pred in *. + pose proof (unnest_predicate_in_pred2 _ _ _ _ _ NEST1). + pose proof (unnest_predicate_in_pred2 _ _ _ _ _ NEST2). + lia. + } } + { rewrite EVAL2. unfold eval_pred_list. cbn [snd fst]. + rewrite upd_asgn_app2. + { rewrite eval_hash_pred_T_Pand2; auto. + apply fold_left_Pand_rev. rewrite to_equiv_rev. + apply eval_hash_pred_equiv_asgn; auto. + { unfold equiv_list_wf, Sorted; split. + { replace pl_lt with (Basics.flip (Basics.flip (@pl_lt predicate))) by auto. + apply sorted_rev4. eapply unnest_predicate_sorted; eauto. } + { apply Forall_rev. eapply unnest_predicate_lt_in_list2; eauto. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST1). + cbn [max_hash_pred] in *; lia. } } + { exploit unnest_predicate_forall_none. apply NEST1. apply NEST2. + all: eauto. intros. cbn. rewrite rev_app_distr in H0. + apply Forall_app in H0; crush. } + { exploit unnest_predicate_forall_gt1. apply NEST1. apply NEST2. + all: eauto. intros. cbn. rewrite rev_app_distr in H0. + apply Forall_app in H0; crush. } } + { apply Forall_rev. apply Forall_forall; intros. + apply Forall_rev. apply Forall_forall; intros. split. + cbn [max_hash_pred] in *. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST1) as TMP. + pose proof (unnest_predicate_lt_in_list2 _ _ _ _ _ NEST2 ltac:(lia)) as X2. + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ NEST2) as Y2. + pose proof (unnest_predicate_lt_in_list2 _ _ _ _ _ NEST1 ltac:(lia)) as X1. + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ NEST1) as Y1. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST2) as Z2. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST1) as Z1. + destruct x, x0. cbn [fst snd] in *. + eapply Forall_forall in X2; eauto. eapply Forall_forall in Y2; eauto. + eapply Forall_forall in X1; eauto. eapply Forall_forall in Y1; eauto. + cbn [fst snd] in *. lia. + + pose proof (unnest_predicate_lt_in_list3 _ _ _ _ _ NEST2) as X. + unfold not; intros Y. eapply Forall_forall in X; eauto. + cbn [max_hash_pred] in *. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST1) as TMP. + pose proof (unnest_predicate_lt_in_list2 _ _ _ _ _ NEST2 ltac:(lia)) as X2. + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ NEST2) as Y2. + pose proof (unnest_predicate_lt_in_list2 _ _ _ _ _ NEST1 ltac:(lia)) as X1. + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ NEST1) as Y1. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST2) as Z2. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST1) as Z1. + eapply Forall_forall in X2; eauto. eapply Forall_forall in Y2; eauto. + eapply Forall_forall in X1; eauto. eapply Forall_forall in Y1; eauto. + destruct x, x0. cbn [fst snd] in *. + unfold max_hash_pred in *; fold max_hash_pred in *. + exploit unnest_predicate_in_pred2; eauto. lia; intros. + exploit unnest_predicate_in_pred2. eapply NEST1. lia. intros. + pose proof Y as YY. apply X in YY. inv YY. + eapply map_in_some in H4; simplify. destruct x; simplify. + eapply max_hash_pred_lt in Y. + pose proof (unnest_predicate_lt_in_list2 _ _ _ _ _ NEST2) as X22. + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ NEST2) as Y22. + eapply Forall_forall in X22; eauto. eapply Forall_forall in Y22; eauto. + cbn [fst snd] in *. + cbn [max_hash_pred] in *. + lia. lia. + eapply max_hash_pred_lt in H4. lia. + } + { intros. apply in_rev in H0. destruct x; cbn [fst snd] in *. + unfold not; intros Y. + cbn [max_hash_pred] in *. + pose proof (unnest_predicate_lt_in_list2 _ _ _ _ _ NEST1 ltac:(lia)) as X1. + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ NEST1) as Y1. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST1) as Z1. + eapply Forall_forall in X1; eauto. eapply Forall_forall in Y1; eauto. + cbn [max_hash_pred fst snd] in *. + exploit unnest_predicate_in_pred2; eauto. lia; intros. intros. + exploit unnest_predicate_in_pred2. eapply NEST1. lia. intros. + exploit unnest_predicate_lt_in_list4. eapply NEST2. eauto. intros. inv H3. + eapply map_in_some in H4; simplify. + destruct x. + cbn [max_hash_pred fst snd] in *. + pose proof (unnest_predicate_lt_in_list2 _ _ _ _ _ NEST2 ltac:(lia)) as X2. + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ NEST2) as Y2. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST2) as Z2. + eapply Forall_forall in X2; eauto. eapply Forall_forall in Y2; eauto. + cbn [max_hash_pred fst snd] in *. + eapply max_hash_pred_lt in Y. lia. + eapply max_hash_pred_lt in H4. lia. + } } } + symmetry; rewrite eval_hash_pred_T_Pand2; rewrite rev_app_distr; + try solve [rewrite <- app_assoc; auto]; auto. + Qed. + +Lemma unnest_predicate_correct_Pimp : + forall p1 p2 p0 p3 l0 l p4 fresh p a, + unnest_predicate fresh p1 = (p, l, p0) -> + unnest_predicate p0 p2 = (p3, l0, p4) -> + eval_hash_pred p1 a = eval_pred_list (p, l) (upd_asgn (rev l) a) -> + eval_hash_pred p2 a = eval_pred_list (p3, l0) (upd_asgn (rev l0) a) -> + (forall y, (fresh <= y)%positive -> a ! y = None) -> + (max_hash_pred (p1 → p2) < fresh)%positive -> + eval_hash_pred (p1 → p2) a = + eval_hash_pred (Pbase p4 ∧ fold_left Pand (to_equiv (l0 ++ l)) (T ∧ equiv (Pbase p4) (p → p3))) + (upd_asgn (rev (l0 ++ l) ++ (p4, p → p3) :: nil) a). + Proof. + intros * NEST1 NEST2 EVAL1 EVAL2 GTMAX MAXHASH. + assert (eval_hash_pred (fold_left Pand (to_equiv (l0 ++ l)) (T ∧ equiv (Pbase p4) (p → p3))) + (upd_asgn (rev l ++ rev l0 ++ (p4, p → p3) :: nil) a) = Some true). + { apply eval_hash_pred_fold_Pand2 with (x := T). + rewrite app_assoc. + rewrite upd_asgn_app1; [|eapply unnest_predicate_not_in; eauto]. + replace (rev l ++ rev l0) with (rev (l0 ++ l)) by (rewrite rev_app_distr; auto). + apply fold_left_Pand_rev. + rewrite to_equiv_rev. + apply eval_hash_pred_equiv_asgn; auto. + eapply unnest_predicate_equiv_list; eauto. + eapply unnest_predicate_forall_none; eauto. + eapply unnest_predicate_forall_gt1; eauto. + rewrite eval_hash_pred_T_Pand. + eapply eval_equiv_added_Pimp; eauto. + } + assert (eval_hash_pred (p1 → p2) a = eval_hash_pred (Pbase p4) + (upd_asgn ((rev l ++ rev l0) ++ (p4, p → p3) :: nil) a)). + { rewrite eval_hash_pred_get. + rewrite upd_asgn_get. + 2: { rewrite upd_asgn_not_in by (eapply unnest_predicate_not_in2; eauto). + eapply GTMAX. unfold max_hash_pred in *. fold max_hash_pred in *. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST1). + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST2). lia. + } + apply eval_hash_pred_pimp. + { rewrite EVAL1. unfold eval_pred_list. cbn [snd fst]. + rewrite upd_asgn_app1. + { rewrite eval_hash_pred_T_Pand2; auto. + apply fold_left_Pand_rev. rewrite to_equiv_rev. + apply eval_hash_pred_equiv_asgn; auto. + { unfold equiv_list_wf, Sorted; split. + { replace pl_lt with (Basics.flip (Basics.flip (@pl_lt predicate))) by auto. + apply sorted_rev4. eapply unnest_predicate_sorted; eauto. } + { apply Forall_rev. eapply unnest_predicate_lt_in_list2; eauto. + cbn [max_hash_pred] in *; lia. } } + { exploit unnest_predicate_forall_none. apply NEST1. apply NEST2. + all: eauto. intros. cbn. rewrite rev_app_distr in H0. + apply Forall_app in H0; crush. } + { exploit unnest_predicate_forall_gt1. apply NEST1. apply NEST2. + all: eauto. intros. cbn. rewrite rev_app_distr in H0. + apply Forall_app in H0; crush. } } + { apply Forall_rev. apply Forall_forall; intros. + cbn [max_hash_pred] in *. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST1) as TMP. + pose proof (unnest_predicate_lt_in_list2 _ _ _ _ _ NEST2 ltac:(lia)). + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ NEST2). + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST2). + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST1). + eapply Forall_forall in H1; eauto. eapply Forall_forall in H2; eauto. + unfold not; intros. apply max_hash_pred_lt in H5. destruct x; cbn [fst snd] in *. + unfold max_hash_pred in *; fold max_hash_pred in *. + pose proof (unnest_predicate_in_pred2 _ _ _ _ _ NEST1). + pose proof (unnest_predicate_in_pred2 _ _ _ _ _ NEST2). + lia. + } } + { rewrite EVAL2. unfold eval_pred_list. cbn [snd fst]. + rewrite upd_asgn_app2. + { rewrite eval_hash_pred_T_Pand2; auto. + apply fold_left_Pand_rev. rewrite to_equiv_rev. + apply eval_hash_pred_equiv_asgn; auto. + { unfold equiv_list_wf, Sorted; split. + { replace pl_lt with (Basics.flip (Basics.flip (@pl_lt predicate))) by auto. + apply sorted_rev4. eapply unnest_predicate_sorted; eauto. } + { apply Forall_rev. eapply unnest_predicate_lt_in_list2; eauto. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST1). + cbn [max_hash_pred] in *; lia. } } + { exploit unnest_predicate_forall_none. apply NEST1. apply NEST2. + all: eauto. intros. cbn. rewrite rev_app_distr in H0. + apply Forall_app in H0; crush. } + { exploit unnest_predicate_forall_gt1. apply NEST1. apply NEST2. + all: eauto. intros. cbn. rewrite rev_app_distr in H0. + apply Forall_app in H0; crush. } } + { apply Forall_rev. apply Forall_forall; intros. + apply Forall_rev. apply Forall_forall; intros. split. + cbn [max_hash_pred] in *. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST1) as TMP. + pose proof (unnest_predicate_lt_in_list2 _ _ _ _ _ NEST2 ltac:(lia)) as X2. + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ NEST2) as Y2. + pose proof (unnest_predicate_lt_in_list2 _ _ _ _ _ NEST1 ltac:(lia)) as X1. + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ NEST1) as Y1. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST2) as Z2. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST1) as Z1. + destruct x, x0. cbn [fst snd] in *. + eapply Forall_forall in X2; eauto. eapply Forall_forall in Y2; eauto. + eapply Forall_forall in X1; eauto. eapply Forall_forall in Y1; eauto. + cbn [fst snd] in *. lia. + + pose proof (unnest_predicate_lt_in_list3 _ _ _ _ _ NEST2) as X. + unfold not; intros Y. eapply Forall_forall in X; eauto. + cbn [max_hash_pred] in *. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST1) as TMP. + pose proof (unnest_predicate_lt_in_list2 _ _ _ _ _ NEST2 ltac:(lia)) as X2. + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ NEST2) as Y2. + pose proof (unnest_predicate_lt_in_list2 _ _ _ _ _ NEST1 ltac:(lia)) as X1. + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ NEST1) as Y1. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST2) as Z2. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST1) as Z1. + eapply Forall_forall in X2; eauto. eapply Forall_forall in Y2; eauto. + eapply Forall_forall in X1; eauto. eapply Forall_forall in Y1; eauto. + destruct x, x0. cbn [fst snd] in *. + unfold max_hash_pred in *; fold max_hash_pred in *. + exploit unnest_predicate_in_pred2; eauto. lia; intros. + exploit unnest_predicate_in_pred2. eapply NEST1. lia. intros. + pose proof Y as YY. apply X in YY. inv YY. + eapply map_in_some in H4; simplify. destruct x; simplify. + eapply max_hash_pred_lt in Y. + pose proof (unnest_predicate_lt_in_list2 _ _ _ _ _ NEST2) as X22. + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ NEST2) as Y22. + eapply Forall_forall in X22; eauto. eapply Forall_forall in Y22; eauto. + cbn [fst snd] in *. + cbn [max_hash_pred] in *. + lia. lia. + eapply max_hash_pred_lt in H4. lia. + } + { intros. apply in_rev in H0. destruct x; cbn [fst snd] in *. + unfold not; intros Y. + cbn [max_hash_pred] in *. + pose proof (unnest_predicate_lt_in_list2 _ _ _ _ _ NEST1 ltac:(lia)) as X1. + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ NEST1) as Y1. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST1) as Z1. + eapply Forall_forall in X1; eauto. eapply Forall_forall in Y1; eauto. + cbn [max_hash_pred fst snd] in *. + exploit unnest_predicate_in_pred2; eauto. lia; intros. intros. + exploit unnest_predicate_in_pred2. eapply NEST1. lia. intros. + exploit unnest_predicate_lt_in_list4. eapply NEST2. eauto. intros. inv H3. + eapply map_in_some in H4; simplify. + destruct x. + cbn [max_hash_pred fst snd] in *. + pose proof (unnest_predicate_lt_in_list2 _ _ _ _ _ NEST2 ltac:(lia)) as X2. + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ NEST2) as Y2. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST2) as Z2. + eapply Forall_forall in X2; eauto. eapply Forall_forall in Y2; eauto. + cbn [max_hash_pred fst snd] in *. + eapply max_hash_pred_lt in Y. lia. + eapply max_hash_pred_lt in H4. lia. + } } } + symmetry; rewrite eval_hash_pred_T_Pand2; rewrite rev_app_distr; + try solve [rewrite <- app_assoc; auto]; auto. + Qed. + + Lemma upd_asgn_nil : + forall a, upd_asgn nil a = a. + Proof. crush. Qed. + + #[local] Opaque eval_hash_pred. + #[local] Opaque upd_asgn. + + Lemma unnest_predicate_correct : + forall a p fresh p' lp fresh', + (max_hash_pred p < fresh)%positive -> + unnest_predicate fresh p = (p', lp, fresh') -> + (forall y : positive, (fresh <= y)%positive -> a ! y = None) -> + eval_hash_pred p a = eval_pred_list (p', lp) (upd_asgn (rev lp) a). + Proof. + induction p; try solve [crush]. + - unfold eval_pred_list. simplify. + rewrite eval_hash_pred_T_Pand3. + rewrite upd_asgn_nil. auto. + - simplify. + destruct (unnest_predicate fresh p1) eqn:?. destruct p. + destruct (unnest_predicate p0 p2) eqn:?. destruct p3. + unfold eval_pred_list. simplify. + pose proof Heqp as X1. pose proof Heqp3 as X2. + pose proof X2 as X3. + pose proof X1 as X4. + apply IHp1 in X1; [|lia|auto]. + apply unnest_predicate_lt_fresh in X2. + apply unnest_predicate_lt_fresh in X4. + apply IHp2 in X3; [|lia|intros; eapply H1; lia]. + eapply unnest_predicate_correct_Pand; eauto. + - simplify. + destruct (unnest_predicate fresh p1) eqn:?. destruct p. + destruct (unnest_predicate p0 p2) eqn:?. destruct p3. + unfold eval_pred_list. simplify. + pose proof Heqp as X1. pose proof Heqp3 as X2. + pose proof X2 as X3. + pose proof X1 as X4. + apply IHp1 in X1; [|lia|auto]. + apply unnest_predicate_lt_fresh in X2. + apply unnest_predicate_lt_fresh in X4. + apply IHp2 in X3; [|lia|intros; eapply H1; lia]. + eapply unnest_predicate_correct_Por; eauto. + - simplify. + destruct (unnest_predicate fresh p1) eqn:?. destruct p. + destruct (unnest_predicate p0 p2) eqn:?. destruct p3. + unfold eval_pred_list. simplify. + pose proof Heqp as X1. pose proof Heqp3 as X2. + pose proof X2 as X3. + pose proof X1 as X4. + apply IHp1 in X1; [|lia|auto]. + apply unnest_predicate_lt_fresh in X2. + apply unnest_predicate_lt_fresh in X4. + apply IHp2 in X3; [|lia|intros; eapply H1; lia]. + eapply unnest_predicate_correct_Pimp; eauto. + - simplify. repeat destruct_match; simplify. + unfold eval_pred_list; simplify. + pose proof Heqp0 as X1. + pose proof Heqp0 as X2. + apply IHp in X1; [|lia|auto]. + apply unnest_predicate_lt_fresh in X2. + symmetry; rewrite eval_hash_pred_T_Pand2; [symmetry|]. + + rewrite eval_hash_pred_get. + rewrite upd_asgn_get; + [|rewrite upd_asgn_not_in; auto; + eapply unnest_predicate_not_in3; eauto]; []. + apply eval_hash_pred_pnot. rewrite X1. unfold eval_pred_list. + simplify. + rewrite eval_hash_pred_T_Pand2; auto; []. + apply fold_left_Pand_rev. rewrite to_equiv_rev. + apply eval_hash_pred_equiv_asgn; auto. + * unfold equiv_list_wf; split. + ** unfold Sorted. + replace pl_lt with (Basics.flip (Basics.flip (@pl_lt predicate))) by auto. + apply sorted_rev4. eapply unnest_predicate_sorted; eauto. + ** apply Forall_rev; eapply unnest_predicate_lt_in_list2; eauto. + * apply Forall_rev. apply Forall_forall; intros. apply H1. + apply unnest_predicate_lt_in_list in Heqp0. eapply Forall_forall in Heqp0; eauto. + lia. + * apply Forall_rev. apply Forall_forall; intros. simplify. + apply unnest_predicate_lt_in_list in Heqp0. eapply Forall_forall in Heqp0; eauto. + lia. + + apply eval_hash_pred_fold_Pand2 with (x := T). + * rewrite upd_asgn_app1; [|eapply unnest_predicate_not_in4; eauto]; []. + apply fold_left_Pand_rev. rewrite to_equiv_rev. + apply eval_hash_pred_equiv_asgn; auto. + ** unfold equiv_list_wf; split. + *** unfold Sorted. + replace pl_lt with (Basics.flip (Basics.flip (@pl_lt predicate))) by auto. + apply sorted_rev4. eapply unnest_predicate_sorted; eauto. + *** apply Forall_rev; eapply unnest_predicate_lt_in_list2; eauto. + ** apply Forall_rev. apply Forall_forall; intros. apply H1. + apply unnest_predicate_lt_in_list in Heqp0. eapply Forall_forall in Heqp0; eauto. + lia. + ** apply Forall_rev. apply Forall_forall; intros. simplify. + apply unnest_predicate_lt_in_list in Heqp0. eapply Forall_forall in Heqp0; eauto. + lia. + * rewrite eval_hash_pred_T_Pand. + apply eval_equiv2. rewrite eval_hash_pred_get. + rewrite upd_asgn_get. + ** symmetry. rewrite upd_asgn_app1; auto. + constructor; [|constructor]; simpl. + pose proof Heqp0 as NEST1. + pose proof NEST1 as Y1. + eapply unnest_predicate_in_pred2 in Y1. + *** pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST1). + unfold not; intros. apply max_hash_pred_lt in H2. + cbn [max_hash_pred] in H2. lia. + *** unfold max_hash_pred in *; fold max_hash_pred in *. + pose proof (unnest_predicate_lt_fresh _ _ _ _ _ NEST1). lia. + ** rewrite upd_asgn_not_in; auto. eapply unnest_predicate_not_in3; eauto. + Qed. + +#[local] Transparent eval_hash_pred. +#[local] Transparent upd_asgn. + +Lemma interp_aux_correct : + forall a ba f h1 h2, + (forall l, h1 <> Fimp l) -> + is_true (form_eqb h1 h2) -> + interp_aux a ba f h1 = interp_aux a ba f h2. +Proof. + unfold is_true. intros * IMP **. + destruct h1; unfold form_eqb in *; try destruct_match; crush; auto. + - apply Int63.eqb_spec in H; subst; auto. + - apply Int63.eqb_spec in H0; subst; + apply Int63.eqb_spec in H1; subst; auto. + - apply list_beq_spec in H. subst. + rewrite !Misc.afold_left_and. rewrite H. auto. + exact Int63.eqb_spec. + - apply list_beq_spec in H. subst. + rewrite !Misc.afold_left_or. rewrite H. auto. + exact Int63.eqb_spec. + - apply Int63.eqb_spec in H0. apply Int63.eqb_spec in H1. subst; auto. + - apply Int63.eqb_spec in H0. apply Int63.eqb_spec in H1. subst; auto. + - apply Int63.eqb_spec in H. apply Int63.eqb_spec in H1. apply Int63.eqb_spec in H2. subst; auto. +Qed. + +Lemma form_eqb_symm : + forall f1 f2, + is_true (form_eqb f1 f2) -> + is_true (form_eqb f2 f1). +Proof. + unfold is_true, form_eqb. intros. repeat destruct_match; try discriminate; auto; + repeat match goal with + | H: Int63.eqb _ _ = true |- _ => apply Int63.eqb_spec in H + | |- Int63.eqb _ _ = true => apply Int63.eqb_spec + | H: list_beq _ _ _ = true |- _ => apply list_beq_spec in H; [|exact Int63.eqb_spec] + | |- list_beq _ _ _ = true => apply list_beq_spec; [exact Int63.eqb_spec|] + | H: _ && _ = true |- _ => simplify + | |- _ && _ = true => apply andb_true_intro; split + end; subst; auto. +Qed. + +Lemma t_interp_check_form : (* First array might be shorter, but it's ok *) + forall a ba f1 f2 l, + is_true (check_array form_eqb f1 f2) -> + is_true (check_local_form l f1) -> + forall i, + to_Z i < to_Z (PArray.length f1) -> + PArray.get (t_interp a ba f1) i = PArray.get (t_interp a ba f2) i. +Proof. + intros * TRUE CHK. + pose proof (check_array_length _ _ _ _ TRUE) as BOUNDS. + pose proof (length_t_interp a ba f1) as Y. + pose proof (length_t_interp a ba f2) as Z. + revert Y. + unfold t_interp. + apply Misc.foldi_ind with (P := + fun iter fol => + PArray.length fol = PArray.length f1 -> + forall i, + to_Z i < to_Z iter -> + PArray.get fol i = PArray.get _ i + ); intros. + + apply Misc.leb_0. + + pose proof (not_int63_lt_0 i). replace (to_Z 0) with 0 in H0 by auto. lia. + + rewrite PArray.length_set in H2. specialize (H1 H2). + erewrite Misc.to_Z_add_1 in H3 by eassumption. + destruct (int_dec i i0); subst. + - rewrite PArray.get_set_same by (now rewrite H2). + revert H1. revert Z. unfold t_interp. + assert (W: to_Z i0 < to_Z (PArray.length f2)). + { apply Int63.ltb_spec in H0. lia. } revert W. + apply Misc.foldi_ind with (P := + fun iter fol => + to_Z i0 < to_Z iter -> + PArray.length fol = PArray.length f2 -> + (forall i, to_Z i < to_Z i0 -> + PArray.get a0 i = PArray.get fol i) -> + _ = PArray.get fol i0 + ); intros. + * apply Misc.leb_0. + * pose proof (not_int63_lt_0 i0). replace (to_Z 0) with 0 in H1 by auto. lia. + * rewrite PArray.length_set in H7. erewrite Misc.to_Z_add_1 in H6 by eassumption. + destruct (int_dec i0 i); subst. rewrite PArray.get_set_same by (now rewrite H7). + rewrite lt_form_interp_form_aux with (f2 := PArray.get a1) (i := i). + erewrite interp_aux_correct; auto. + eapply check_local_form_spec; eauto. + apply form_eqb_symm. eapply check_array_correct; eauto. + apply Int63.ltb_spec in H0. auto. + intros. rewrite H8. rewrite PArray.get_set_other; auto. + unfold not; intros; subst. apply Int63.ltb_spec in H9. lia. + apply Int63.ltb_spec in H9; auto. + unfold check_local_form, is_true in *. simplify. + unfold check_form in *. simplify. unfold wf in *. + eapply Misc.aforallbi_spec in H12; eauto. + rewrite PArray.get_set_other by auto. eapply H5; auto. + assert (to_Z i <> to_Z i0) by (unfold not; intros; apply n; now apply to_Z_inj). + lia. intros. rewrite H8; auto. rewrite PArray.get_set_other. auto. + unfold not; intros. subst. lia. + - rewrite PArray.get_set_other by auto. eapply H1. + assert (to_Z i <> to_Z i0) by (unfold not; intros; apply n; now apply to_Z_inj). + lia. +Qed. + +(* Lemma a_interp_check_form : *) +(* forall t_i (t_func: PArray.array (tval t_i)) a ba a1 a2, *) +(* is_true (check_array Atom.eqb a1 a2) -> *) +(* is_true (check_array Bool.eqb (Atom.t_interp t_i t_func a1) (Atom.t_interp t_i t_func a2)). *) + +(* Misc.foldi_ind2: *) +(* forall (A B : Type) (P : Int63.int -> A -> B -> Prop) (f1 : Int63.int -> A -> A) *) +(* (f2 : Int63.int -> B -> B) (from to : Int63.int) (a1 : A) (a2 : B), *) +(* Int63.leb from to = true -> *) +(* P from a1 a2 -> *) +(* (forall (i : Int63.int) (a3 : A) (a4 : B), *) +(* Int63.leb from i = true -> Int63.ltb i to = true -> P i a3 a4 -> P (Int63.add i 1) (f1 i a3) (f2 i a4)) -> *) +(* P to (Misc.foldi f1 from to a1) (Misc.foldi f2 from to a2) *) + +Definition ind_S A a_init j (to: Int63.int) (fold1: PArray.array A) := + PArray.get fold1 j = PArray.get a_init j. + +Lemma ind_S_nochange : + forall a2 j from to a_init, + Int63.to_Z j < Int63.to_Z from <= Int63.to_Z to -> + ind_S _ a_init j to (Misc.foldi + (fun (i : Int63.int) (t_a : PArray.array (bval t_i)) => + PArray.set t_a i (Atom.interp_aux t_i t_func (PArray.get t_a) (PArray.get a2 i))) from to a_init). +Proof. + intros. apply Misc.foldi_ind. apply Int63.leb_spec. lia. now unfold ind_S. + intros. unfold ind_S. rewrite PArray.get_set_other. + unfold ind_S in H2. auto. apply Int63.leb_spec in H0. + unfold not. intros. assert (Int63.to_Z i = Int63.to_Z j) by (now subst). + lia. +Qed. + +Lemma foldi_array_get_constant : + forall a2 j from to a_init, + Int63.to_Z j < Int63.to_Z from <= Int63.to_Z to -> + PArray.get (Misc.foldi + (fun (i : Int63.int) (t_a : PArray.array (bval t_i)) => + PArray.set t_a i (Atom.interp_aux t_i t_func (PArray.get t_a) (PArray.get a2 i))) from to a_init) j = PArray.get a_init j. +Proof. apply ind_S_nochange; auto. Qed. + +Definition interp_ind_S t_i j (to: Int63.int) (fold1: PArray.array (bval t_i)) fold2 := + PArray.get fold1 j = PArray.get fold2 j. + +(* (Misc.foldi *) +(* (fun (i : Int63.int) (t_a : PArray.array (bval t_i)) => *) +(* PArray.set t_a i (Atom.interp_aux t_i t_func (PArray.get t_a) (PArray.get a2 i))) (Int63.of_Z 0) *) +(* (PArray.length a2) (PArray.make (PArray.length a2) (interp_cop t_i CO_xH))) *) + +Lemma get_t_interp : + forall a1 a2 j, + is_true (Atom.wf a1) -> + is_true (check_array Atom.eqb a1 a2) -> + Int63.to_Z j < Int63.to_Z (PArray.length a1) -> + PArray.get (Atom.t_interp t_i t_func a1) j = + PArray.get (Atom.t_interp t_i t_func a2) j. +Proof. + intros * WF CHECK BOUNDS. unfold Atom.t_interp. + pose proof (check_array_length _ _ _ _ CHECK) as ABOUNDS. + pose proof (check_array_correct _ _ _ _ CHECK). + generalize dependent j. + assert (TMP: forall (A: Prop), + A /\ ((Int63.to_Z (PArray.length (Misc.foldi + (fun (i : Int63.int) (t_a : PArray.array (bval t_i)) => + PArray.set t_a i (Atom.interp_aux t_i t_func (PArray.get t_a) (PArray.get a1 i))) (Int63.of_Z 0) + (PArray.length a1) (PArray.make (PArray.length a1) (interp_cop t_i CO_xH))))) + = Int63.to_Z (PArray.length a1)) -> A) by tauto. apply TMP. clear TMP. + apply Misc.foldi_ind with + (P := fun iter fol => + (forall j, + Int63.to_Z j < Int63.to_Z iter -> + PArray.get fol j = + PArray.get + (Misc.foldi + (fun (i : Int63.int) (t_a : PArray.array (bval t_i)) => + PArray.set t_a i (Atom.interp_aux t_i t_func (PArray.get t_a) (PArray.get a2 i))) + (Int63.of_Z 0) + (PArray.length a2) (PArray.make (PArray.length a2) (interp_cop t_i CO_xH))) j) /\ + Int63.to_Z (PArray.length fol) = Int63.to_Z (PArray.length a1) + ). + + apply Int63.leb_spec; simpl. + pose proof (not_int63_lt_0 (PArray.length a1)). + rewrite Int63.to_Z_0. auto. + + split. + + intros j BOUNDS0. pose proof (not_int63_lt_0 j). simpl in BOUNDS0. rewrite Int63.to_Z_0 in BOUNDS0. lia. + auto. + + intros i a ILEB ILTB [HIND ARRLEN]; split. + + intros j BOUNDS1. + erewrite Misc.to_Z_add_1 in BOUNDS1 by eassumption. + apply Int63.ltb_spec in ILTB. + apply Int63.leb_spec in ILEB. + assert (X: Int63.to_Z j < Int63.to_Z i \/ Int63.to_Z j = Int63.to_Z i) by lia. + inversion X as [X1|X1]. rewrite PArray.get_set_other. + rewrite HIND; auto; try lia. + unfold not; intro EQ. assert (Int63.to_Z j = Int63.to_Z i) by (now subst). lia. + assert (j = i) by (now apply Int63.to_Z_inj). subst. + rewrite PArray.get_set_same. 2: { apply Int63.ltb_spec. lia. } + generalize dependent HIND. + assert (X2: Int63.to_Z i < Int63.to_Z (PArray.length a2)) by lia. + generalize X2. + assert (TMP: forall (A: Prop), + A /\ ((Int63.to_Z (PArray.length (Misc.foldi + (fun (i0 : Int63.int) (t_a : PArray.array (bval t_i)) => + PArray.set t_a i0 (Atom.interp_aux t_i t_func (PArray.get t_a) (PArray.get a2 i0))) + (Int63.of_Z 0) (PArray.length a2) (PArray.make (PArray.length a2) (interp_cop t_i CO_xH))))) + = Int63.to_Z (PArray.length a2)) -> A) by tauto. apply TMP. clear TMP. + apply Misc.foldi_ind with + (P := fun iter fol => + (Int63.to_Z i < Int63.to_Z iter -> + (forall j : Int63.int, + Int63.to_Z j < Int63.to_Z i -> + PArray.get a j = PArray.get fol j) -> + Atom.interp_aux t_i t_func (PArray.get a) (PArray.get a1 i) = PArray.get fol i) + /\ Int63.to_Z (PArray.length fol) = Int63.to_Z (PArray.length a2) + ). + + apply Int63.leb_spec. lia. + + split. intros BOUNDS2. + pose proof (not_int63_lt_0 i). lia. auto. + + intros i0 a0 ILEB1 ILTB1 HIND2. split. + intros BOUNDS3 HHIND. + erewrite Misc.to_Z_add_1 in BOUNDS3 by eassumption. + assert (A: Int63.to_Z i < Int63.to_Z i0 \/ Int63.to_Z i = Int63.to_Z i0) by lia. + inversion A as [A1|A1]; clear A; subst. + rewrite PArray.get_set_other. eapply HIND2. lia. + intros j BOUNDS4. rewrite HHIND. rewrite PArray.get_set_other; auto. + unfold not; intros EQ. assert (U: Int63.to_Z j = Int63.to_Z i0) by (now subst). lia. lia. + unfold not; intros EQ. assert (U: Int63.to_Z i = Int63.to_Z i0) by (now subst). lia. + assert (U: i = i0) by (now apply Int63.to_Z_inj). subst. + rewrite PArray.get_set_same by (apply Int63.ltb_spec; lia). + assert (EQ: PArray.get a2 i0 = PArray.get a1 i0). + { apply Atom.eqb_spec. apply check_array_correct; auto. } + + rewrite EQ in *. + apply lt_interp_aux with (i := i0). intros j ILTB2. + rewrite HHIND. apply Int63.ltb_spec in ILTB2. rewrite PArray.get_set_other. + auto. unfold not; intros EQ1. assert (U: Int63.to_Z i0 = Int63.to_Z j) by (now subst). lia. + apply Int63.ltb_spec in ILTB2. + auto. + unfold Atom.wf in WF. eapply Misc.aforallbi_spec in WF. eassumption. + apply Int63.ltb_spec. lia. + rewrite PArray.length_set. lia. + rewrite PArray.length_set. lia. +Qed. + +Lemma interp_form_hatom_preserved : + forall a1 a2 x, + is_true (Atom.wf a1) -> + is_true (check_array Atom.eqb a1 a2) -> + Int63.to_Z x < Int63.to_Z (PArray.length a1) -> + interp_form_hatom t_i t_func a1 x = interp_form_hatom t_i t_func a2 x. +Proof. + unfold interp_form_hatom. unfold interp_hatom. intros. + erewrite get_t_interp; auto. +Qed. + +(* Definition F f2 := *) +(* (fun i fol => *) +(* (forall x, 0 <= Int63.to_Z x < Int63.to_Z i -> *) +(* PArray.get fol x *) +(* = PArray.get *) +(* (Misc.foldi *) +(* (fun (i : Int63.int) (t_b : PArray.array bool) => *) +(* PArray.set t_b i *) +(* (interp_aux (interp_form_hatom t_i t_func a1) *) +(* (interp_form_hatom_bv t_i t_func a1) *) +(* (PArray.get t_b) (PArray.get f2 i))) *) +(* (Int63.of_Z 0) (PArray.length f2) *) +(* (PArray.make (PArray.length f2) true)) x)). *) + +Definition form_atom f P := + forall i a, PArray.get f i = Fatom a -> P a. + +Definition single_form_atom f P := + forall a, f = Fatom a -> P a. + +Lemma lt_form_interp_single_form_atom : + forall a1 a2 b f h, + single_form_atom h (fun j => a1 j = a2 j) -> + interp_aux a1 b f h = interp_aux a2 b f h. +Proof. + destruct h;simpl;intros; trivial. auto. +Qed. + +Lemma lt_form_interp_not_bv : + forall b1 b2 a f h, + is_true (single_form_not_bv h) -> + interp_aux a b1 f h = interp_aux a b2 f h. +Proof. + destruct h;simpl;intros; trivial. discriminate. +Qed. + +Lemma check_single_form_not_bv : + forall f1 l x, + is_true (check_local_form l f1) -> + to_Z x < to_Z (PArray.length f1) -> + is_true (single_form_not_bv (PArray.get f1 x)). +Proof. + unfold check_local_form, is_true; simplify. + eapply Misc.aforallbi_spec in H2; [|apply Int63.ltb_spec; eassumption]. + crush. +Qed. + +Lemma is_true_lt_form : + forall a b f1 x, + to_Z a < to_Z b -> + is_true (lt_form a (PArray.get f1 x)) -> + is_true (lt_form b (PArray.get f1 x)). +Proof. + unfold is_true, lt_form; intros. destruct_match; auto. + - apply Int63.ltb_spec in H0. apply Int63.ltb_spec. lia. + - apply Misc.aforallbi_spec; intros. eapply Misc.aforallbi_spec in H0; eauto. + apply Int63.ltb_spec in H0. apply Int63.ltb_spec. lia. + - apply Misc.aforallbi_spec; intros. eapply Misc.aforallbi_spec in H0; eauto. + apply Int63.ltb_spec in H0. apply Int63.ltb_spec. lia. + - apply Misc.aforallbi_spec; intros. eapply Misc.aforallbi_spec in H0; eauto. + apply Int63.ltb_spec in H0. apply Int63.ltb_spec. lia. + - simplify. apply Int63.ltb_spec in H1. apply Int63.ltb_spec in H2. + apply andb_true_intro. split; apply Int63.ltb_spec; lia. + - simplify. apply Int63.ltb_spec in H1. apply Int63.ltb_spec in H2. + apply andb_true_intro. split; apply Int63.ltb_spec; lia. + - simplify. apply Int63.ltb_spec in H2. apply Int63.ltb_spec in H3. + apply Int63.ltb_spec in H0. + apply andb_true_intro; split; [apply andb_true_intro;split|]; + apply Int63.ltb_spec; lia. + - apply forallb_forall; intros. eapply forallb_forall in H0; try eassumption. + apply Int63.ltb_spec in H0. apply Int63.ltb_spec. lia. +Qed. + +Lemma get_t_interp_form : + forall a1 a2 f1, + is_true (check_array Atom.eqb a1 a2) -> + is_true (check_local_form (PArray.length a1) f1) -> + is_true (Atom.wf a1) -> + forall x, + Int63.to_Z x < Int63.to_Z (PArray.length f1) -> + (PArray.get (t_interp (interp_form_hatom t_i t_func a2) (interp_form_hatom_bv t_i t_func a2) f1) x + = PArray.get (t_interp (interp_form_hatom t_i t_func a1) (interp_form_hatom_bv t_i t_func a1) f1) x). +Proof. + intros * TRUE LOCAL WFA. + pose proof (length_t_interp (interp_form_hatom t_i t_func a1) + (interp_form_hatom_bv t_i t_func a1) f1) as Y1. + pose proof (length_t_interp (interp_form_hatom t_i t_func a2) + (interp_form_hatom_bv t_i t_func a2) f1) as Y2. + revert Y1. revert Y2. + unfold t_interp. + apply Misc.foldi_ind2 with (P := + fun i a b => + PArray.length a = PArray.length f1 -> + PArray.length b = PArray.length f1 -> + forall x, + to_Z x < to_Z (PArray.length f1) -> + PArray.get a x = PArray.get b x + ). + - apply Misc.leb_0. + - intros. now rewrite PArray.get_make. + - intros. rewrite PArray.length_set in H2. rewrite PArray.length_set in H3. + specialize (H1 H2 H3). + destruct (int_dec i x); subst. + + symmetry. rewrite !PArray.get_set_same + by (try rewrite H3 in *; try rewrite H2 in *; apply Int63.ltb_spec; lia); auto. + erewrite lt_form_interp_not_bv; [|eapply check_single_form_not_bv; eauto]. + erewrite lt_form_interp_single_form_atom. + 2: { unfold single_form_atom; intros. + rewrite interp_form_hatom_preserved with (a2 := a2). eauto. + auto. auto. apply check_local_form_spec in LOCAL; simplify. + eapply Int63.ltb_spec. eapply H7; eauto. } + erewrite lt_form_interp_form_aux. eauto. intros. symmetry. eapply H1; eauto. + apply Int63.ltb_spec in H5. eauto. + unfold is_true in LOCAL. unfold check_local_form in LOCAL. simplify. unfold check_form in H5. + simplify. unfold wf in H8. eapply Misc.aforallbi_spec in H8; eauto. + eapply is_true_lt_form; eauto. + + rewrite !PArray.get_set_other by auto; auto. +Qed. + +Lemma valid_same_corr : + forall r a1 a2 f1 f2, + ~ is_true (Euf_Checker.valid t_func a2 f2 r) -> + is_true (check_array Atom.eqb a1 a2) -> + is_true (check_array form_eqb f1 f2) -> + is_true (check_local_form (PArray.length a1) f1) -> + is_true (Atom.wf a1) -> + Forall (fun x => Int63.to_Z (State.Lit.blit x) < Int63.to_Z (PArray.length f1)) + (Misc.to_list r) -> + ~ is_true (Euf_Checker.valid t_func a1 f1 r). +Proof. + unfold is_true. intros * VAL CHKA CHKF LOCAL WFA LT. + unfold not in *. intros * VAL2; apply VAL. + unfold Euf_Checker.valid in *. rewrite Misc.afold_left_and in *. + rewrite forallb_forall in *; intros x IN. + pose proof IN as VAL3. + apply VAL2 in VAL3. + rewrite Forall_forall in LT. + pose proof IN as VAL4. + apply LT in VAL4. + unfold interp_state_var. + unfold State.Lit.interp. + unfold State.Var.interp. + pose proof CHKF as INTERP. + eapply t_interp_check_form in INTERP; eauto. + rewrite <- INTERP. + now rewrite get_t_interp_form with (a1 := a1). +Qed. + +End PROOF. + +Lemma restrict_a : + forall p a a', + (forall y, (y <= max_hash_pred p)%positive -> a ! y = a' ! y) -> + eval_hash_pred p a = eval_hash_pred p a'. +Proof. + induction p; crush. + - now rewrite H by lia. + - erewrite IHp1. erewrite IHp2. eauto. + intros. eapply H. lia. + intros. eapply H. lia. + - erewrite IHp1. erewrite IHp2. eauto. + intros. eapply H. lia. + intros. eapply H. lia. + - erewrite IHp1. erewrite IHp2. eauto. + intros. eapply H. lia. + intros. eapply H. lia. + - erewrite IHp. eauto. + intros. eapply H. lia. +Qed. + +Definition nuke_asgn p (a: PTree.t bool) := + PTree.fold (fun b i a => + if (i <=? max_hash_pred p)%positive then PTree.set i a b else b + ) a (PTree.empty bool). + +Lemma eval_hash_pred_ext: + forall m m': PTree.t bool, + (forall x : PTree.elt, m ! x = m' ! x) -> + forall p, + eval_hash_pred p m = eval_hash_pred p m'. +Proof. + intros. eapply restrict_a; eauto. +Qed. + +Lemma nuke_asgn_spec : + forall p a, + exists a', + eval_hash_pred p a = eval_hash_pred p a' + /\ (forall y, (max_hash_pred p < y)%positive -> a' ! y = None). +Proof. + intros. exists (nuke_asgn p a). + revert p. revert a. + split. + - eapply restrict_a; eauto. + intros. unfold nuke_asgn. + eapply PTree_Properties.fold_rec; eauto. + + intros. rewrite <- H0; auto. + + intros. + case_eq ((k <=? max_hash_pred p)%positive); intros. + * destruct (peq k y). + -- subst. rewrite ! PTree.gss; auto. + -- rewrite ! PTree.gso; auto. + * rewrite POrderedType.Positive_as_DT.leb_gt in H3. + assert (k <> y) by lia. + rewrite PTree.gso; auto. + - unfold nuke_asgn. + eapply PTree_Properties.fold_rec; eauto; intros. + + unfold PTree.empty. rewrite PTree.gleaf; auto. + + destruct_match. + * rewrite POrderedType.Positive_as_DT.leb_le in Heqb. + assert (k <> y) by lia. + rewrite PTree.gso; auto. + * eapply H1; eauto. +Qed. + +Lemma nuke_1 : + forall p a x, + ~ pred_In x p -> + eval_hash_pred p a = eval_hash_pred p (PTree.remove x a). +Proof. + induction p; crush. + - assert (a <> x) by (unfold not; intros; subst; apply H; constructor). + rewrite PTree.gro; auto. + - assert (~ pred_In x p1) by (unfold not; intros; apply H; apply pred_In_Pand1; auto). + assert (~ pred_In x p2) by (unfold not; intros; apply H; apply pred_In_Pand2; auto). + erewrite IHp1; [|eassumption]; erewrite IHp2; eauto. + - assert (~ pred_In x p1) by (unfold not; intros; apply H; apply pred_In_Por1; auto). + assert (~ pred_In x p2) by (unfold not; intros; apply H; apply pred_In_Por2; auto). + erewrite IHp1; [|eassumption]; erewrite IHp2; eauto. + - assert (~ pred_In x p1) by (unfold not; intros; apply H; apply pred_In_Pimp1; auto). + assert (~ pred_In x p2) by (unfold not; intros; apply H; apply pred_In_Pimp2; auto). + erewrite IHp1; [|eassumption]; erewrite IHp2; eauto. + - assert (~ pred_In x p) by (unfold not; intros; apply H; constructor; auto). + erewrite IHp; eauto. +Qed. + +Lemma check_atom_to_smt : + forall a p d forms atoms, + to_smt a p = Some (d, forms, atoms) -> + is_true (check_atom atoms). +Proof. + intros. unfold to_smt in *. repeat (destruct_match; try discriminate; []). + crush. +Qed. + +Lemma check_wt_extra_spec : + forall a, + is_true (check_atom a) -> + is_true (check_wt_extra a) -> + (forall i x l, PArray.get a i <> Anop x l) + /\ (forall i n l, PArray.get a i = Aapp n l -> l = nil). +Proof. + unfold check_wt_extra, is_true; intros. + split; intros. + - unfold not; intros. exploit PArray.get_not_default_lt. rewrite H1. + unfold check_atom in H. destruct (PArray.default a); try discriminate. + intros. eapply Misc.aforallbi_spec in H0; eauto. + rewrite H1 in H0. discriminate. + - unfold not; intros. exploit PArray.get_not_default_lt. rewrite H1. + unfold check_atom in H. destruct (PArray.default a); try discriminate. + intros. eapply Misc.aforallbi_spec in H0; eauto. + rewrite H1 in H0. destruct_match; crush. +Qed. + +Lemma pred_in_correct : + forall p c, + pred_in c p = true -> + pred_In c p. +Proof. + induction p; crush; subst. + - constructor. + - apply orb_prop in H. inv H; auto using pred_In_Pand1, pred_In_Pand2. + - apply orb_prop in H. inv H; auto using pred_In_Por1, pred_In_Por2. + - apply orb_prop in H. inv H; auto using pred_In_Pimp1, pred_In_Pimp2. + - constructor; auto. +Qed. + +Lemma pred_in_complete : + forall p c, + pred_In c p -> + pred_in c p = true. +Proof. + induction p; crush; subst; try solve [inv H]. + - inv H. apply Pos.eqb_refl. + - apply orb_true_intro. inv H; intuition. + - apply orb_true_intro. inv H; intuition. + - apply orb_true_intro. inv H; intuition. + - inv H. auto. +Qed. + +Lemma max_key_upd_asgn : + forall l a x, + Forall (fun y => Z.pos y < x) (map fst l) -> + (Z.pos (max_key a) < x) -> + (Z.pos (max_key (upd_asgn l a)) < x). +Proof. + induction l; crush. + inv H. destruct_match. eapply IHl; auto. + - assert (exists y, (PTree.set (fst a) b a0) ! (max_key (PTree.set (fst a) b a0)) = Some y). + { apply max_key_in. + destruct (PTree.bempty (PTree.set (fst a) b a0)) eqn:?; auto. + exfalso. apply PTree.bempty_correct with (x := (fst a)) in Heqb0. rewrite PTree.gss in Heqb0. discriminate. + } + simplify. destruct (peq (max_key (PTree.set (fst a) b a0)) (fst a)). + + lia. + + rewrite PTree.gso in H1; auto. + assert (forall x y, ~ y <= x -> x < y) by lia. + apply H; unfold not; intros. + assert (Z.pos (max_key a0) < Z.pos (max_key (PTree.set (fst a) b a0))) by lia. + pose proof (max_not_present _ (max_key (PTree.set (fst a) b a0)) a0). + rewrite H6 in H1 by lia. discriminate. + - eapply IHl; eauto. +Qed. + +Theorem valid_check_smt : + forall p a, + check_smt p = true -> eval_hash_pred p a = Some true. +Proof. + intros * CHK. unfold check_smt in CHK. repeat (destruct_match; try discriminate; []). + apply andb_prop in CHK; inversion CHK as [CHK1 H0]; clear CHK. + apply andb_prop in CHK1; inversion CHK1 as [CHK6 CHKROOT]; clear CHK1. + apply andb_prop in CHK6; inversion CHK6 as [CHK2 CHK_EXTRA]; clear CHK6. + apply andb_prop in CHK2; inversion CHK2 as [CHK3 H2]; clear CHK2. + apply andb_prop in CHK3; inversion CHK3 as [CHK4 H3]; clear CHK3. + apply andb_prop in CHK4; inversion CHK4 as [CHK5 H4]; clear CHK4. + apply andb_prop in CHK5; inversion CHK5 as [H H5]; clear CHK5. + apply andb_prop in CHKROOT; inversion CHKROOT as [CHKROOT2 P1LT]; clear CHKROOT. + apply andb_prop in CHKROOT2; inversion CHKROOT2 as [CHKROOT3 NOTPREDIN]; clear CHKROOT2. + simplify. destruct p0. + exploit nuke_asgn_spec. intros. inv H1. inv H6. rewrite H1. + assert (NOTPRED: ~ pred_In 1%positive p). + { unfold not; intros HH. apply pred_in_complete in HH. rewrite HH in NOTPREDIN. discriminate. } + erewrite nuke_1; eauto. + assert (ALLEQ: forall y, (max_hash_pred p < y)%positive -> (PTree.remove 1 x) ! y = None). + { intros. destruct (peq 1 y); subst. rewrite PTree.grs. auto. rewrite PTree.gro; auto. } + erewrite unnest_predicate_correct; try eassumption; try lia; [|intros; eapply ALLEQ; lia]. + pose proof (exists_asgn (upd_asgn (rev l) (PTree.remove 1 x))) as X. + assert (ASSUMP1: Z.pos (max_key (upd_asgn (rev l) (PTree.remove 1 x))) - 2 < wB - 1). + { assert (forall l a x, + Forall (fun y => Z.pos y < x) (map fst l) -> + (Z.pos (max_key a) < x) -> + (Z.pos (max_key (upd_asgn l a)) < x)). + { apply max_key_upd_asgn; auto. } + assert (Z.pos (max_key (upd_asgn (rev l) (PTree.remove 1 x))) < wB). + { assert (GTWB: Z.pos p1 < wB) by assumption. eapply H6. apply Forall_forall; intros. + apply map_in_some in H8. inv H8. + inv H9. destruct x1 in *. apply in_rev in H8. + apply unnest_predicate_lt_in_list in Heqp0. eapply Forall_forall in Heqp0; eauto. + cbn [fst snd] in *. lia. + assert ((max_key (PTree.remove 1 x) <= max_hash_pred p)%positive). + { assert (~ (max_hash_pred p < max_key (PTree.remove 1 x))%positive -> (max_key (PTree.remove 1 x) <= max_hash_pred p)%positive) by lia. + apply H8. unfold not; intros. pose proof H9. apply ALLEQ in H9. + destruct (PTree.bempty (PTree.remove 1 x)) eqn:?. + - rewrite max_key_empty in * by auto. lia. + - exploit max_key_in; try eassumption. intros. inv H11. rewrite H12 in H9. discriminate. + } + assert (max_hash_pred p < p1)%positive. + { eapply unnest_predicate_lt_fresh in Heqp0. lia. } + lia. + } + lia. + } + assert (ASSUMP2: (forall (k : positive) (x0 : bool), + (upd_asgn (rev l) (PTree.remove 1 x)) ! k = Some x0 -> 0 <= Z.pos k - 2 < wB)). + { intros. pose proof H6 as XXX. apply max_key_correct in H6. + destruct (in_dec peq k (map fst l)). + { apply map_in_some in i. inv i. inv H8. destruct x1; cbn [fst] in *. + apply unnest_predicate_lt_in_list in Heqp0. eapply Forall_forall in Heqp0; eauto. + cbn [fst] in *. lia. + } + { assert (~ In k (map fst (rev l))). + { unfold not; intros; apply n. rewrite map_rev in H8. apply in_rev in H8. auto. } + rewrite upd_asgn_not_in in XXX by auto. + assert (k = 1%positive \/ (1 < k)%positive) by lia. inv H9. rewrite PTree.grs in XXX. discriminate. + lia. + } + } + assert (X2 := X). clear X. + pose proof (X2 ASSUMP1 ASSUMP2) as X. + inversion X as [res ASGN]; clear X. + assert (ASSUMP3: forall (i : Int63.int) (x0 : nop) (l0 : list Int63.int), + PArray.get a5 i <> Anop x0 l0) by (eapply check_wt_extra_spec; eauto). + assert (ASSUMP4: forall (i n : Int63.int) (l0 : list Int63.int), + PArray.get a5 i = Aapp n l0 -> l0 = nil) by (eapply check_wt_extra_spec; eauto). + eapply wt_correct in H2; try eassumption. + pose proof (Checker_Ext.checker_ext_correct _ _ _ _ _ H0) as X. + assert (XX: is_true (Atom.wf a0)) by + (apply check_atom_to_smt in Heqo; unfold check_atom in *; + destruct (PArray.default a0); try discriminate; + destruct c0; crush). + eapply valid_same_corr in X; try eassumption. + eapply valid_to_smt; try eassumption. + simpl. eapply fold_left_Pand_rev. + rewrite to_equiv_rev. eapply eval_hash_pred_equiv_asgn; auto. + unfold equiv_list_wf, Sorted; split. + replace pl_lt with (Basics.flip (Basics.flip (@pl_lt predicate))) by auto. + eapply sorted_rev4. eapply unnest_predicate_sorted; eauto. + apply Forall_rev; eapply unnest_predicate_lt_in_list2; eauto; lia. + apply Forall_rev; apply Forall_forall; intros. eapply ALLEQ. + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ Heqp0) as Y. eapply Forall_forall in Y; eauto. lia. + apply Forall_rev; apply Forall_forall; intros. simpl. + pose proof (unnest_predicate_lt_in_list _ _ _ _ _ Heqp0) as Y. eapply Forall_forall in Y; eauto. lia. + apply Forall_forall; intros. unfold check_root_index in CHKROOT3. + eapply forallb_forall in CHKROOT3; eauto. + lia. +Qed. + +Lemma mutate_eval_correct : + forall p a, + eval_hash_pred (mutate1_p p) a = eval_hash_pred p a. +Proof. + unfold mutate1_p; intros. + repeat (destruct_match; crush). +Qed. + +Theorem valid_check_smt_total : + forall p a, + check_smt_total p = true -> eval_hash_pred p a = Some true. +Proof. + unfold check_smt_total; intros. + destruct_match; auto using valid_check_smt. + rewrite <- mutate_eval_correct. + destruct_match; auto using valid_check_smt. + discriminate. +Qed. |