(********************************************************************)
(*                                                                  *)
(*  The Why3 Verification Platform   /   The Why3 Development Team  *)
(*  Copyright 2010-2023 --  Inria - CNRS - Paris-Saclay University  *)
(*                                                                  *)
(*  This software is distributed under the terms of the GNU Lesser  *)
(*  General Public License version 2.1, with the special exception  *)
(*  on linking described in file LICENSE.                           *)
(*                                                                  *)
(********************************************************************)

(* This file is generated by Why3's Coq-realize driver *)
(* Beware! Only edit allowed sections below    *)
Require Import BuiltIn.
Require BuiltIn.
Require HighOrd.
Require int.Int.

Require Import Lia.

Fixpoint numof_aux (f : Z -> bool) (a : Z) (n : nat) : Z :=
  match n with
    | S n => (numof_aux f a n + (if f (a + (Z.of_nat n)) then 1%Z else 0%Z))%Z
    | 0 => 0%Z
  end.

(* Why3 goal *)
Definition numof :
  (Numbers.BinNums.Z -> Init.Datatypes.bool) -> Numbers.BinNums.Z ->
  Numbers.BinNums.Z -> Numbers.BinNums.Z.
Proof.
  exact (fun f a b => numof_aux f a (Z.to_nat (b - a))).
Defined.

(* Why3 goal *)
Lemma numof'def :
  forall (p:Numbers.BinNums.Z -> Init.Datatypes.bool) (a:Numbers.BinNums.Z)
    (b:Numbers.BinNums.Z),
  ((b <= a)%Z -> ((numof p a b) = 0%Z)) /\
  (~ (b <= a)%Z ->
   (((p (b - 1%Z)%Z) = Init.Datatypes.true) ->
    ((numof p a b) = (1%Z + (numof p a (b - 1%Z)%Z))%Z)) /\
   (~ ((p (b - 1%Z)%Z) = Init.Datatypes.true) ->
    ((numof p a b) = (numof p a (b - 1%Z)%Z)))).
Proof.
intros p a b.
unfold numof.
split ; intros h1.
- assert (Z.to_nat (b - a) = 0).
  revert h1.
  rewrite <-Z.le_sub_0.
  now destruct (b - a)%Z.
  now rewrite H.
- rewrite S_pred with (m := 0) (n := Z.to_nat (b - a)).
  2: apply (Z2Nat.inj_lt 0); lia.
  rewrite <- Z2Nat.inj_pred.
  simpl numof_aux.
  rewrite Z2Nat.id by lia.
  replace (a + Z.pred (b - a))%Z with (b - 1)%Z by (unfold Z.pred ; ring).
  replace (Z.pred (b - a)) with (b - 1 - a)%Z by (unfold Z.pred ; ring).
  split ; intros h2.
  rewrite h2.
  apply Zplus_comm.
  apply Bool.not_true_is_false in h2.
  rewrite h2.
  apply Zplus_0_r.
Qed.

Lemma Numof_empty :
  forall p a b, (b <= a)%Z -> numof p a b = 0%Z.
Proof.
  intros p a b h1.
  now apply numof'def.
Qed.

(* Why3 goal *)
Lemma Numof_bounds :
  forall (p:Numbers.BinNums.Z -> Init.Datatypes.bool) (a:Numbers.BinNums.Z)
    (b:Numbers.BinNums.Z),
  (a < b)%Z -> (0%Z <= (numof p a b))%Z /\ ((numof p a b) <= (b - a)%Z)%Z.
Proof.
  intros p a b h1.
  unfold numof.
  set (x := Z.to_nat (b - a)).
  rewrite <-Z2Nat.id with (n := (b - a)%Z) by lia.
  change (0 <= numof_aux p a x <= Z.of_nat x)%Z.
  induction x.
  split ; apply Z.le_refl.
  rewrite Nat2Z.inj_succ; simpl numof_aux.
  case (p (a + Z.of_nat x)%Z); lia.
Qed.

(* Why3 goal *)
Lemma Numof_append :
  forall (p:Numbers.BinNums.Z -> Init.Datatypes.bool) (a:Numbers.BinNums.Z)
    (b:Numbers.BinNums.Z) (c:Numbers.BinNums.Z),
  (a <= b)%Z /\ (b <= c)%Z ->
  ((numof p a c) = ((numof p a b) + (numof p b c))%Z).
Proof.
  intros p a b c (h1,h2).
  pattern c.
  apply Zlt_lower_bound_ind with (z := b); auto.
  intros.
  case (Z.eq_dec b x).
  intro e; rewrite e.
  rewrite Numof_empty with (a := x) (b := x); lia.
  intro H6.
  refine (_ (proj2 (numof'def p a x) _)).
  intros [H1 H2].
  refine (_ (proj2 (numof'def p b x) _)).
  intros [H3 H4].
  destruct (Bool.bool_dec (p (x - 1)%Z) true) as [H5|H5].
  rewrite H1, H3, H ; auto with zarith.
  rewrite H2, H4, H ; auto with zarith.
  clear -H0 H6 ; lia.
  clear -h1 H0 H6 ; lia.
Qed.

Lemma numof_succ: forall p a, numof p a (a + 1) = (if p a then 1%Z else 0%Z).
Proof.
  intros.
  unfold numof.
  replace (a + 1 - a)%Z with 1%Z by ring.
  simpl.
  rewrite <-Zplus_0_r_reverse.
  trivial.
Qed.

Lemma numof_pred: forall p a, numof p (a - 1) a = (if p (a - 1)%Z then 1%Z else 0%Z).
Proof.
  intros.
  replace (numof p (a - 1) a)%Z with (numof p (a - 1) ((a - 1) + 1))%Z.
  apply numof_succ.
  apply f_equal.
  ring.
Qed.

(* Why3 goal *)
Lemma Numof_left_no_add :
  forall (p:Numbers.BinNums.Z -> Init.Datatypes.bool) (a:Numbers.BinNums.Z)
    (b:Numbers.BinNums.Z),
  (a < b)%Z -> ~ ((p a) = Init.Datatypes.true) ->
  ((numof p a b) = (numof p (a + 1%Z)%Z b)).
Proof.
  intros p a b h1 h2.
  rewrite Numof_append with (b := (a+1)%Z) by lia.
  rewrite (numof_succ p a).
  apply Bool.not_true_is_false in h2.
  rewrite h2; trivial.
Qed.

(* Why3 goal *)
Lemma Numof_left_add :
  forall (p:Numbers.BinNums.Z -> Init.Datatypes.bool) (a:Numbers.BinNums.Z)
    (b:Numbers.BinNums.Z),
  (a < b)%Z -> ((p a) = Init.Datatypes.true) ->
  ((numof p a b) = (1%Z + (numof p (a + 1%Z)%Z b))%Z).
Proof.
  intros p a b h1 h2.
  rewrite Numof_append with (b := (a+1)%Z) by lia.
  rewrite (numof_succ p a).
  rewrite h2; trivial.
Qed.

(* Why3 goal *)
Lemma Empty :
  forall (p:Numbers.BinNums.Z -> Init.Datatypes.bool) (a:Numbers.BinNums.Z)
    (b:Numbers.BinNums.Z),
  (forall (n:Numbers.BinNums.Z), (a <= n)%Z /\ (n < b)%Z ->
   ~ ((p n) = Init.Datatypes.true)) ->
  ((numof p a b) = 0%Z).
Proof.
  intros p a b.
  case (Z_lt_le_dec a b); intro; [|intro; apply Numof_empty]; auto.
  pattern b.
  apply Zlt_lower_bound_ind with (z := a); auto with zarith; intros.
  case (Z.eq_dec a x); intro e.
  rewrite e; apply Numof_empty; lia.
  rewrite Numof_append with (b := (x - 1)%Z) by lia.
  assert (numof p (x - 1) x = 0)%Z.
  rewrite numof_pred.
  assert (a <= (x - 1)%Z < x)%Z as H2 by lia.
  generalize (H1 (x - 1)%Z H2).
  intro H3; apply Bool.not_true_is_false in H3; rewrite H3; trivial.
  rewrite H2.
  rewrite H; auto with zarith.
Qed.

(* Why3 goal *)
Lemma Full :
  forall (p:Numbers.BinNums.Z -> Init.Datatypes.bool) (a:Numbers.BinNums.Z)
    (b:Numbers.BinNums.Z),
  (a <= b)%Z ->
  (forall (n:Numbers.BinNums.Z), (a <= n)%Z /\ (n < b)%Z ->
   ((p n) = Init.Datatypes.true)) ->
  ((numof p a b) = (b - a)%Z).
Proof.
  intros p a b h1.
  pattern b.
  apply Zlt_lower_bound_ind with (z := a); auto with zarith; intros.
  case (Z.eq_dec a x); intro e.
  rewrite e; rewrite Zminus_diag; apply Numof_empty; lia.
  rewrite Numof_append with (b := (x - 1)%Z) by lia.
  assert (numof p (x - 1) x = 1)%Z.
  rewrite numof_pred.
  assert (a <= (x - 1)%Z < x)%Z as H2 by lia.
  generalize (H1 (x - 1)%Z H2).
  intro; rewrite H3; trivial.
  rewrite H2.
  rewrite H; auto with zarith.
Qed.

Lemma numof_nat: forall p a b, (0 <= numof p a b)%Z.
Proof.
  intros.
  case (Z_lt_le_dec a b); intro; [|rewrite Numof_empty; auto with zarith].
  pattern b.
  apply Zlt_lower_bound_ind with (z := a) (x := b); auto with zarith; intros.
  case (Z.eq_dec a x); intro e.
  rewrite e; rewrite Numof_empty; lia.
  rewrite Numof_append with (b := (x - 1)%Z) by lia.
  apply Z.add_nonneg_nonneg.
  apply H; lia.
  rewrite numof_pred.
  case (p (x - 1)%Z); easy.
Qed.

Lemma numof_pos: forall p a b k, (a <= k < b)%Z -> p k = true -> (0 < numof p a b)%Z.
Proof.
  intros p a b k h.
  generalize h; pattern b.
  apply Zlt_lower_bound_ind with (z := (a + 1)%Z) (x := b); auto with zarith; intros.
  rewrite Z.add_1_r in H0; apply Zle_succ_gt in H0.
  rewrite Numof_append with (b := (x - 1)%Z) by lia.
  case (Z.eq_dec k (x-1)); intro e.
  rewrite e in H1.
  apply Z.add_nonneg_pos.
  apply numof_nat.
  rewrite numof_pred, H1; easy.
  apply Z.add_pos_nonneg.
  apply H; auto with zarith.
  apply numof_nat.
Qed.

(* Why3 goal *)
Lemma numof_increasing :
  forall (p:Numbers.BinNums.Z -> Init.Datatypes.bool) (i:Numbers.BinNums.Z)
    (j:Numbers.BinNums.Z) (k:Numbers.BinNums.Z),
  (i <= j)%Z /\ (j <= k)%Z -> ((numof p i j) <= (numof p i k))%Z.
Proof.
intros p i j k (h1,h2).
rewrite (Numof_append p i j k) by lia.
rewrite <-Z.le_sub_le_add_l, Zminus_diag.
apply numof_nat.
Qed.

(* Why3 goal *)
Lemma numof_strictly_increasing :
  forall (p:Numbers.BinNums.Z -> Init.Datatypes.bool) (i:Numbers.BinNums.Z)
    (j:Numbers.BinNums.Z) (k:Numbers.BinNums.Z) (l:Numbers.BinNums.Z),
  (i <= j)%Z /\ (j <= k)%Z /\ (k < l)%Z -> ((p k) = Init.Datatypes.true) ->
  ((numof p i j) < (numof p i l))%Z.
Proof.
intros p i j k l (h1,(h2,h3)) h4.
rewrite (Numof_append p i j l) by lia.
rewrite <-Z.lt_sub_lt_add_l, Zminus_diag.
apply numof_pos with (k := k); auto with zarith.
Qed.

(* Why3 goal *)
Lemma numof_change_any :
  forall (p1:Numbers.BinNums.Z -> Init.Datatypes.bool)
    (p2:Numbers.BinNums.Z -> Init.Datatypes.bool) (a:Numbers.BinNums.Z)
    (b:Numbers.BinNums.Z),
  (forall (j:Numbers.BinNums.Z), (a <= j)%Z /\ (j < b)%Z ->
   ((p1 j) = Init.Datatypes.true) -> ((p2 j) = Init.Datatypes.true)) ->
  ((numof p1 a b) <= (numof p2 a b))%Z.
Proof.
  intros p1 p2 a b.
  case (Z_lt_le_dec a b); intro; [|rewrite Numof_empty, Numof_empty; lia].
  pattern b.
  apply Zlt_lower_bound_ind with (z := a); auto with zarith; intros.
  case (Z.eq_dec a x); intro eq.
  rewrite eq; rewrite Numof_empty, Numof_empty; lia.
  rewrite Numof_append with (b := (x-1)%Z) by lia.
  rewrite Numof_append with (p := p2) (b := (x-1)%Z) by lia.
  apply Z.add_le_mono.
  apply H; auto with zarith.
  rewrite numof_pred, numof_pred.
  case (Bool.bool_dec (p1 (x - 1)%Z) true); intro e.
  rewrite e, H1; auto with zarith.
  apply Bool.not_true_is_false in e; rewrite e.
  case (p2 (x -1 )%Z); easy.
Qed.

(* Why3 goal *)
Lemma numof_change_some :
  forall (p1:Numbers.BinNums.Z -> Init.Datatypes.bool)
    (p2:Numbers.BinNums.Z -> Init.Datatypes.bool) (a:Numbers.BinNums.Z)
    (b:Numbers.BinNums.Z) (i:Numbers.BinNums.Z),
  (a <= i)%Z /\ (i < b)%Z ->
  (forall (j:Numbers.BinNums.Z), (a <= j)%Z /\ (j < b)%Z ->
   ((p1 j) = Init.Datatypes.true) -> ((p2 j) = Init.Datatypes.true)) ->
  ~ ((p1 i) = Init.Datatypes.true) -> ((p2 i) = Init.Datatypes.true) ->
  ((numof p1 a b) < (numof p2 a b))%Z.
Proof.
  intros p1 p2 a b i (h1,h2) h3 h4 h5.
  generalize (Z_le_lt_eq_dec _ _ (numof_change_any p1 p2 a b h3)).
  intro H; destruct H; trivial.
  cut False; auto with zarith.
  rewrite Numof_append with (b := i) in e by lia.
  rewrite Numof_append with (p := p2) (b := i) in e by lia.
  rewrite (Numof_left_add _ _ _ h2 h5), (Numof_left_no_add _ _ _ h2 h4) in e.
  assert (forall j : int, (a <= j < i)%Z -> p1 j = true -> p2 j = true) by auto with zarith.
  generalize (numof_change_any p1 p2 _ _ H).
  assert (forall j : int, ((i + 1) <= j < b)%Z -> p1 j = true -> p2 j = true) by auto with zarith.
  generalize (numof_change_any p1 p2 _ _ H0).
  lia.
Qed.

Lemma le_ge_eq: forall a b, (a <= b)%Z /\ (b <= a)%Z -> (a = b)%Z.
Proof.
  auto with zarith.
Qed.

(* Why3 goal *)
Lemma numof_change_equiv :
  forall (p1:Numbers.BinNums.Z -> Init.Datatypes.bool)
    (p2:Numbers.BinNums.Z -> Init.Datatypes.bool) (a:Numbers.BinNums.Z)
    (b:Numbers.BinNums.Z),
  (forall (j:Numbers.BinNums.Z), (a <= j)%Z /\ (j < b)%Z ->
   ((p1 j) = Init.Datatypes.true) <-> ((p2 j) = Init.Datatypes.true)) ->
  ((numof p2 a b) = (numof p1 a b)).
Proof.
intros p1 p2 a b h1.
apply le_ge_eq.
split; apply numof_change_any; intros; now apply h1.
Qed.

