Load loadpath.
Require Import ssreflect ssrbool ssrnat ssrfun eqtype seq fintype finfun tuple 
               path.
Require Import prelude heaps rels hprop domains ordtype finmap secmod seclog 
               datatypes array.
Set Implicit Arguments.
Unset Strict Implicit.
Import Prenex Implicits.

(* general lems about options *)

Section Option.
Variables (T : Type) (t0 : T).

Definition isNone (o : option T) := if o is None then true else false.

Definition isSome (o : option T) := if o is Some _ then True else False.

Definition isSome_pred (o : option T) := if o is Some _ then true else false.

Definition getSome (o : option T) (pf : isSome o) : T :=
  (match o as o' return isSome o' -> T with
   | None => fun pf => match pf with end
   | Some v => fun pf => v
   end) pf. 

Lemma val_getSome o t (pf : isSome o) : o = Some t <-> getSome pf = t.
Proof. 
split; first by case: o pf=>// a pf; case=> ->. 
by case:o pf=>// a pf; case=> ->. 
Qed.

(* getSome with a default value, needed in RHTT programs below *)

Definition getSome0 (o : option T) : T := if o is Some t then t else t0.

Lemma val_getSome0 o t (pf : isSome o) : o = Some t <-> getSome0 o = t.
Proof. 
split; first by case: o pf=>// a pf; case=> ->. 
by case:o pf=>// a pf; case=> ->. 
Qed.

End Option.

Section Min.
Variables (K : ordType).

(* k lower-bounds the elements in s *)

Definition lowerb (k : K) (s : seq K) := 
  forall k', k' \in s -> k = k' \/ ord k k'.

Lemma lowerb_cons k k' s : 
  lowerb k (k' :: s) <-> (k = k' \/ ord k k') /\ lowerb k s.
Proof.
split; [rewrite/==>A; case:(A k'); first by rewrite in_cons; apply/orP; left|].
move=><-; split; first by left.
move=>k1 B; apply:A; by rewrite in_cons; apply/orP; right. split; first by right. 
move=>k1 B; apply:A; by rewrite in_cons; apply/orP; right. 
move=>[A B]; rewrite/==>k1; rewrite in_cons; move/orP.
case; first by move/eqP=>->. by move=>?; apply: B.
Qed.

Lemma ord_lowerb k k' s : lowerb k s -> ord k' k -> lowerb k' s.
Proof.
elim:s k k'=>//k1 s' IH k k'; case/lowerb_cons=> A B C. 
case:A=>[<-|A]; rewrite /lowerb=> k2; rewrite in_cons; case/orP.
by case/eqP=> ->; right. by apply: (IH _ _ B C). 
by case/eqP=> ->; right; apply: (@trans _ k). 
by apply: (IH _ _ B C).
Qed.

Fixpoint min' (s : seq K) (k0 : K) := 
  if s is k :: s' then if ord k k0 then min' s' k else min' s' k0
  else k0.

Lemma min_base' s x y : x \in s -> ord x y -> min' s x = min' s y.
Proof.
elim: s x y=>[|a s IH] x y //.
rewrite inE.
case: eqP=>[|N R] /=; first by move=><- _ O; rewrite O irr.
case: (totalP a x)=>H1 H2.
- by rewrite (trans H1 H2).
- by rewrite (eqP H1) in N.
by case: ifP=>// _; [apply: IH H1 | apply: IH H2].
Qed.

Lemma minP' s1 s2 k : s1 =i s2 -> min' s1 k = min' s2 k.
Proof.
have min_base2: forall s x a, ord a x -> 
  min' (filter [pred z | ord z x] s) a = min' s a.
- elim=>[|b s IH] x a O1 //=.
  case: ifP=>O2 /=; rewrite IH //; first by rewrite IH.
  by case: ifP=>O3 //=; rewrite (trans O3 O1) in O2.
have min_base3: forall s a, min' (filter [pred z | ord z a] s) a = min' s a.
- elim=>[|b s IH] a //=.
  case: ifP=>O /=; last by apply: IH.
  by rewrite O; apply: min_base2.
elim: s1 s2 k=>[|x1 s1 IH] /=.
- by case=>[//|x2 s2] k; move/(_ x2); rewrite inE eq_refl.
move=>s2 k E.
case T: (x1 \in s1).
- have: s1 =i s2 by move=>x; rewrite -E inE; case: eqP=>// ->.
  by move/IH=><-; case: ifP=>O //; apply: min_base'.
have: s1 =i filter (predC (pred1 x1)) s2.
- by move=>x; rewrite mem_filter /= -E inE; case: eqP=>// ->.
have {T} T: x1 \in s2 by rewrite -E inE eq_refl.
move/IH=>T'. 
case: ifP=>O1; rewrite T'.
- rewrite -(min_base' T O1) -(min_base3 s2) -(min_base3) -filter_predI.
  congr min'; apply: eq_filter.
  by move=>z /=; rewrite eq_sym; case: totalP.
rewrite -(min_base3 s2 k) -(min_base3 _ k) -filter_predI.
congr min'; apply: eq_filter.
move=>z /=; rewrite eq_sym; case: totalP=>//=.
by case: eqP=>//= <-; rewrite O1.
Qed.

Definition minn' (x1 x2 : K) := if ord x1 x2 then x1 else x2.

Lemma minnC' (x1 x2 : K) : minn' x1 x2 = minn' x2 x1.
Proof. by rewrite /minn'; case: totalP=>//; move/eqP. Qed.

Lemma minnCA' (x1 x2 x3 : K) : 
        minn' x1 (minn' x2 x3) = minn' x2 (minn' x1 x3).
Proof.
rewrite /minn'; case: (totalP x2 x3)=>O23; last first.
- case O13: (ord x1 x3); first by case: totalP (trans O13 O23).
  by case: totalP O23.
- rewrite {O23}(eqP O23).
  by case O13: (ord x1 x3); [case: totalP O13 | rewrite irr].
case: (totalP x1 x2)=>O12.
- by rewrite (trans O12 O23); case: totalP O12.
- by rewrite (eqP O12) O23 irr.
by case: (ord x1 x3); [rewrite O12| rewrite O23].
Qed.

Lemma min_ord s k0 k : min' s k0 = k -> k = k0 \/ ord k k0. 
Proof. 
elim: s k0 k=>//=; first by left. move=> k' s' IH k0 k.
case A: (ord k' k0)=> B. move: (IH _ _ B)=> [->|C]; first by right. 
by right; apply (@trans _ k'). by apply: (IH _ _ B).
Qed.

Lemma min_ordb s x : (x == min' s x) || (ord (min' s x) x).
Proof.
case: (min_ord (erefl (min' s x)))=>->.
- by rewrite eq_refl.
by rewrite orbT.
Qed.

Lemma min_in' s x : ord (min' s x) x -> min' s x \in s.
Proof.
elim: s x=>[|a s IH] x /=; first by rewrite irr.
case O1: (ord a x); last by move/IH; rewrite inE=>->; rewrite orbT.
move=>_; rewrite inE.
case/orP: (min_ordb s a).
- by move/eqP=><-; rewrite eq_refl.
by move/IH=>->; rewrite orbT.
Qed.

Lemma min_minn' x s : minn' x (min' s x) = min' s x.
Proof.
rewrite /minn'; case: ifP=>// O.
case: (min_ord (erefl (min' s x)))=>//.
by case: totalP O.
Qed.

Lemma minn_min' (s : seq K) x1 x2 : 
        ord x1 x2 -> min' s x1 = minn' x1 (min' s x2).
Proof.
elim: s x1 x2=>[|a s IH] x1 x2 /=; first by rewrite /minn'; move=>->.
move=>O1; case: (totalP a x1)=>O2.
- by rewrite (trans O2 O1) (IH _ _ O2) minnCA' min_minn'.
- by rewrite {O2}(eqP O2)O1 min_minn'.
by case: ifP=>O3; rewrite -IH.
Qed.

Lemma min_min' s x : min' s (min' s x) = min' s x.
Proof.
move: (min_ordb s x).
case/orP; first by move/eqP=>E; rewrite -E.
by move=>E; move/min_in': (E)=>E1; apply: min_base' E1 E.
Qed.

(* reassociation for min' *)

Lemma min_cons' x y s : min' (x :: s) y = minn' y (min' s x).
Proof.
rewrite /minn' /=; case: totalP=>O.
- by apply: minn_min' O.
- by rewrite (eqP O) -{1}min_minn'.
case/orP: (min_ordb s x)=>H.
- by rewrite -(eqP H); case: totalP O.
by case: totalP (trans H O).
Qed.

Lemma neg_ord (k k' : K) : ~~ ord k k' -> k' = k \/ ord k' k.
Proof.
case: (total k k'); case/orP=> A; move/negP=> B; first by exfalso.
move: A; case/orP; move/eqP=> A; first by left. 
by move: A; move/eqP; right.
Qed.

Lemma min'_lowerb s k0 k : min' s k0 = k -> lowerb k s.
Proof.
elim: s k k0=>// k' s' IH k k0 /= A; rewrite lowerb_cons.
move: A; case B: (ord k' k0). 
by move=> C; move: (IH _ _ C); split=>//; apply: (min_ord C).
move=> A; move: (IH _ _ A)=> C; split=>//; move: (min_ord A)=> [->|D]. 
by apply: neg_ord; apply/negP; congruence.
apply: neg_ord; apply/negP=> E. have F: ord k' k0 by apply: (@trans _ k). 
by congruence.
Qed.

Definition min (s : seq K) : option K := 
  if s is k :: s' then Some (min' s' k) else None.

Lemma min_lowerb s k : min s = Some k -> lowerb k s.
Proof.
rewrite /min; case: s=>// k0 s'; case=> A. 
move: (min'_lowerb A); rewrite /lowerb=> B /= k1 C.
move: (min_ord A)=> D; rewrite in_cons in C; move: C; move/orP; case.
by move/eqP=> ->. by move=> E; apply: (B _ E).
Qed.

Lemma min_in s k : min s = Some k -> k \in s.
Proof.
case: s k=>//= k s' k'; case.
elim: s' k=>//= [_ -> | k1 s1 IH k]; first by rewrite in_cons; apply/orP; left.
case: (ord k1 k)=> A. move: (IH _ A). rewrite 3!in_cons.
case/orP. case/eqP=> ->. by apply/orP; right; apply/orP; left. 
by move=> B; apply/orP; right; apply/orP; right.
move: (IH _ A). rewrite 3!in_cons. case/orP. case/eqP=> ->. by apply/orP; left.
by move=> B; apply/orP; right; apply/orP; right.
Qed.

Lemma min_none s : min s = None -> s = [::].
Proof. by case: s. Qed.

(* reassociation for min *)

Lemma min_cons x y s : min [:: x, y & s] = Some (minn' x (min' s y)).
Proof. by rewrite -min_cons'. Qed.

Lemma minP s1 s2 : s1 =i s2 -> min s1 = min s2.
Proof.
case: s1=>[|a1 s1]; case: s2=>[|a2 s2] //.
- by move/(_ a2); rewrite inE eq_refl.
- by move/(_ a1); rewrite inE eq_refl.
move=>T; move/minP': (T); move/(_ a2); rewrite /= irr => <-.
case: totalP=>//; last by move/eqP=>->.
move=>O; rewrite (min_base' _ O) //.
move/(_ a2): T; rewrite !inE eq_refl eq_sym.
by case: totalP O.
Qed.

End Min.

