Definition BDDzero := N0.Definition BDDone := Npos 1.Definition BDDstate := Map (BDDvar * (ad * ad)).Definition BDDsharing_map := Map (Map (Map ad)).Definition BDDfree_list := list ad.Definition BDDneg_memo := Map ad.Definition BDDor_memo := Map (Map ad).Definition BDDuniv_memo := Map (Map ad).Definition BDDconfig :=
  (BDDstate *
   (BDDsharing_map *
    (BDDfree_list * (ad * (BDDneg_memo * (BDDor_memo * BDDuniv_memo))))))%type.Definition initBDDstate := newMap (BDDvar * (ad * ad)).Definition initBDDsharing_map := newMap (Map (Map ad)).Definition initBDDfree_list := nil (A:=ad).Definition initBDDneg_memo := newMap ad.Definition initBDDor_memo := newMap (Map ad).Definition initBDDuniv_memo := newMap (Map ad).Definition initBDDconfig :=
  (initBDDstate,
  (initBDDsharing_map,
  (initBDDfree_list,
  (Npos 2, (initBDDneg_memo, (initBDDor_memo, initBDDuniv_memo)))))).Definition bs_node_height (bs : BDDstate) (node : ad) :=
  match MapGet _ bs node with
  | None => N0
  | Some (x, (l, r)) => ad_S x
  end.Definition node_height (cfg : BDDconfig) (node : ad) :=
  bs_node_height (fst cfg) node.Fixpoint bool_fun_of_BDD_1 (bs : BDDstate) (node : ad) 
 (bound : nat) {struct bound} : bool_fun :=
  match bound with
  | O => (* Error *)  bool_fun_zero
  | S bound' =>
      match MapGet _ bs node with
      | None => if Neqb node BDDzero then bool_fun_zero else bool_fun_one
      | Some (x, (l, r)) =>
          bool_fun_if x (bool_fun_of_BDD_1 bs r bound')
            (bool_fun_of_BDD_1 bs l bound')
      end
  end.Definition bool_fun_of_BDD_bs (bs : BDDstate) (node : ad) :=
  bool_fun_of_BDD_1 bs node (S (nat_of_N (bs_node_height bs node))).Definition bool_fun_of_BDD (cfg : BDDconfig) := bool_fun_of_BDD_bs (fst cfg).Definition nodes_preserved_bs (bs bs' : BDDstate) :=
  forall (x : BDDvar) (l r node : ad),
  MapGet _ bs node = Some (x, (l, r)) ->
  MapGet _ bs' node = Some (x, (l, r)).Definition nodes_preserved (cfg cfg' : BDDconfig) :=
  nodes_preserved_bs (fst cfg) (fst cfg').Inductive nodes_reachable (bs : BDDstate) : ad -> ad -> Prop :=
  | nodes_reachable_0 : forall node : ad, nodes_reachable bs node node
  | nodes_reachable_1 :
      forall (node node' l r : ad) (x : BDDvar),
      MapGet _ bs node = Some (x, (l, r)) ->
      nodes_reachable bs l node' -> nodes_reachable bs node node'
  | nodes_reachable_2 :
      forall (node node' l r : ad) (x : BDDvar),
      MapGet _ bs node = Some (x, (l, r)) ->
      nodes_reachable bs r node' -> nodes_reachable bs node node'.Inductive nodes_reachable (bs : BDDstate) : ad -> ad -> Prop :=
  | nodes_reachable_0 : forall node : ad, nodes_reachable bs node node
  | nodes_reachable_1 :
      forall (node node' l r : ad) (x : BDDvar),
      MapGet _ bs node = Some (x, (l, r)) ->
      nodes_reachable bs l node' -> nodes_reachable bs node node'
  | nodes_reachable_2 :
      forall (node node' l r : ad) (x : BDDvar),
      MapGet _ bs node = Some (x, (l, r)) ->
      nodes_reachable bs r node' -> nodes_reachable bs node node'.Inductive nodes_reachable (bs : BDDstate) : ad -> ad -> Prop :=
  | nodes_reachable_0 : forall node : ad, nodes_reachable bs node node
  | nodes_reachable_1 :
      forall (node node' l r : ad) (x : BDDvar),
      MapGet _ bs node = Some (x, (l, r)) ->
      nodes_reachable bs l node' -> nodes_reachable bs node node'
  | nodes_reachable_2 :
      forall (node node' l r : ad) (x : BDDvar),
      MapGet _ bs node = Some (x, (l, r)) ->
      nodes_reachable bs r node' -> nodes_reachable bs node node'.Inductive nodes_reachable (bs : BDDstate) : ad -> ad -> Prop :=
  | nodes_reachable_0 : forall node : ad, nodes_reachable bs node node
  | nodes_reachable_1 :
      forall (node node' l r : ad) (x : BDDvar),
      MapGet _ bs node = Some (x, (l, r)) ->
      nodes_reachable bs l node' -> nodes_reachable bs node node'
  | nodes_reachable_2 :
      forall (node node' l r : ad) (x : BDDvar),
      MapGet _ bs node = Some (x, (l, r)) ->
      nodes_reachable bs r node' -> nodes_reachable bs node node'.Definition node_preserved_bs (bs bs' : BDDstate) (node : ad) :=
  forall (x : BDDvar) (l r node' : ad),
  nodes_reachable bs node node' ->
  MapGet _ bs node' = Some (x, (l, r)) ->
  MapGet _ bs' node' = Some (x, (l, r)).Definition node_preserved (cfg cfg' : BDDconfig) :=
  node_preserved_bs (fst cfg) (fst cfg').Definition used_node_bs (bs : BDDstate) (ul : list ad) 
  (node : ad) :=
  exists node' : ad, In node' ul /\ nodes_reachable bs node' node.Definition used_node'_bs (bs : BDDstate) (ul : list ad) 
  (node : ad) := node = BDDzero \/ node = BDDone \/ used_node_bs bs ul node.Definition used_node (cfg : BDDconfig) := used_node_bs (fst cfg).Definition used_node' (cfg : BDDconfig) := used_node'_bs (fst cfg).Definition node_OK (bs : BDDstate) (node : ad) :=
  node = BDDzero \/ node = BDDone \/ in_dom _ node bs = true.Definition config_node_OK (cfg : BDDconfig) := node_OK (fst cfg).Definition no_new_node_bs (bs bs' : BDDstate) :=
  forall (x : BDDvar) (l r node : ad),
  MapGet _ bs' node = Some (x, (l, r)) ->
  MapGet _ bs node = Some (x, (l, r)).Definition no_new_node (cfg cfg' : BDDconfig) :=
  no_new_node_bs (fst cfg) (fst cfg').Inductive BDDbounded (bs : BDDstate) : ad -> BDDvar -> Prop :=
  | BDDbounded_0 : forall n : BDDvar, BDDbounded bs BDDzero n
  | BDDbounded_1 : forall n : BDDvar, BDDbounded bs BDDone n
  | BDDbounded_2 :
      forall (node : ad) (n x : BDDvar) (l r : ad),
      MapGet _ bs node = Some (x, (l, r)) ->
      BDDcompare x n = Datatypes.Lt ->
      Neqb l r = false ->
      BDDbounded bs l x -> BDDbounded bs r x -> BDDbounded bs node n.Inductive BDDbounded (bs : BDDstate) : ad -> BDDvar -> Prop :=
  | BDDbounded_0 : forall n : BDDvar, BDDbounded bs BDDzero n
  | BDDbounded_1 : forall n : BDDvar, BDDbounded bs BDDone n
  | BDDbounded_2 :
      forall (node : ad) (n x : BDDvar) (l r : ad),
      MapGet _ bs node = Some (x, (l, r)) ->
      BDDcompare x n = Datatypes.Lt ->
      Neqb l r = false ->
      BDDbounded bs l x -> BDDbounded bs r x -> BDDbounded bs node n.Inductive BDDbounded (bs : BDDstate) : ad -> BDDvar -> Prop :=
  | BDDbounded_0 : forall n : BDDvar, BDDbounded bs BDDzero n
  | BDDbounded_1 : forall n : BDDvar, BDDbounded bs BDDone n
  | BDDbounded_2 :
      forall (node : ad) (n x : BDDvar) (l r : ad),
      MapGet _ bs node = Some (x, (l, r)) ->
      BDDcompare x n = Datatypes.Lt ->
      Neqb l r = false ->
      BDDbounded bs l x -> BDDbounded bs r x -> BDDbounded bs node n.Inductive BDDbounded (bs : BDDstate) : ad -> BDDvar -> Prop :=
  | BDDbounded_0 : forall n : BDDvar, BDDbounded bs BDDzero n
  | BDDbounded_1 : forall n : BDDvar, BDDbounded bs BDDone n
  | BDDbounded_2 :
      forall (node : ad) (n x : BDDvar) (l r : ad),
      MapGet _ bs node = Some (x, (l, r)) ->
      BDDcompare x n = Datatypes.Lt ->
      Neqb l r = false ->
      BDDbounded bs l x -> BDDbounded bs r x -> BDDbounded bs node n.Definition BDD_OK (bs : BDDstate) (node : ad) :=
  match MapGet _ bs node with
  | None => node = BDDzero \/ node = BDDone
  | Some (n, _) => BDDbounded bs node (ad_S n)
  end.Definition BDDstate_OK (bs : BDDstate) :=
  MapGet _ bs BDDzero = None /\
  MapGet _ bs BDDone = None /\
  (forall a : ad, in_dom _ a bs = true -> BDD_OK bs a).Definition BDDsharing_OK (bs : BDDstate) (share : BDDsharing_map) :=
  forall (x : BDDvar) (l r a : ad),
  MapGet3 _ share l r x = Some a <-> MapGet _ bs a = Some (x, (l, r)).Definition BDDfree_list_OK (bs : BDDstate) (fl : BDDfree_list) 
  (cnt : ad) :=
  no_dup_list _ fl /\
  (forall node : ad,
   In node fl <->
   Nleb (Npos 2) node = true /\
   Nleb (ad_S node) cnt = true /\ MapGet _ bs node = None).Definition cnt_OK (bs : BDDstate) (cnt : ad) :=
  Nleb (Npos 2) cnt = true /\
  (forall a : ad, Nleb cnt a = true -> MapGet _ bs a = None).Definition BDDneg_memo_OK (bs : BDDstate) (negm : BDDneg_memo) :=
  forall node node' : ad,
  MapGet _ negm node = Some node' ->
  node_OK bs node /\
  node_OK bs node' /\
  Neqb (bs_node_height bs node') (bs_node_height bs node) = true /\
  bool_fun_eq (bool_fun_of_BDD_bs bs node')
    (bool_fun_neg (bool_fun_of_BDD_bs bs node)).Definition BDDor_memo_OK (bs : BDDstate) (orm : BDDor_memo) :=
  forall node1 node2 node : ad,
  MapGet2 _ orm node1 node2 = Some node ->
  node_OK bs node1 /\
  node_OK bs node2 /\
  node_OK bs node /\
  Nleb (bs_node_height bs node)
    (BDDvar_max (bs_node_height bs node1) (bs_node_height bs node2)) = true /\
  bool_fun_eq (bool_fun_of_BDD_bs bs node)
    (bool_fun_or (bool_fun_of_BDD_bs bs node1) (bool_fun_of_BDD_bs bs node2)).Definition BDDuniv_memo_OK (bs : BDDstate) (um : BDDuniv_memo) :=
  forall (x : BDDvar) (node node' : ad),
  MapGet2 _ um node x = Some node' ->
  node_OK bs node /\
  node_OK bs node' /\
  Nleb (bs_node_height bs node') (bs_node_height bs node) = true /\
  bool_fun_eq (bool_fun_of_BDD_bs bs node')
    (bool_fun_forall x (bool_fun_of_BDD_bs bs node)).Definition BDDconfig_OK (cfg : BDDconfig) :=
  BDDstate_OK (fst cfg) /\
  BDDsharing_OK (fst cfg) (fst (snd cfg)) /\
  BDDfree_list_OK (fst cfg) (fst (snd (snd cfg))) (fst (snd (snd (snd cfg)))) /\
  cnt_OK (fst cfg) (fst (snd (snd (snd cfg)))) /\
  BDDneg_memo_OK (fst cfg) (fst (snd (snd (snd (snd cfg))))) /\
  BDDor_memo_OK (fst cfg) (fst (snd (snd (snd (snd (snd cfg)))))) /\
  BDDuniv_memo_OK (fst cfg) (snd (snd (snd (snd (snd (snd cfg)))))).Definition used_list_OK_bs (bs : BDDstate) (ul : list ad) :=
  forall node : ad, In node ul -> node_OK bs node.Definition used_list_OK (cfg : BDDconfig) := used_list_OK_bs (fst cfg).Definition used_nodes_preserved_bs (bs bs' : BDDstate) 
  (ul : list ad) :=
  forall node : ad, In node ul -> node_preserved_bs bs bs' node.Definition used_nodes_preserved (cfg cfg' : BDDconfig) :=
  used_nodes_preserved_bs (fst cfg) (fst cfg').Definition gc_OK (gc : BDDconfig -> list ad -> BDDconfig) :=
  forall (cfg : BDDconfig) (ul : list ad),
  BDDconfig_OK cfg ->
  used_list_OK cfg ul ->
  BDDconfig_OK (gc cfg ul) /\
  used_nodes_preserved cfg (gc cfg ul) ul /\ no_new_node cfg (gc cfg ul).

(* Specifications: *)
Lemma initBDDstate_OK : BDDstate_OK initBDDstate.
Lemma initBDDsharing_map_OK : BDDsharing_OK initBDDstate initBDDsharing_map.
Lemma initBDDfree_list_OK : BDDfree_list_OK initBDDstate initBDDfree_list (Npos 2).
Lemma initBDDneg_memo_OK : forall bs : BDDstate, BDDneg_memo_OK bs initBDDneg_memo.
Lemma initBDDor_memo_OK : forall bs : BDDstate, BDDor_memo_OK bs initBDDor_memo.
Lemma initBDDuniv_memo_OK : forall bs : BDDstate, BDDuniv_memo_OK bs initBDDuniv_memo.
Lemma initBDDconfig_OK : BDDconfig_OK initBDDconfig.
Lemma config_OK_zero : forall cfg : BDDconfig, BDDconfig_OK cfg -> MapGet _ (fst cfg) BDDzero = None.
Lemma config_OK_one : forall cfg : BDDconfig, BDDconfig_OK cfg -> MapGet _ (fst cfg) BDDone = None.
Lemma zero_OK : forall cfg : BDDconfig, config_node_OK cfg BDDzero.
Lemma one_OK : forall cfg : BDDconfig, config_node_OK cfg BDDone.
Lemma node_height_zero : forall cfg : BDDconfig, BDDconfig_OK cfg -> Neqb (node_height cfg BDDzero) N0 = true.
Lemma node_height_one : forall cfg : BDDconfig, BDDconfig_OK cfg -> Neqb (node_height cfg BDDone) N0 = true.
Lemma nodes_preserved_bs_trans : forall bs1 bs2 bs3 : BDDstate, nodes_preserved_bs bs1 bs2 -> nodes_preserved_bs bs2 bs3 -> nodes_preserved_bs bs1 bs3.
Lemma nodes_preserved_bs_refl : forall bs : BDDstate, nodes_preserved_bs bs bs.
Lemma nodes_preserved_trans : forall cfg1 cfg2 cfg3 : BDDconfig, nodes_preserved cfg1 cfg2 -> nodes_preserved cfg2 cfg3 -> nodes_preserved cfg1 cfg3.
Lemma nodes_preserved_refl : forall cfg : BDDconfig, nodes_preserved cfg cfg.
Lemma increase_bound : forall (bs : BDDstate) (n n' : BDDvar) (node : ad), BDDbounded bs node n -> BDDcompare n n' = Datatypes.Lt -> BDDbounded bs node n'.
Lemma nodes_preserved_bounded : forall (bs bs' : BDDstate) (n : BDDvar) (node : ad), nodes_preserved_bs bs bs' -> BDDbounded bs node n -> BDDbounded bs' node n.
Lemma BDDbounded_lemma : forall (bs : BDDstate) (node : ad) (n : BDDvar), BDDbounded bs node n -> node = BDDzero \/ node = BDDone \/ (exists x : BDDvar, (exists l : BDDvar, (exists r : BDDvar, MapGet _ bs node = Some (x, (l, r)) /\ BDDcompare x n = Datatypes.Lt /\ Neqb l r = false /\ BDDbounded bs l x /\ BDDbounded bs r x))).
Lemma BDD_OK_node_OK : forall (bs : BDDstate) (node : ad), BDD_OK bs node -> node_OK bs node.
Lemma node_OK_BDD_OK : forall (bs : BDDstate) (node : ad), BDDstate_OK bs -> node_OK bs node -> BDD_OK bs node.
Lemma bs_node_height_left : forall (bs : BDDstate) (node l r : ad) (x : BDDvar), BDDstate_OK bs -> MapGet _ bs node = Some (x, (l, r)) -> BDDcompare (bs_node_height bs l) (bs_node_height bs node) = Datatypes.Lt.
Lemma bs_node_height_right : forall (bs : BDDstate) (node l r : ad) (x : BDDvar), BDDstate_OK bs -> MapGet _ bs node = Some (x, (l, r)) -> BDDcompare (bs_node_height bs r) (bs_node_height bs node) = Datatypes.Lt.
Lemma internal_node_lemma : forall (bs : BDDstate) (x : BDDvar) (l r node : ad), BDDstate_OK bs -> MapGet _ bs node = Some (x, (l, r)) -> Neqb l r = false /\ BDDbounded bs l x /\ BDDbounded bs r x.
Lemma high_bounded : forall (bs : BDDstate) (x : BDDvar) (l r node : ad), BDDstate_OK bs -> MapGet _ bs node = Some (x, (l, r)) -> BDDbounded bs r x.
Lemma low_bounded : forall (bs : BDDstate) (x : BDDvar) (l r node : ad), BDDstate_OK bs -> MapGet _ bs node = Some (x, (l, r)) -> BDDbounded bs l x.
Lemma BDDbounded_node_OK : forall (bs : BDDstate) (node : ad) (n : BDDvar), BDDbounded bs node n -> node_OK bs node.
Lemma high_OK : forall (bs : BDDstate) (x : BDDvar) (l r node : ad), BDDstate_OK bs -> MapGet _ bs node = Some (x, (l, r)) -> node_OK bs r.
Lemma low_OK : forall (bs : BDDstate) (x : BDDvar) (l r node : ad), BDDstate_OK bs -> MapGet _ bs node = Some (x, (l, r)) -> node_OK bs l.
Lemma low_high_neq : forall (cfg : BDDconfig) (x : BDDvar) (l r node : ad), BDDconfig_OK cfg -> MapGet _ (fst cfg) node = Some (x, (l, r)) -> Neqb l r = false.
Lemma bs_node_height_left_le : forall bs : BDDstate, BDDstate_OK bs -> forall (x : BDDvar) (l r node : ad), MapGet _ bs node = Some (x, (l, r)) -> Nleb (bs_node_height bs l) x = true.
Lemma bs_node_height_right_le : forall bs : BDDstate, BDDstate_OK bs -> forall (x : BDDvar) (l r node : ad), MapGet _ bs node = Some (x, (l, r)) -> Nleb (bs_node_height bs r) x = true.
Lemma no_duplicate_node : forall (bs : BDDstate) (share : BDDsharing_map), BDDstate_OK bs -> BDDsharing_OK bs share -> forall (x : BDDvar) (l r node1 node2 : ad), MapGet _ bs node1 = Some (x, (l, r)) -> MapGet _ bs node2 = Some (x, (l, r)) -> node1 = node2.
Lemma int_node_gt_1 : forall (bs : BDDstate) (node : ad), BDDstate_OK bs -> in_dom _ node bs = true -> Nleb (Npos 2) node = true.
Lemma int_node_lt_cnt : forall (bs : BDDstate) (cnt node : ad), cnt_OK bs cnt -> in_dom _ node bs = true -> Nleb (ad_S node) cnt = true.
Lemma nodes_preserved_bs_node_OK : forall (bs1 bs2 : BDDstate) (node : ad), nodes_preserved_bs bs1 bs2 -> node_OK bs1 node -> node_OK bs2 node.
Lemma nodes_preserved_config_node_OK : forall (cfg1 cfg2 : BDDconfig) (node : ad), nodes_preserved cfg1 cfg2 -> config_node_OK cfg1 node -> config_node_OK cfg2 node.
Lemma nodes_preserved_bs_node_height_eq : forall (bs1 bs2 : BDDstate) (node : ad), nodes_preserved_bs bs1 bs2 -> BDDstate_OK bs1 -> BDDstate_OK bs2 -> node_OK bs1 node -> Neqb (bs_node_height bs2 node) (bs_node_height bs1 node) = true.
Lemma nodes_preserved_node_height_eq : forall (cfg1 cfg2 : BDDconfig) (node : ad), BDDconfig_OK cfg1 -> BDDconfig_OK cfg2 -> nodes_preserved cfg1 cfg2 -> config_node_OK cfg1 node -> Neqb (node_height cfg2 node) (node_height cfg1 node) = true.