LAProof.C.distinct

Require Import VST.floyd.functional_base.
Require Import Coq.Relations.Relations.
Require Import Coq.Classes.RelationClasses.
Import ListNotations.

Inductive sorted {A} (le: relation A): list A Prop :=
| sorted_nil:
    sorted le nil
| sorted_1: x,
    sorted le (x::nil)
| sorted_cons: x y l,
     le x y sorted le (y::l) sorted le (x::y::l).

Lemma sorted_e: {A} `{d: Inhabitant A} {le: A A Prop}
       {TR: Transitive le}
        (al: list A),
      sorted le al i j, 0 i < j j < Zlength al
               (le (Znth i al) (Znth j al)).

Lemma sorted_i: {A} `{d: Inhabitant A} (le: A A Prop) {TR: Transitive le}
         (al: list A),
         ( i j, 0 i < j j < Zlength al le (Znth i al) (Znth j al))
         sorted le al.

Lemma le1_sorted: {A} {le: A A Prop}
       {TR: Transitive le}
        (a b: A) (al: list A),
      (le a b) sorted le (b::al) sorted le (a::al).

Lemma sorted_app:
   {A} {le: AAProp} {TRANS: Transitive le}
    pivot al bl,
    sorted le al sorted le bl
    Forall (fun xle x pivot) al
    Forall (le pivot) bl
    sorted le (al++bl).

Lemma sorted_app_e3:
   {A} {le: relation A} {TRANS: Transitive le}
    pivot al bl,
    sorted le (al++[pivot]++bl)
    sorted le al sorted le bl
    Forall (fun xle x pivot) al
    Forall (le pivot) bl.

Lemma sorted_e1: {t} {Inh: Inhabitant t} {le: relation t} (al: list t), sorted le al
    i,
   0 i < Zlength al-1 le (Znth i al) (Znth (i+1) al).

Lemma sorted_sublist:
   (i j: Z) {A} {InhA: Inhabitant A} le (al: list A),
   0 i j j Zlength al sorted le al sorted le (sublist i j al).

Class BoolOrder {A: Type} (R: relation A): Type := {
    test : A A bool;
    test_spec: x y, reflect (R x y) (test x y)
}.

Arguments test {A} R {BoolOrder}.

Module BPO.

Local Lemma mkteststrictspec {A} {R: relation A} {B: BoolOrder R}{P: PreOrder R}:
  let strict x y := R x y ¬R y x in
  let teststrict x y := andb (test R x y) (negb (test R y x)) in
    x y : A, reflect (strict x y) (teststrict x y).

Local Lemma StrictOrderstrict {A}{R: relation A}{P: PreOrder R}:
 let strict x y := R x y ¬ R y x in
   StrictOrder strict.

Local Lemma mktesteqvspec {A} {R: relation A}{BO: BoolOrder R}:
  let eqv x y := R x y R y x in
  let testeqv x y := andb (test R x y) (test R y x) in
   x y : A, reflect (eqv x y) (testeqv x y).

Local Lemma mkEqv {A}{R: relation A}{PO: PreOrder R}:
  let eqv x y := R x y R y x in
  Equivalence eqv.

Class BoolPreOrder {A: Type} (R: relation A) : Type := {
    #[export] BO :: BoolOrder R ;
    #[export] PO :: PreOrder R ;
    lt : relation A := fun x yR x y ¬R y x;
    ltb: A A bool := fun x yandb (test R x y) (negb (test R y x));
    ltb_spec: x y, reflect (lt x y) (ltb x y) :=
              mkteststrictspec;
    #[export] Strict :: StrictOrder lt := StrictOrderstrict;
    eqv: relation A := fun x yR x y R y x;
    eqvb: A A bool := fun x yandb (test R x y) (test R y x);
    eqvbspec: x y, reflect (eqv x y) (eqvb x y) :=
              mktesteqvspec;
    Eqv :: Equivalence eqv := mkEqv
 }.

Arguments lt [A] [R] {_}.
Arguments ltb [A] [R] {_}.
Arguments eqv [A] [R] {_}.
Arguments eqvb [A] [R] {_}.

End BPO.
Import BPO.

Section SBO.

Context {A: Type} {le: relation A} {SBO: BoolPreOrder le}.
Context {InhA: Inhabitant A}.

Definition count_distinct_aux u v :=
   let '(k,x) := u in (if ltb x v then k+1 else k, v).

Definition count_distinct (al: list A) : Z :=
 match al with
 | nil ⇒ 0
 | a::restfst (fold_left count_distinct_aux rest (1,a))
 end.

Lemma count_distinct_bound: el: list A,
  0 count_distinct el Zlength el.

Lemma count_distinct_bound': el,
  0 < Zlength el
  0 < @count_distinct el.

Lemma count_distinct_mono:
   (bl: list A) i,
      count_distinct (sublist 0 i bl) count_distinct bl.

Lemma count_distinct_incr:
   (bl: list A) i,
      lt (Znth (i-1) bl) (Znth i bl)
      0 < i < Zlength bl
      count_distinct (sublist 0 i bl) + 1 = count_distinct (sublist 0 (i+1) bl).

Lemma count_distinct_incr':
   (bl: list A) i,
      lt (Znth (i - 1) bl) (Znth i bl)
      0 i - 1 < Zlength bl - 1
      count_distinct (sublist 0 i bl) < count_distinct bl.

Lemma count_distinct_noincr:
   (al: list A) i,
         0 < i < Zlength al
         ¬lt (Znth (i-1) al) (Znth i al)
         count_distinct (sublist 0 i al) = count_distinct (sublist 0 (i+1) al).

Lemma count_distinct_aux_mono:
  al y u,
   fst (fold_left count_distinct_aux al (u, y)) u.

Lemma eqv_cda: a x u al,
  eqv x a
  fst (fold_left count_distinct_aux al (u, x)) =
  fst (fold_left count_distinct_aux al (u, a)).

Lemma count_distinct_aux_lemma:
  al y (SOR: sorted le (y::al)) u,
   fst (fold_left count_distinct_aux al (u, y)) u
   Forall (Basics.compose not (lt y)) al.

Lemma count_distinct_1:
  al
   (SOR: sorted le al),
   count_distinct al = 1
    x y, In x al In y al eqv x y.

Lemma count_distinct_range_same:
  al g h,
        sorted le al 0 g < h-1 h Zlength al
        BPO.eqv (Znth g al) (Znth (h-1) al)
        count_distinct (sublist 0 (g+1) al) = count_distinct (sublist 0 h al).

End SBO.