Require Import Arith.
Require FunctionalExtensionality.
Require Import List.
Import List.ListNotations.

Require Import mathcomp.ssreflect.ssreflect.
Set Bullet Behavior "Strict Subproofs".

Require Import StructTact.StructTactics.
Require Import StructTact.Util.

Require Import Chord.Chord.
Require Import Chord.HandlerLemmas.
Require Import Chord.SystemLemmas.

(* for all nodes, query = none -> no request or response in the network for node *)
(* for all nodes, query = some -> exactly one corresponding req or res in net *)
Definition request_for_query (gst : global_state) (src dst : addr) (q : query) (msg : payload) : Prop :=
  query_request q msg /\
  In (src, (dst, msg)) (msgs gst).

Definition response_for_query (gst : global_state) (src dst : addr) (q : query) (msg : payload) : Prop :=
  query_response q msg /\
  In (dst, (src, msg)) (msgs gst).

Definition query_delayed_at (dst : addr) (st : data) (src : addr) (msg : payload) : Prop :=
  In (src, msg) (delayed_queries st).

Inductive reachable_st : global_state -> Prop :=
| reachableInit :
    forall gst,
      initial_st gst ->
      reachable_st gst
| reachableStep :
    forall gst gst',
    reachable_st gst ->
    step_dynamic gst gst' ->
    reachable_st gst'.

Ltac induct_reachable_st :=
  match goal with
  | [H : reachable_st _ |- _ ] => prep_induction H; induction H
  end.

Inductive intermediate_reachable_st : global_state -> Prop :=
| intReachableInit :
    forall gst,
      initial_st gst ->
      intermediate_reachable_st gst
| intReachableStep :
    forall gst gst',
      intermediate_reachable_st gst ->
      step_dynamic gst gst' ->
      intermediate_reachable_st gst'
| intReachableDelayedQueries :
    forall gst h d res,
      In h (nodes gst) ->
      ~ In h (failed_nodes gst) ->
      sigma gst h = Some d ->
      do_delayed_queries h d = res ->
      intermediate_reachable_st (apply_handler_result h res [] gst).

Ltac induct_int_reachable_st :=
  match goal with
  | [H : intermediate_reachable_st _ |- _ ] => prep_induction H; induction H
  end.

Ltac induct type :=
  match goal with
  | [H : type _ |- _ ] => prep_induction H; induction H
  end.

Lemma reachable_intermediate_reachable :
  forall gst,
    reachable_st gst ->
    intermediate_reachable_st gst.
Proof using.
  intros.
  induct_reachable_st; econstructor; now eauto.
Qed.

(* transitive closure of best_succ *)
(* treat as opaque *)
Inductive reachable (gst : global_state) : addr -> addr -> Prop :=
| ReachableSucc : forall from to,
    best_succ gst from to ->
    reachable gst from to
| ReachableTransL : forall from x to,
    best_succ gst from x ->
    reachable gst x to ->
    reachable gst from to.

Ltac induct_reachable :=
  match goal with
  | H : reachable _ _ _ |- _ =>
    induction H
  end.

Lemma ReachableTransR : forall gst from x to,
    reachable gst from x ->
    best_succ gst x to ->
    reachable gst from to.
Proof using.
  intuition.
  induct_reachable.
  - eapply ReachableTransL.
    eauto.
    eapply ReachableSucc.
    eauto.
  - eauto using ReachableTransL.
Qed.

Lemma ReachableTrans : forall gst from x to,
    reachable gst from x ->
    reachable gst x to ->
    reachable gst from to.
Proof using.
  intros gst from x to H.
  generalize dependent to.
  induction H.
  - intuition.
    eauto using ReachableSucc, ReachableTransL.
  - intuition.
    eauto using ReachableSucc, ReachableTransL.
Qed.

(* treat as opaque *)
Definition ring_member (gst : global_state) (h : addr) : Prop :=
  reachable gst h h.
Hint Unfold ring_member.

(* If we have an open query at a live node, we have one of the following:
     - an appropriate request from src to dst
       and no other requests from src to dst
       and no responses from dst to src
       and no pending queries from src in the local state of dst
     - an appropriate response from dst to src
       and no requests from src to dst
       and no other responses from dst to src
       and no pending queries from src in the local state of dst
     - a corresponding pending query from src in the local state of dst
       and no requests from src to dst
       and no responses from dst to src *)
Definition request_in_transit (gst : global_state) (src dst : addr) (q : query) : Prop :=
  forall chan,
    chan = channel gst src dst ->
    exists req,
      query_request q req /\
      In req chan /\
      (* no other requests *)
      (forall m xs ys,
          chan = xs ++ req :: ys ->
          In m (xs ++ ys) ->
          request_payload m ->
          m = req) /\
      (* no responses *)
      (forall m,
          In m (channel gst dst src) ->
          ~ response_payload m) /\
      (forall m st,
          sigma gst dst = Some st ->
          ~ query_delayed_at dst st src m).

Definition response_in_transit (gst : global_state) (src dst : addr) (q : query) : Prop :=
  forall chan,
    chan = channel gst dst src ->
    exists res,
      query_response q res /\
      In res chan /\
      (* no other responses *)
      (forall m xs ys,
          chan = xs ++ res :: ys ->
          In m (xs ++ ys) ->
          response_payload m ->
          m = res) /\
      (* no requests *)
      (forall m,
          In m (channel gst src dst) ->
          ~ request_payload m) /\
      (forall m st,
          sigma gst dst = Some st ->
          ~ query_delayed_at dst st src m).

Definition pending_query (gst : global_state) (src dst : addr) (q : query) : Prop :=
  (forall st,
      exists m,
        sigma gst src = Some st ->
        query_delayed_at dst st src m) /\
  (forall m,
      In m (channel gst src dst) ->
      ~ request_payload m) /\
  (forall m,
      In m (channel gst dst src) ->
      ~ response_payload m).

Lemma coarse_reachable_characterization :
  forall from to gst gst',
    reachable gst from to ->
    nodes gst' = nodes gst ->
    failed_nodes gst' = failed_nodes gst ->
    sigma gst' = sigma gst ->
    reachable gst' from to.
Proof using.
  intuition.
  induction H;
    eauto using ReachableSucc, ReachableTransL, coarse_best_succ_characterization.
Qed.

Lemma adding_node_preserves_reachable :
  forall h from to gst gst' st,
    reachable gst from to ->
    ~ In h (nodes gst) ->
    nodes gst' = h :: nodes gst ->
    failed_nodes gst' = failed_nodes gst ->
    sigma gst' = update addr_eq_dec (sigma gst) h (Some st) ->
    reachable gst' from to.
Proof using.
  intuition.
  induction H;
    eauto using ReachableSucc, ReachableTransL, adding_nodes_does_not_affect_best_succ.
Qed.

Ltac break_best_succ :=
  match goal with
  | H : best_succ _ _ _ |- _ =>
    let x := fresh in
    pose proof H as x;
    unfold best_succ in x;
    break_exists;
    break_and
  end.

Lemma In_timeouts_in :
  forall t st,
    In t (timeouts_in st) ->
    exists dst q m,
      cur_request st = Some (dst, q, m) /\
      t = Request (addr_of dst) m.
Proof using.
  unfold timeouts_in.
  intros.
  repeat break_match.
  repeat eexists; eauto.
  match type of H with
  | In _ _ => inv H
  end.
  eauto.
  exfalso; auto with *.
  exfalso; auto with *.
Qed.

Definition chord_init_invariant (P : global_state -> Prop) :=
  forall gst,
    initial_st gst ->
    P gst.
Hint Unfold chord_init_invariant.

Definition chord_start_pre_post (P Q : global_state -> Prop) : Prop :=
  forall h gst gst' k st ms nts,
    reachable_st gst ->
    step_dynamic gst gst' ->

    ~ In h (nodes gst) ->
    ~ client_addr h ->
    start_constraint gst h ->
    In k (nodes gst) ->
    ~ In k (failed_nodes gst) ->
    start_handler h [k] = (st, ms, nts) ->

    nodes gst' = h :: nodes gst ->
    failed_nodes gst' = failed_nodes gst ->
    timeouts gst' = update addr_eq_dec (timeouts gst) h nts ->
    sigma gst' = update addr_eq_dec (sigma gst) h (Some st) ->
    msgs gst' = map (send h) ms ++ msgs gst ->
    trace gst' = trace gst ++ map e_send (map (send h) ms) ->

    P gst ->
    Q gst'.
Hint Unfold chord_start_pre_post.

Definition chord_start_invariant (P : global_state -> Prop) : Prop :=
  chord_start_pre_post P P.
Hint Unfold chord_start_invariant.

Definition chord_fail_pre_post (P Q : global_state -> Prop) : Prop :=
  forall h gst gst',
    reachable_st gst ->
    step_dynamic gst gst' ->

    In h (nodes gst) ->
    ~ In h (failed_nodes gst) ->
    failure_constraint gst h gst' ->

    nodes gst' = nodes gst ->
    failed_nodes gst' = h :: failed_nodes  gst ->
    timeouts gst' = timeouts gst ->
    sigma gst' = sigma gst ->
    msgs gst' = msgs gst ->
    trace gst' = trace gst ->

    P gst ->
    Q gst'.
Hint Unfold chord_fail_pre_post.

Definition chord_fail_invariant (P : global_state -> Prop) : Prop :=
  chord_fail_pre_post P P.
Hint Unfold chord_fail_invariant.

Definition chord_tick_pre_post (P Q : global_state -> Prop) : Prop :=
  forall gst gst' h st st' ms nts cts eff,
    reachable_st gst ->
    step_dynamic gst gst' ->

    In Tick (timeouts gst h) ->
    In h (nodes gst) ->
    ~ In h (failed_nodes gst) ->
    sigma gst h = Some st ->
    tick_handler h st = (st', ms, nts, cts, eff) ->

    nodes gst' = nodes gst ->
    failed_nodes gst' = failed_nodes gst ->
    timeouts gst' = update addr_eq_dec (timeouts gst) h
                           (nts ++ remove_all timeout_eq_dec cts
                                (remove timeout_eq_dec Tick (timeouts gst h))) ->
    sigma gst' = update addr_eq_dec (sigma gst) h (Some st') ->
    msgs gst' = (map (send h) ms) ++ msgs gst ->
    trace gst' = trace gst ++ [e_timeout h Tick] ->

    P gst ->
    Q gst'.
Hint Unfold chord_tick_pre_post.

Definition chord_tick_invariant (P : global_state -> Prop) : Prop :=
  chord_tick_pre_post P P.
Hint Unfold chord_tick_invariant.

Definition chord_keepalive_pre_post (P Q : global_state -> Prop) : Prop :=
  forall gst gst' h st st' ms nts cts eff,
    reachable_st gst ->
    step_dynamic gst gst' ->

    In KeepaliveTick (timeouts gst h) ->
    In h (nodes gst) ->
    ~ In h (failed_nodes gst) ->
    sigma gst h = Some st ->
    keepalive_handler st = (st', ms, nts, cts, eff) ->

    nodes gst' = nodes gst ->
    failed_nodes gst' = failed_nodes gst ->
    timeouts gst' = update addr_eq_dec (timeouts gst) h
                           (nts ++ remove_all timeout_eq_dec cts
                                (remove timeout_eq_dec KeepaliveTick (timeouts gst h))) ->
    sigma gst' = update addr_eq_dec (sigma gst) h (Some st') ->
    msgs gst' = (map (send h) ms) ++ msgs gst ->
    trace gst' = trace gst ++ [e_timeout h KeepaliveTick] ->

    P gst ->
    Q gst'.
Hint Unfold chord_keepalive_pre_post.

Definition chord_keepalive_invariant (P : global_state -> Prop) : Prop :=
  chord_keepalive_pre_post P P.
Hint Unfold chord_keepalive_invariant.

Definition chord_rectify_pre_post (P Q : global_state -> Prop) :=
  forall gst gst' h st st' ms nts cts eff,
    reachable_st gst ->
    step_dynamic gst gst' ->

    In RectifyTick (timeouts gst h) ->
    In h (nodes gst) ->
    ~ In h (failed_nodes gst) ->
    sigma gst h = Some st ->
    do_rectify h st = (st', ms, nts, cts, eff) ->

    nodes gst' = nodes gst ->
    failed_nodes gst' = failed_nodes gst ->
    timeouts gst' = update addr_eq_dec (timeouts gst) h
                           (nts ++ remove_all timeout_eq_dec cts
                                (remove timeout_eq_dec RectifyTick (timeouts gst h))) ->
    sigma gst' = update addr_eq_dec (sigma gst) h (Some st') ->
    msgs gst' = (map (send h) ms) ++ msgs gst ->
    trace gst' = trace gst ++ [e_timeout h RectifyTick] ->

    P gst ->
    Q gst'.
Hint Unfold chord_rectify_pre_post.

Definition chord_rectify_invariant (P : global_state -> Prop) :=
  chord_rectify_pre_post P P.
Hint Unfold chord_rectify_invariant.

Definition chord_request_pre_post (P Q : global_state -> Prop) : Prop :=
  forall gst gst' h st st' ms nts cts eff t dst req,
    reachable_st gst ->
    step_dynamic gst gst' ->

    t = Request dst req ->
    timeout_constraint gst h t ->
    In t (timeouts gst h) ->
    In h (nodes gst) ->
    ~ In h (failed_nodes gst) ->
    sigma gst h = Some st ->
    request_timeout_handler h st dst req = (st', ms, nts, cts, eff) ->

    nodes gst' = nodes gst ->
    failed_nodes gst' = failed_nodes gst ->
    timeouts gst' = update addr_eq_dec (timeouts gst) h
                           (nts ++ remove_all timeout_eq_dec cts
                                (remove timeout_eq_dec t (timeouts gst h))) ->
    sigma gst' = update addr_eq_dec (sigma gst) h (Some st') ->
    msgs gst' = (map (send h) ms) ++ msgs gst ->
    trace gst' = trace gst ++ [e_timeout h t] ->

    P gst ->
    Q gst'.
Hint Unfold chord_request_pre_post.

Definition chord_request_invariant (P : global_state -> Prop) : Prop :=
  chord_request_pre_post P P.
Hint Unfold chord_request_invariant.

Definition chord_recv_handler_pre_post (P Q : global_state -> Prop) : Prop :=
  forall gst gst' src h st p xs ys st' ms nts cts,
    reachable_st gst ->
    step_dynamic gst gst' ->

    recv_handler src h st p = (st', ms, nts, cts) ->
    msgs gst = xs ++ (src, (h, p)) :: ys ->
    In h (nodes gst) ->
    ~ In h (failed_nodes gst') ->
    sigma gst h = Some st ->

    nodes gst' = nodes gst ->
    failed_nodes gst' = failed_nodes gst ->
    timeouts gst' = update addr_eq_dec (timeouts gst) h (nts ++ remove_all timeout_eq_dec cts (timeouts gst h)) ->
    sigma gst' = update addr_eq_dec (sigma gst) h (Some st') ->
    msgs gst' = (map (send h) ms) ++ xs ++ ys ->
    trace gst' = trace gst ++ [e_recv (src, (h, p))] ->

    P gst ->
    Q gst'.
Hint Unfold chord_recv_handler_pre_post.

Definition chord_recv_handler_invariant (P : global_state -> Prop) : Prop :=
  chord_recv_handler_pre_post P P.
Hint Unfold chord_recv_handler_invariant.

Definition chord_input_pre_post (P Q : global_state -> Prop) :=
  forall gst gst' h i to m,
    reachable_st gst ->
    step_dynamic gst gst' ->

    gst' = update_msgs_and_trace gst (m :: msgs gst) (e_send m) ->
    client_addr h ->
    client_payload i ->
    m = send h (to, i) ->
    P gst ->
    Q gst'.
Hint Unfold chord_input_pre_post.

Definition chord_input_invariant (P : global_state -> Prop) :=
  chord_input_pre_post P P.
Hint Unfold chord_input_invariant.

Definition chord_output_pre_post (P Q : global_state -> Prop) :=
  forall gst gst' h xs m ys,
    reachable_st gst ->
    step_dynamic gst gst' ->

    gst' = update_msgs_and_trace gst (xs ++ ys) (e_recv m) ->
    client_addr h ->
    msgs gst = xs ++ m :: ys ->
    h = fst (snd m) ->
    P gst ->
    Q gst'.
Hint Unfold chord_output_pre_post.

Definition chord_output_invariant (P : global_state -> Prop) :=
  chord_output_pre_post P P.
Hint Unfold chord_output_invariant.

Theorem chord_net_invariant :
  forall P,
    chord_init_invariant P ->

    chord_start_invariant P ->
    chord_fail_invariant P ->

    chord_tick_invariant P ->
    chord_keepalive_invariant P ->
    chord_rectify_invariant P ->
    chord_request_invariant P ->

    chord_recv_handler_invariant P ->
    chord_input_invariant P ->
    chord_output_invariant P ->

    forall gst,
      reachable_st gst ->
      P gst.
Proof using.
  intros.
  match goal with
  | [H : reachable_st _ |- _] => induction H
  end.
  - now eapply_prop chord_init_invariant.
  - inv_prop step_dynamic; eauto.
    + destruct (start_handler _ _) as [[? ?] ?] eqn:?; eauto.
    + destruct t; unfold timeout_handler, timeout_handler_eff in *.
      * destruct (tick_handler _ _) as [[[? ?] ?] ?] eqn:?.
        simpl in *; tuple_inversion; eauto.
      * destruct (do_rectify _ _) as [[[? ?] ?] ?] eqn:?.
        simpl in *; tuple_inversion; eauto.
      * destruct (keepalive_handler _) as [[[? ?] ?] ?] eqn:?.
        simpl in *; tuple_inversion; eauto.
      * destruct (request_timeout_handler _ _ _ _) as [[[? ?] ?] ?] eqn:?.
        simpl in *; tuple_inversion; eauto.
    + destruct m as [? [? ?]]; eauto.
Qed.

Lemma chord_fail_pre_post_conj :
  forall Pre P Q,
    chord_fail_pre_post Pre P ->
    chord_fail_pre_post Pre Q ->
    chord_fail_pre_post Pre (fun gst => P gst /\ Q gst).
Proof.
  eauto.
Qed.
Hint Resolve chord_fail_pre_post_conj.

Lemma chord_fail_pre_post_weaken_l :
  forall Pre P Q,
    chord_fail_pre_post Pre (fun gst => P gst /\ Q gst) ->
    chord_fail_pre_post Pre P.
Proof.
  autounfold; intros.
  cut (P gst' /\ Q gst'); [tauto|eauto].
Qed.
Hint Resolve chord_fail_pre_post_weaken_l.

Lemma chord_fail_pre_post_weaken_r :
  forall Pre P Q,
    chord_fail_pre_post Pre (fun gst => P gst /\ Q gst) ->
    chord_fail_pre_post Pre Q.
Proof.
  autounfold; intros.
  cut (P gst' /\ Q gst'); [tauto|eauto].
Qed.
Hint Resolve chord_fail_pre_post_weaken_r.

Lemma chord_fail_pre_post_strengthen_l :
  forall P Q Post,
    chord_fail_pre_post P Post ->
    chord_fail_pre_post (fun gst => P gst /\ Q gst) Post.
Proof.
  autounfold; intros; intuition eauto.
Qed.
Hint Resolve chord_fail_pre_post_strengthen_l.

Lemma chord_fail_pre_post_strengthen_r :
  forall P Q Post,
    chord_fail_pre_post Q Post ->
    chord_fail_pre_post (fun gst => P gst /\ Q gst) Post.
Proof.
  autounfold; intros; intuition eauto.
Qed.
Hint Resolve chord_fail_pre_post_strengthen_r.

Lemma chord_start_pre_post_conj :
  forall Pre P Q,
    chord_start_pre_post Pre P ->
    chord_start_pre_post Pre Q ->
    chord_start_pre_post Pre (fun gst => P gst /\ Q gst).
Proof.
  eauto.
Qed.
Hint Resolve chord_start_pre_post_conj.

Lemma chord_start_pre_post_weaken_l :
  forall Pre P Q,
    chord_start_pre_post Pre (fun gst => P gst /\ Q gst) ->
    chord_start_pre_post Pre P.
Proof.
  autounfold; intros.
  cut (P gst' /\ Q gst'); [tauto|eauto].
Qed.
Hint Resolve chord_start_pre_post_weaken_l.

Lemma chord_start_pre_post_weaken_r :
  forall Pre P Q,
    chord_start_pre_post Pre (fun gst => P gst /\ Q gst) ->
    chord_start_pre_post Pre Q.
Proof.
  autounfold; intros.
  cut (P gst' /\ Q gst'); [tauto|eauto].
Qed.
Hint Resolve chord_start_pre_post_weaken_r.

Lemma chord_start_pre_post_strengthen_l :
  forall P Q Post,
    chord_start_pre_post P Post ->
    chord_start_pre_post (fun gst => P gst /\ Q gst) Post.
Proof.
  autounfold; intros; intuition eauto.
Qed.
Hint Resolve chord_start_pre_post_strengthen_l.

Lemma chord_start_pre_post_strengthen_r :
  forall P Q Post,
    chord_start_pre_post Q Post ->
    chord_start_pre_post (fun gst => P gst /\ Q gst) Post.
Proof.
  autounfold; intros; intuition eauto.
Qed.
Hint Resolve chord_start_pre_post_strengthen_r.

Lemma chord_init_invariant_weaken_l :
  forall P Q,
    chord_init_invariant (fun gst => P gst /\ Q gst) ->
    chord_init_invariant P.
Proof.
  simpl; autounfold; intros; find_apply_hyp_hyp; tauto.
Qed.
Hint Resolve chord_init_invariant_weaken_l.

Lemma chord_init_invariant_weaken_r :
  forall P Q,
    chord_init_invariant (fun gst => P gst /\ Q gst) ->
    chord_init_invariant Q.
Proof.
  simpl; autounfold; intros; find_apply_hyp_hyp; tauto.
Qed.
Hint Resolve chord_init_invariant_weaken_r.

Lemma recv_handler_post_conj :
  forall Pre P Q,
    chord_recv_handler_pre_post Pre P ->
    chord_recv_handler_pre_post Pre Q ->
    chord_recv_handler_pre_post Pre (fun gst => P gst /\ Q gst).
Proof.
  autounfold; eauto.
Qed.
Hint Resolve recv_handler_post_conj.

Lemma recv_handler_pre_strengthen_l :
  forall P Q Post,
    chord_recv_handler_pre_post P Post ->
    chord_recv_handler_pre_post (fun gst => P gst /\ Q gst) Post.
Proof.
  autounfold; intuition eauto.
Qed.
Hint Resolve recv_handler_pre_strengthen_l.

Lemma recv_handler_pre_strengthen_r :
  forall P Q Post,
    chord_recv_handler_pre_post P Post ->
    chord_recv_handler_pre_post (fun gst => Q gst /\ P gst) Post.
Proof.
  autounfold; intuition eauto.
Qed.
Hint Resolve recv_handler_pre_strengthen_r.


From iris.base_logic.lib Require Import invariants.

Section tests.
  Context `{invG Σ}.

  Program Definition test : (iProp Σ -n> iProp Σ) -n> (iProp Σ -n> iProp Σ) :=
    λne P v, (▷ (P v))%I.
  Solve Obligations with solve_proper.

End tests.


Require Export Task.model_code.
Import FloatIO.
Import DebugIO.
Import DScopeNotations.
Import ListNotations.
Import RecordSetNotations'.
Open Scope D_scope.

Definition process_one_event (ev: eventTy) (sim: simTy) :=
  let vars := sim.(vars) in
  let t := svGetFloat SvT vars in
  let dt_min := svGetFloat SvDT_MIN vars in
  let dt_max := svGetFloat SvDT_MAX vars in
  let et := ev.(time) in
  let etdelta := round ((et - t) * "1.e6"#D) in
  let minrnd := round (dt_min * "1.e6"#D) in
  if (etdelta <? minrnd) then
    let (sim2, new_time_opt) := handle_event model_handlers ev sim in
    match new_time_opt with
    | Some new_time => (ev[[time := new_time]], sim2)
    | None => (ev, sim2)
    end
  else
    (ev, sim).

Fixpoint process_events_helper (evs: list eventTy) (sim: simTy) :=
  match evs with
  | nil => (nil, sim)
  | cons ev evtl =>
    let (ev', sim') := process_one_event ev sim in
    let (evs', sim'') := process_events_helper evtl sim' in
    (ev' :: evs', sim'')
  end.

Fixpoint min_event_time (evs: list eventTy) (min_so_far: float) : float :=
  match evs with
  | nil => min_so_far
  | cons ev evtl =>
    let et := ev.(time) in
    let new_min :=
        if (et >? 0%D) && (et <? min_so_far) then
          et
        else
          min_so_far
    in
    min_event_time evtl new_min
  end.

Definition process_events (sim: simTy) : simTy :=
  let vars0 := sim.(vars) in
  let events := sim.(sim_events) in
  match events with
  | nil => set_var SvDT (svGetFloat SvDT_MAX vars0) sim
  | cons x x0 =>
    let t := svGetFloat SvT vars0 in
    let dt_max := svGetFloat SvDT_MAX vars0 in
    let (evs', sim1) := process_events_helper events sim in
    let vars' := sim1.(vars) in
    let min_time0 := svGetFloat SvT_STOP vars' in
    let min_time := min_event_time evs' min_time0 in
    let time_to_next_event := min_time - t in
    let new_dt :=
        if (time_to_next_event >? 0%D) && ((dt_max - time_to_next_event) >? epsilon) then
          time_to_next_event
        else
          dt_max
    in
    let sim3 := set_var SvDT new_dt sim1 in
    sim3[[sim_events := evs']]
  end.

Fixpoint advance_states (pairs: list (stateVar * stateVar)) (dt: float) (sim: simTy) : simTy :=
  match pairs with
  | nil => sim
  | cons (sv, svd) pairstl =>
    let vars0 := sim.(vars) in
    let el := svGetFloat sv vars0 in
    let eld := svGetFloat svd vars0 in
    let el' := el + eld * dt in
    let sim1 := set_var sv el' sim in
    advance_states pairstl dt sim1
  end.

Definition advance_model (sim: simTy) : simTy :=
  let vars0 := sim.(vars) in
  let dt := svGetFloat SvDT vars0 in
  let sim2 := advance_states modelPairs dt sim in
  let vars' := sim2.(vars) in
  let t := svGetFloat SvT vars' in
  let new_t := t + dt in
  let rounded_new_t := round (new_t * "1.e6"#D)/"1.e6"#D in
  set_var SvT rounded_new_t sim2.

Definition oneStep (sim: simTy) : simTy :=
  let sim1 := differential_equations sim in
  let sim2 := process_events sim1 in
  let sim3 :=
      if sim2.(flags).(evaluate_xd) then
        let sim4 := differential_equations sim2 in
        let flags4 := sim4.(flags) in
        let flags4' := flags4[[evaluate_xd := false]] in
        sim4[[flags := flags4']]
      else
        sim2 in
  advance_model sim3.

Require Import Zwf.
From compcert Require Import Coqlib.

Function run_sim_loop (steps: Z) (sim: simTy) { wf (Zwf 0%Z) steps } :=
  if zle 0%Z steps
  then
    let f := sim.(flags) in
    if f.(end_of_run) || f.(stop_simulation)
    then sim
    else
      let sim2 := (oneStep sim) in
      let steps2 := (steps - 1) in
      run_sim_loop steps2 sim2
  else sim.
Proof.
  intros; red; omega.
  apply Zwf_well_founded.
Qed.

Definition run_sim (sim: simTy) :=
  let vars := sim.(vars) in
  let dtmin := svGetFloat SvDT_MIN vars in
  let tstop := svGetFloat SvT_STOP vars in
  let max_steps_float := (tstop / dtmin)%D in
  let steps := ZofFloat max_steps_float in
  run_sim_loop steps sim.

Definition main (_ : unit) :=
  let sim1 := init_sim default_sim in
  let sim2 := run_sim sim1 in
  let sim3 := sim2[[solution ::= (fun sol => rev sol)]] in
  sim3[[log_entries ::= (fun lg => rev lg)]].


Require Import Coq.Vectors.Vector.
Require Import Coq.QArith.QArith_base.
Require Import Coq.QArith.QArith.
Require Import Coq.QArith.Qfield.
Require Import Coq.PArith.BinPos.
Require Import Coq.Arith.Arith.
Require Import Coq.Lists.List.
Require Import NPeano.
Require Import Program.

Require Import Shorthand.
Require Import Prefix.
Require Import Combi.
Require Import KraftList.
Require Import Transports.

(* mostly boilerplate *)

Local Open Scope Q_scope.

Function kraft_nvec {n} (vn : vec nat n) : Q :=
  fold 0 Qplus (Vmap (fun d =>
                        match d with
                          | 0%nat => 0%Q
                          | d' => 1 # (e2 d')
                        end) vn).

Function kraft_vec {n} (vc : vec LB n) : Q :=
  kraft_nvec (Vmap (ll(A:=bool)) vc).

Fixpoint list_of_nonnil_codes {n} (v : vec LB n) : list LB :=
  match v with
    | Vnil => nil
    | (Vcons h _ v') =>
      match h with
        | nil => list_of_nonnil_codes v'
        | h' => h' :: list_of_nonnil_codes v'
      end
  end.

Lemma list_of_nonnil_codes_does_not_contain_nil :
  forall {n} (a : LB) (v : vec LB n),
    In a (list_of_nonnil_codes v) -> a <> nil.
Proof.
  intros n a v H isnil.
  dependent induction n.
  assert (H0 : v = Vnil).
  apply vec_0_nil.
  rewrite -> H0 in H.
  inversion H.
  dependent destruction v.
  elim (list_eq_dec bool_dec a h).
  intros a_eq_h.
  replace (list_of_nonnil_codes (Vcons h n v))
  with (list_of_nonnil_codes v) in H.
  apply (IHn a v).
  apply H.
  apply isnil.
  rewrite <- a_eq_h.
  rewrite -> isnil.
  reflexivity.
  intros a_neq_h.
  assert (inor : h = a \/ (In a (list_of_nonnil_codes v))).
  apply in_inv.
  assert (calc : h :: list_of_nonnil_codes v = list_of_nonnil_codes (Vcons h n v)).
  destruct h.
  contradict a_neq_h.
  apply isnil.
  reflexivity.
  rewrite -> calc.
  apply H.
  elim inor.
  intros h_eq_a.
  contradict a_neq_h.
  symmetry.
  apply h_eq_a.
  intros incdr.
  apply (IHn a v).
  apply incdr.
  apply isnil.
Defined.

Lemma list_of_nonnil_codes_contains_everything' :
  forall {n} (a : LB) (v : vec LB n) na, Vnth v na = a -> a <> Bnil ->
                                         In a (list_of_nonnil_codes v).
Proof.
  induction n.
  intros q r na.
  inversion na.
  intros a v na eq nnil.
  dependent destruction v.
  dependent destruction na.
  assert (H : h = a).
  apply eq.
  rewrite -> H.
  destruct a.
  contradict nnil.
  reflexivity.
  apply in_eq.
  destruct h.
  apply (IHn _ _ na).
  auto.
  auto.
  apply in_cons.
  apply (IHn _ _ na).
  auto.
  auto.
Defined.

Lemma list_of_nonnil_codes_contains_everything :
  forall {n} (a : LB) (v : vec LB n),
    In a (list_of_nonnil_codes v) -> {na | Vnth v na = a}.
Proof.
  intros n a v i.
  induction n.
  dependent destruction v.
  inversion i.
  dependent destruction v.
  elim (list_eq_dec bool_dec a h).
  intros a_eq_h.
  exists(FinF1(n:=n)).
  rewrite -> a_eq_h.
  reflexivity.
  intros a_neq_h.
  assert(ind:{na : fin n | Vnth v na = a}).
  apply IHn.
  assert (inv:h = a \/ In a (list_of_nonnil_codes v)).
  destruct h.
  apply or_intror.
  apply i.
  apply in_inv.
  apply i.
  elim inv.
  intros h_eq_a.
  contradict a_neq_h.
  symmetry.
  apply h_eq_a.
  trivial.
  elim ind.
  intros x xu.
  exists (FinFS x).
  apply xu.
Defined.

Lemma list_of_nonnil_codes_is_dup_free' :
  forall {n} (v : vec LB n),
    (forall a b, a <> b -> ((Vnth v a) = nil) + ~ (prefix (Vnth v a) (Vnth v b))) ->
    dflist (list_of_nonnil_codes v).
Proof.
  intros n v pf.
  dependent induction v.
  apply dflist_nil.
  destruct h.
  apply IHv.
  intros a b neq.
  apply (pf (FinFS a) (FinFS b)).
  contradict neq.
  apply FS_inj.
  apply neq.
  assert (tmp1 :
            list_of_nonnil_codes (Vcons (b :: h) n v) =
            (b :: h) :: list_of_nonnil_codes v).
  reflexivity.
  rewrite -> tmp1.
  apply dflist_cons.
  apply IHv.
  intros a b0 neq.
  apply (pf (FinFS a) (FinFS b0)).
  contradict neq.
  apply FS_inj.
  apply neq.
  intros isin.
  assert (cont : {na | Vnth v na = (b :: h)}).
  apply list_of_nonnil_codes_contains_everything.
  apply isin.
  elim cont.
  intros na nan.
  assert (ispref:prefix (Vnth (Vcons (b :: h) n v) (FinFS na)) (Vnth (Vcons (b :: h) n v) FinF1)).
  exists Bnil.
  replace (Vnth (Vcons (b :: h) n v) (FinFS na)) with (Vnth v na).
  rewrite -> nan.
  replace (Vnth (Vcons (b :: h) n v) FinF1) with (b :: h).
  apply app_nil_r.
  reflexivity.
  reflexivity.
  assert (contr: ((Vnth (Vcons (b :: h) n v) (FinFS na)) = Bnil) + ~ prefix (Vnth (Vcons (b :: h) n v) (FinFS na))
                                                                        (Vnth (Vcons (b :: h) n v) FinF1)).
  apply pf.
  intros Q.
  inversion Q.
  elim contr.
  intros Q.
  inversion Q.
  rewrite -> nan in H0.
  inversion H0.
  contradict ispref.
  apply ispref.
Defined.

Lemma list_of_nonnil_codes_is_prefix_free' :
  forall {n} (v : vec LB n),
    (forall a b, a <> b -> ((Vnth v a) = nil) +
                           ~ (prefix (Vnth v a) (Vnth v b))) ->
    pflist (list_of_nonnil_codes v).
Proof.
  unfold pflist.
  intros n v pf a b H H0 H1 H2.
  assert (xna : {na | Vnth v na = a}).
  apply list_of_nonnil_codes_contains_everything.
  apply H.
  elim xna.
  intros na cdna.
  assert (xnb : {nb | Vnth v nb = b}).
  apply list_of_nonnil_codes_contains_everything.
  apply H0.
  elim xnb.
  intros nb cdnb.
  assert (cpf : (a = nil) + ~ (prefix a b)).
  rewrite <- cdna.
  rewrite <- cdnb.
  apply pf.
  contradict H1.
  rewrite <- cdna.
  rewrite <- cdnb.
  rewrite -> H1.
  reflexivity.
  elim cpf.
  apply (list_of_nonnil_codes_does_not_contain_nil a v H).
  contradict H2.
  apply H2.
Defined.

Lemma kraft_list_is_kraft_vec :
  forall {n} (v : vec LB n),
    kraft_list (list_of_nonnil_codes v) ==
    kraft_vec v.
Proof.
  intros n v.
  dependent induction n.
  dependent destruction v.
  reflexivity.
  dependent destruction v.
  destruct h.
  replace (list_of_nonnil_codes (Vcons Bnil n v)) with (list_of_nonnil_codes v).
  assert (tmp1 : kraft_vec (Vcons Bnil n v) == kraft_vec v).
  replace (kraft_vec (Vcons Bnil n v)) with (0 + kraft_vec v).
  ring.
  reflexivity.
  rewrite -> tmp1.
  apply IHn.
  reflexivity.
  assert (tmp1:kraft_list (list_of_nonnil_codes (Vcons (b :: h) n v)) =
               (1 # (e2 (ll (b :: h)))) + kraft_list (list_of_nonnil_codes v)).
  reflexivity.
  rewrite -> tmp1.
  assert (tmp2:kraft_vec (Vcons (b :: h) n v) = (1 # (e2 (ll (b :: h)))) + (kraft_vec v)).
  reflexivity.
  rewrite -> tmp2.
  rewrite -> IHn.
  reflexivity.
Defined.

Lemma kraft_nvec_positive : forall {n} (vn : vec nat n), kraft_nvec vn >= 0.
Proof.
  induction vn.
  replace (kraft_nvec Vnil) with 0.
  compute.
  intros Q.
  inversion Q.
  auto.
  elim (eq_nat_dec h 0%nat).
  intros h_0.
  rewrite -> h_0.
  assert (H : kraft_nvec (Vcons 0%nat n vn) = Qplus 0 (kraft_nvec vn)).
  assert (H1 : kraft_nvec (Vcons 0%nat n vn) = Qplus 0 (fold 0 Qplus (Vmap (fun d =>
                                                                                match d with
                                                                                | 0%nat => 0%Q
                                                                                | d' => 1 # (e2 d')
                                                                                end) vn))).
  auto.
  assert (H2 : kraft_nvec vn = fold 0 Qplus (Vmap (fun d =>
                                                     match d with
                                                       | 0%nat => 0%Q
                                                       | d' => 1 # (e2 d')
                                                     end) vn)).
  auto.
  rewrite -> H1.
  rewrite <- H2.
  reflexivity.
  rewrite -> H.
  assert (H' : 0 + 0 <= 0 + kraft_nvec vn).
  apply Qplus_le_compat.
  compute.
  intros Q.
  inversion Q.
  auto.
  auto.
  intros h_neq_0.
  induction h.
  contradict h_neq_0.
  auto.
  assert (H : kraft_nvec (Vcons (S h) n vn) = Qplus (1 # (e2 (S h))) (kraft_nvec vn)).
  assert (H1 : kraft_nvec (Vcons (S h) n vn) = Qplus (1 # (e2 (S h))) (fold 0 Qplus (Vmap (fun d =>
                                                                                match d with
                                                                                | 0%nat => 0%Q
                                                                                | d' => 1 # (e2 d')
                                                                                end) vn))).
  auto.
  assert (H2 : kraft_nvec vn = fold 0 Qplus (Vmap (fun d =>
                                                     match d with
                                                       | 0%nat => 0%Q
                                                       | d' => 1 # (e2 d')
                                                     end) vn)).
  auto.
  rewrite -> H1.
  rewrite <- H2.
  reflexivity.
  rewrite -> H.
  replace 0 with (Qplus 0 0).
  apply Qplus_le_compat.
  assert (H0 : 1 # e2 (S h) = / (Zpos (e2 (S h)) # 1)).
  auto.
  rewrite -> H0.
  apply Qinv_le_0_compat.
  compute.
  intros Q.
  inversion Q.
  auto.
  auto.
Defined.

Lemma kraft_nvec_inc : forall n (v1 v2 : vec nat n) (q : fin n) (r : nat),
                         (forall (b : fin n),
                            (b = q -> (Vnth v1 b = 0%nat
                                       /\ Vnth v2 b = (S r))) /\
                            (b <> q -> Vnth v2 b = Vnth v1 b)) ->
                         kraft_nvec v2 == (1 # (e2 (S r))) + kraft_nvec v1.
Proof.
  dependent induction n.
  intros v1 v2 q.
  inversion q.
  intros v1 v2 q r eq.
  dependent destruction v1.
  dependent destruction v2.
  dependent induction q.
  assert (h_eq_0 : h = 0%nat).
  replace h with (Vnth (Vcons h n v1) FinF1).
  apply (proj1 (eq FinF1) eq_refl).
  auto.
  rewrite -> h_eq_0.
  assert (h0_eq_sr : h0 = S r).
  replace h0 with (Vnth (Vcons h0 n v2) FinF1).
  apply (proj1 (eq FinF1) eq_refl).
  auto.
  rewrite -> h0_eq_sr.
  replace (kraft_nvec (Vcons (S r) n v2)) with ((1 # e2 (S r)) + kraft_nvec v2).
  replace (kraft_nvec (Vcons 0%nat n v1)) with (0 + kraft_nvec v1).
  replace v2 with v1.
  rewrite -> Qplus_assoc.
  rewrite -> Qplus_0_r.
  reflexivity.
  apply eq_nth_iff.
  intros p1 p2 peq.
  rewrite -> peq.
  replace (Vnth v1 p2) with (Vnth (Vcons h n v1) (FinFS p2)).
  replace (Vnth v2 p2) with (Vnth (Vcons h0 n v2) (FinFS p2)).
  assert (neq : FinFS p2 <> FinF1).
  intros Q.
  inversion Q.
  symmetry.
  apply (proj2 (eq (FinFS p2)) neq).
  auto.
  auto.
  auto.
  auto.

  assert(rec : kraft_nvec v2 == (1 # e2 (S r)) + kraft_nvec v1).
  apply (IHn _ _ q).
  intros b.
  elim (eq_fin_dec b q).
  intros b_eq_q.
  assert (fbq : FinFS b = FinFS q).
  f_equal.
  auto.
  elim (proj1 (eq (FinFS b)) fbq).
  intros H H0.
  split.
  auto.
  intros b_neq_q.
  contradict b_eq_q.
  auto.
  intros b_neq_q.
  assert (fbq : FinFS b <> FinFS q).
  contradict b_neq_q.
  apply FS_inj.
  auto.
  split.
  intros b_eq_q.
  contradict b_eq_q.
  auto.
  intros b_neq_q_2.
  apply (proj2 (eq (FinFS b)) fbq).
  assert (h_eq_h0 : h0 = h).
  assert (ffq : FinF1 <> FinFS q).
  intros Q.
  inversion Q.
  apply (proj2 (eq FinF1) ffq).
  rewrite <- h_eq_h0.
  replace (kraft_nvec (Vcons h0 n v2)) with ((fun d =>
                                                    match d with
                                                      | 0%nat => 0%Q
                                                      | d' => 1 # (e2 d')
                                                    end) h0 + kraft_nvec v2).
  replace (kraft_nvec (Vcons h0 n v1)) with ((fun d =>
                                                    match d with
                                                      | 0%nat => 0%Q
                                                      | d' => 1 # (e2 d')
                                                    end) h0 + kraft_nvec v1).
  rewrite -> rec.
  field.
  auto.
  auto.
Defined.

Lemma kraft_nvec_le : forall n (v1 v2 : vec nat n),
                        (forall (q : fin n), (Vnth v1 q = Vnth v2 q) \/ (Vnth v1 q = 0%nat)) -> kraft_nvec v1 <= kraft_nvec v2.
Proof.
  intros n v1 v2 imp.
  dependent induction n.
  replace v1 with (@Vector.nil nat).
  replace (kraft_nvec Vnil) with 0.
  apply kraft_nvec_positive.
  auto.
  symmetry.
  apply vec_0_nil.

  dependent destruction v1.
  dependent destruction v2.
  assert (h' : h = h0 \/ h = 0%nat).
  replace h with (Vnth (Vcons h n v1) FinF1).
  replace h0 with (Vnth (Vcons h0 n v2) FinF1).
  apply imp.
  auto.
  auto.
  elim (eq_nat_dec h0 0%nat).
  intros h0_eq_0.
  assert (h_eq_0 : h = 0%nat).
  elim h'.
  intros h_eq_h0.
  rewrite -> h_eq_h0.
  auto.
  auto.
  rewrite -> h_eq_0.
  rewrite -> h0_eq_0.
  replace (kraft_nvec (Vcons 0%nat n v1)) with (0 + kraft_nvec v1).
  replace (kraft_nvec (Vcons h0 n v2)) with (0 + kraft_nvec v2).
  apply Qplus_le_compat.
  compute.
  intros Q.
  inversion Q.
  apply IHn.
  intros q.
  elim (imp (FinFS q)).
  intros eq.
  apply or_introl.
  auto.
  intros zer.
  apply or_intror.
  auto.
  rewrite -> h0_eq_0.
  compute.
  auto.
  auto.
  intros h0_neq_0.
  elim h'.
  intros h_eq_h0.
  rewrite -> h_eq_h0.
  induction h0.
  contradict h0_neq_0.
  auto.
  replace (kraft_nvec (Vcons (S h0) n v1)) with ((1 # (e2 (S h0))) + kraft_nvec v1).
  replace (kraft_nvec (Vcons (S h0) n v2)) with ((1 # (e2 (S h0))) + kraft_nvec v2).
  apply Qplus_le_compat.
  apply Qle_refl.
  apply IHn.
  intros q.
  elim (imp (FinFS q)).
  auto.
  auto.
  compute.
  auto.
  compute.
  auto.
  intros h_eq_0.
  induction h0.
  contradict h0_neq_0.
  auto.
  rewrite -> h_eq_0.
  replace (kraft_nvec (Vcons 0%nat n v1)) with (0 + kraft_nvec v1).
  replace (kraft_nvec (Vcons (S h0) n v2)) with ((1 # e2 (S h0)) + kraft_nvec v2).
  apply Qplus_le_compat.
  replace (1 # e2 (S h0)) with (/ ((Zpos (e2 (S h0))) # 1)).
  apply Qinv_le_0_compat.
  compute.
  intros Q.
  inversion Q.
  auto.
  apply IHn.
  intros q.
  elim (imp (FinFS q)).
  auto.
  auto.
  compute.
  auto.
  compute.
  auto.
Defined.

Lemma nvec_id : forall {n m} (eq : n = m) q, kraft_nvec q = kraft_nvec (vec_id eq q).
Proof.
  intros n m eq q.
  dependent destruction eq.
  rewrite -> vec_id_destroy.
  reflexivity.
Defined.


Load LFindLoad.
From lfind Require Import LFind.
From QuickChick Require Import QuickChick.
From adtind Require Import goal33.

Derive Show for natural.

              Derive Arbitrary for natural.

              Instance Dec_Eq_natural : Dec_Eq natural.

              Proof. dec_eq. Qed.

Lemma conj25eqsynthconj6 : forall  (lv0 : natural) (lv1 : natural), (@eq natural (plus lv0 lv1) (plus lv1 lv0)).
Admitted.
QuickChick conj25eqsynthconj6.


Require Import VerdiRaft.Raft.
Require Import VerdiRaft.RaftRefinementInterface.

Section LeaderLogsSublogInterface.
  Context {orig_base_params : BaseParams}.
  Context {one_node_params : OneNodeParams orig_base_params}.
  Context {raft_params : RaftParams orig_base_params}.

  Definition leaderLogs_sublog (net : network) :=
    forall leader t ll e h,
      type (snd (nwState net leader)) = Leader ->
      In (t, ll) (leaderLogs (fst (nwState net h))) ->
      In e ll ->
      eTerm e = currentTerm (snd (nwState net leader)) ->
      In e (log (snd (nwState net leader))).

  Class leaderLogs_sublog_interface : Prop :=
    {
      leaderLogs_sublog_invariant :
        forall net,
          refined_raft_intermediate_reachable net ->
          leaderLogs_sublog net
    }.

End LeaderLogsSublogInterface.


Load LFindLoad.
From lfind Require Import LFind.
From QuickChick Require Import QuickChick.
From adtind Require Import goal33.

Derive Show for natural.

              Derive Arbitrary for natural.

              Instance Dec_Eq_natural : Dec_Eq natural.

              Proof. dec_eq. Qed.

Lemma conj14eqsynthconj4 : forall  (lv0 : natural) (lv1 : natural), (@eq natural (plus lv0 lv1) (plus lv1 (plus Zero lv0))).
Admitted.
QuickChick conj14eqsynthconj4.


Load LFindLoad.
From lfind Require Import LFind.
From QuickChick Require Import QuickChick.
From adtind Require Import goal33.

Derive Show for natural.

              Derive Arbitrary for natural.

              Instance Dec_Eq_natural : Dec_Eq natural.

              Proof. dec_eq. Qed.

Lemma conj11eqsynthconj6 : forall  (lv0 : natural) (lv1 : natural), (@eq natural (plus lv0 lv1) (plus lv1 lv0)).
Admitted.
QuickChick conj11eqsynthconj6.


Require Import Crypto.Specific.Framework.RawCurveParameters.
Require Import Crypto.Util.LetIn.

(***
Modulus : 2^137 - 13
Base: 34.25
***)

Definition curve : CurveParameters :=
  {|
    sz := 4%nat;
    base := 34 + 1/4;
    bitwidth := 64;
    s := 2^137;
    c := [(1, 13)];
    carry_chains := Some [seq 0 (pred 4); [0; 1]]%nat;

    a24 := None;
    coef_div_modulus := Some 2%nat;

    goldilocks := None;
    karatsuba := None;
    montgomery := false;
    freeze := Some true;
    ladderstep := false;

    mul_code := None;

    square_code := None;

    upper_bound_of_exponent_loose := None;
    upper_bound_of_exponent_tight := None;
    allowable_bit_widths := None;
    freeze_extra_allowable_bit_widths := None;
    modinv_fuel := None
  |}.

Ltac extra_prove_mul_eq _ := idtac.
Ltac extra_prove_square_eq _ := idtac.


Require Import Crypto.Specific.Framework.RawCurveParameters.
Require Import Crypto.Util.LetIn.

(***
Modulus : 2^369 - 25
Base: 46.125
***)

Definition curve : CurveParameters :=
  {|
    sz := 8%nat;
    base := 46 + 1/8;
    bitwidth := 64;
    s := 2^369;
    c := [(1, 25)];
    carry_chains := Some [seq 0 (pred 8); [0; 1]]%nat;

    a24 := None;
    coef_div_modulus := Some 2%nat;

    goldilocks := None;
    karatsuba := None;
    montgomery := false;
    freeze := Some true;
    ladderstep := false;

    mul_code := None;

    square_code := None;

    upper_bound_of_exponent_loose := None;
    upper_bound_of_exponent_tight := None;
    allowable_bit_widths := None;
    freeze_extra_allowable_bit_widths := None;
    modinv_fuel := None
  |}.

Ltac extra_prove_mul_eq _ := idtac.
Ltac extra_prove_square_eq _ := idtac.


Require Import Crypto.Specific.Framework.SynthesisFramework.
Require Import Crypto.Specific.solinas64_2e256m189_5limbs.CurveParameters.

Module P <: PrePackage.
  Definition package : Tag.Context.
  Proof. make_Synthesis_package curve extra_prove_mul_eq extra_prove_square_eq. Defined.
End P.

Module Export S := PackageSynthesis P.