Module Qcert.Data.Model.DataLift


Require Import ZArith.
Require Import String.
Require Import List.
Require Import Utils.
Require Import ForeignData.
Require Import Data.

Section DataLift.

  Context {fdata:foreign_data}.

  Definition unbdbool (f:bool -> bool -> bool) (d1 d2:data) : option data :=
    match d1 with
    | dbool b1 =>
      match d2 with
      | dbool b2 => Some (dbool (f b1 b2))
      | _ => None
      end
    | _ => None
    end.

  Definition unudbool (f:bool -> bool) (d:data) : option data :=
    match d with
    | dbool b =>Some (dbool (f b))
    | _ => None
    end.

  Definition unbdnat (f:Z -> Z -> bool) (d1 d2:data) : option data :=
    match d1 with
    | dnat n1 =>
      match d2 with
      | dnat n2 => Some (dbool (f n1 n2))
      | _ => None
      end
    | _ => None
    end.

  Definition unbdata (f:data -> data -> bool) (d1 d2:data) : option data :=
    Some (dbool (f d1 d2)).

  Definition unndstring (f:string -> Z) (d1:data) : option data :=
    match d1 with
    | dstring s1 => (Some (dnat (f s1)))
    | _ => None
    end.

  Lemma unndstring_is_nat f d1 d2:
    unndstring f d1 = Some d2 -> exists z:Z, d2 = dnat z.
Proof.
    intros.
    destruct d1; simpl in *; try congruence.
    exists (f s); inversion H; reflexivity.
  Qed.

  Definition unsdstring (f:string -> string -> string) (d1 d2:data) : option data :=
    match d1 with
    | dstring s1 =>
      match d2 with
      | dstring s2 => Some (dstring (f s1 s2))
      | _ => None
      end
    | _ => None
    end.

  Lemma unsdstring_is_string f d1 d2 d3:
    unsdstring f d1 d2 = Some d3 -> exists s:string, d3 = dstring s.
Proof.
    intros.
    destruct d1; destruct d2; simpl in *; try congruence.
    exists (f s s0); inversion H; reflexivity.
  Qed.

  Definition ondcoll2 {A} : (list data -> list data -> A) -> data -> data -> option A.
Proof.
    intros f d1 d2.
    destruct d1.
    exact None. exact None. exact None. exact None. exact None.
    2: exact None. 2: exact None.
    2: exact None. 2: exact None.
    destruct d2.
    exact None. exact None. exact None.
    exact None. exact None. 2: exact None. 2: exact None. 2: exact None. 2: exact None.
    exact (Some (f l l0)).
    exact None.
    exact None.
  Defined.

  Definition rondcoll2 (f:(list data -> list data -> list data)) (d1 d2:data) : option data :=
    lift dcoll (ondcoll2 f d1 d2).

  Definition ondstring {A} (f : string -> A) (d : data) :=
    match d with
    | dstring n => Some (f n)
    | _ => None
    end.

  Definition ondnat {A} (f : Z -> A) (d : data) :=
    match d with
    | dnat n => Some (f n)
    | _ => None
    end.

  Definition ondfloat {A} (f : float -> A) (d : data) :=
    match d with
    | dfloat n => Some (f n)
    | _ => None
    end.

  Definition ondcoll {A} (f : list data -> A) (d : data) :=
    match d with
    | dcoll l => Some (f l)
    | _ => None
    end.

    Definition lift_oncoll {A} (f : list data -> option A) (d : data) :=
      match d with
      | dcoll l => f l
      | _ => None
      end.

    Definition lift_ondcoll2 (f:list data -> list data -> option (list data)) (d1 d2:data) : option data :=
      match d1,d2 with
      | dcoll l1, dcoll l2 => lift dcoll (f l1 l2)
      | _,_ => None
      end.

    Lemma lift_oncoll_dcoll {A} (f : list data -> option A) (dl : list data) :
      lift_oncoll f (dcoll dl) = f dl.
Proof.
      reflexivity.
    Qed.

    Lemma olift_on_lift_dcoll (f:list data->option data) (d1:option (list data)):
      olift (lift_oncoll f) (lift dcoll d1) =
      olift f d1.
Proof.
      destruct d1; reflexivity.
    Qed.

    Lemma olift_lift_dcoll (f:list data -> option (list data)) (d1:option (list data)):
      olift (fun c1 : list data => lift dcoll (f c1)) d1 =
      lift dcoll (olift f d1).
Proof.
      destruct d1; reflexivity.
    Qed.

    Lemma lift_dcoll_inversion d1 d2:
      d1 = d2 -> lift dcoll d1 = lift dcoll d2.
Proof.
      intros; rewrite H; reflexivity.
    Qed.

    Definition rondcoll (f:list data -> list data) (d:data) : option data :=
      lift dcoll (ondcoll f d).

    Lemma lift_dcoll_cons d l1 l2 :
      lift dcoll l1 = lift dcoll l2 ->
      lift dcoll (lift (fun t => d :: t) l1) = lift dcoll (lift (fun t => d :: t) l2).
Proof.
      intros.
      unfold lift in *.
      destruct l1; destruct l2; congruence.
    Qed.

    Lemma rondcoll2_dcoll f (l1 l2:list data):
      rondcoll2 f (dcoll l1) (dcoll l2) = Some (dcoll (f l1 l2)).
Proof.
reflexivity. Qed.
  
    Lemma rondcoll_dcoll f (l:list data):
      rondcoll f (dcoll l) = Some (dcoll (f l)).
Proof.
reflexivity. Qed.
  
    Lemma ondcoll_dcoll {A} (f:list data -> A) (l:list data):
      ondcoll f (dcoll l) = Some (f l).
Proof.
reflexivity. Qed.

    Lemma dcoll_map_app {A} (f:A -> data) (l1 l2:list A) :
      Some (dcoll (map f l1 ++ map f l2)) =
      rondcoll2 bunion (dcoll (map f l1)) (dcoll (map f l2)).
Proof.
reflexivity. Qed.

    Lemma lift_oncoll_ext {A : Type} (f g : list data -> option A) (d : data) :
      (forall a, d = dcoll a -> f a = g a) ->
      lift_oncoll f d = lift_oncoll g d.
Proof.
      destruct d; simpl; auto.
    Qed.

End DataLift.

Global Hint Rewrite @rondcoll2_dcoll : alg.
Global Hint Rewrite @rondcoll_dcoll : alg.
Global Hint Rewrite @ondcoll_dcoll : alg.
Global Hint Rewrite @lift_oncoll_dcoll : alg.
Global Hint Rewrite @olift_on_lift_dcoll : alg.
Global Hint Rewrite @olift_lift_dcoll : alg.
Global Hint Rewrite @lift_map_id : alg.
Global Hint Rewrite @lift_map_map : alg.
Global Hint Rewrite @lift_dcoll_cons : alg.