Module Qcert.Utils.OptimizerStep



Require Import String.
Require Import List.
Require Import Setoid.
Require Import Equivalence.
Require Import EquivDec.
Require Import CoqLibAdd.
Require Import ListAdd.

Section OptimizerStep.

  Record OptimizerStep (lang:Set) : Set :=
    mkOptimizerStep {
        optim_step_name : String.string
        ; optim_step_description : String.string
        ; optim_step_lemma : String.string
        ; optim_step_step (input:lang): lang
      }.
  
  Global Arguments mkOptimizerStep {lang} optim_step_name optim_step_description optim_step_lemma optim_step_step.
  Global Arguments optim_step_name {lang} o.
  Global Arguments optim_step_description {lang} o.
  Global Arguments optim_step_lemma {lang} o.
  Global Arguments optim_step_step {lang} o input.
  
  Record OptimizerStepModel {lang:Set} (R:relation lang) :=
    mkOptimizerStepModel {
        optim_step_model_step : OptimizerStep lang
        ; optim_step_model_step_correct (input:lang): R input (optim_step_step optim_step_model_step input)
      }.
  
  Global Arguments mkOptimizerStepModel {lang} {R} optim_step_model_step optim_step_model_step_correct.


  Definition optim_model_list_complete {lang:Set} {R:relation lang}
             (optim_list:list (OptimizerStep lang))
             (optim_correct_list :list (OptimizerStepModel R))
    := forall x,
      In x optim_list ->
      exists y,
        In y optim_correct_list
        /\ optim_step_model_step _ y = x.

  Definition optim_list_correct {lang:Set} (R:relation lang)
             (optim_list:list (OptimizerStep lang))
    := Forall (fun o => forall input, R input (optim_step_step o input)) optim_list.

  Lemma optim_list_correct_from_model {lang:Set} {R:relation lang}
        {optim_list:list (OptimizerStep lang)}
        {optim_correct_list :list (OptimizerStepModel R)} :
    optim_model_list_complete optim_list optim_correct_list ->
    optim_list_correct R optim_list.
Proof.
    unfold optim_model_list_complete, optim_list_correct.
    rewrite Forall_forall.
    intros pf ? inn.
    destruct (pf _ inn) as [cor [_ ?]].
    subst.
    apply optim_step_model_step_correct.
  Qed.
    

  Definition optim_list_distinct {lang:Set}
             (optim_list:list (OptimizerStep lang))
    := NoDup (map optim_step_name optim_list).

  Lemma optim_list_distinct_dec {lang:Set}
        (optim_list:list (OptimizerStep lang))
    : {optim_list_distinct optim_list} + {~ optim_list_distinct optim_list}.
Proof.
    apply NoDup_dec.
  Defined.

  Definition optim_list_distinct_holds {lang:Set}
             (optim_list:list (OptimizerStep lang))
    := holds (optim_list_distinct_dec optim_list).

  Lemma optim_list_distinct_prover {lang:Set}
        (optim_list:list (OptimizerStep lang))
        (pf:optim_list_distinct_holds optim_list) :
    optim_list_distinct optim_list.
Proof.
    generalize (optim_list_distinct_dec optim_list).
    unfold optim_list_distinct_holds, holds, is_true in *.
    match_destr_in pf.
  Qed.

  Fixpoint optim_lookup {lang:Set}
           (optim_list:list (OptimizerStep lang)) (name:string)
    : option (OptimizerStep lang)
    := match optim_list with
       | nil => None
       | opt::rest =>
         if optim_step_name opt == name
         then Some opt
         else optim_lookup rest name
       end.

  Lemma optim_lookup_name_correct {lang:Set}
           (optim_list:list (OptimizerStep lang)) (name:string) (o:OptimizerStep lang)
    : optim_lookup optim_list name = Some o -> optim_step_name o = name.
Proof.
    induction optim_list; simpl; intros eqq.
    - invcs eqq.
    - match_destr_in eqq.
      + invcs eqq; trivial.
      + auto.
  Qed.

  Lemma optim_lookup_some_in {lang:Set}
           (optim_list:list (OptimizerStep lang)) (name:string) (o:OptimizerStep lang)
    : optim_lookup optim_list name = Some o -> In o optim_list.
Proof.
    induction optim_list; simpl; intros eqq.
    - invcs eqq.
    - match_destr_in eqq.
      + invcs eqq; auto.
      + auto.
  Qed.

    Lemma optim_lookup_none_nin {lang:Set}
           (optim_list:list (OptimizerStep lang)) (name:string)
      : optim_lookup optim_list name = None ->
        forall o, In o optim_list -> optim_step_name o <> name.
Proof.
    induction optim_list; simpl; intros eqq.
    - intuition.
    - match_destr_in eqq.
      intros o [eqq2 | inn].
      + subst; intuition.
      + auto.
  Qed.

  Definition optim_lookup_as_list {lang:Set}
           (optim_list:list (OptimizerStep lang)) (name:string)
    : list (OptimizerStep lang)
    := match optim_lookup optim_list name with
       | None => nil
       | Some res => res::nil
       end.

  Definition valid_optims {lang:Set}
           (optim_list:list (OptimizerStep lang)) (names:list string) :
    list string * list string
    := partition
         (fun x => if optim_lookup optim_list x then true else false)
         names .
  
  Definition project_optims {lang:Set}
           (optim_list:list (OptimizerStep lang)) (names:list string)
    := flat_map (optim_lookup_as_list optim_list) names.

  Lemma project_optims_incl {lang:Set}
        (optim_list:list (OptimizerStep lang)) (names:list string) :
    incl (project_optims optim_list names) optim_list.
Proof.
    unfold project_optims.
    red; intros o inn.
    apply in_flat_map in inn.
    destruct inn as [name [inn inl]].
    unfold optim_lookup_as_list in inl.
    match_case_in inl; [intros ? eqq | intros eqq]
    ; rewrite eqq in inl; simpl in *; intuition.
    subst.
    apply optim_lookup_some_in in eqq; trivial.
  Qed.
        
  Lemma project_optims_model_list_complete {lang:Set} {R:relation lang}
        {optim_list:list (OptimizerStep lang)}
        {optim_correct_list:list (OptimizerStepModel R)}
        (pf:optim_model_list_complete optim_list optim_correct_list)
        (names:list string)
    : optim_model_list_complete
        (project_optims optim_list names)
        optim_correct_list.
Proof.
    red; intros o inn.
    apply project_optims_incl in inn.
    auto.
  Qed.

    Lemma project_optims_list_correct {lang:Set} {R:relation lang}
        {optim_list:list (OptimizerStep lang)}
        (pf:optim_list_correct R optim_list)
        (names:list string)
    : optim_list_correct R
        (project_optims optim_list names).
Proof.
      unfold optim_list_correct in *.
      rewrite Forall_forall in *.
      intros ? inn.
      apply project_optims_incl in inn.
      auto.
    Qed.
  
End OptimizerStep.

Ltac optim_correct_list_complete_try_pf_match pf step
  := match eval simpl in (optim_step_model_step _ pf) with
     | step => idtac
     end.

Ltac optim_correct_list_complete_pf_finder step l
  := match l with
     | (?pf = _) \/ ?rest =>
       (optim_correct_list_complete_try_pf_match pf step; instantiate (1:=pf); (split; tauto; simpl; trivial))
       || (optim_correct_list_complete_pf_finder step rest)
     | (?pf = _) => (optim_correct_list_complete_try_pf_match pf step; instantiate (1:=pf); (split; tauto; simpl; trivial))
     end.

Ltac optim_correct_list_complete_step steps
  := match type of steps with
     | ?step \/ ?rest =>
       destruct steps as [? | steps]
       ; [eexists
          ; match step with
              ?name = _ =>
              match goal with
                [|- ?l /\ ?pf ] =>
                optim_correct_list_complete_pf_finder name l
              end
            end | idtac]
     | False => inversion steps
     end.


Ltac optim_correct_list_complete_prover :=
  unfold optim_model_list_complete; simpl; intros ? steps
  ; repeat optim_correct_list_complete_step steps.