Axiom Tile : Type.

Axiom haskell : Tile.
Axiom sandy : Tile.
Axiom cw : Tile -> Tile.

Axiom cw'cw'cw'cw :
  forall t : Tile,
    cw (cw (cw (cw t))) = t.

Axiom ccw : Tile -> Tile.

Axiom ccw'cw :
  forall t : Tile,
    ccw (cw t) = t.

Axiom cw'ccw :
  forall t : Tile,
    cw (ccw t) = t.

Theorem ccw_is_redundant :
  forall t : Tile,
    ccw t = cw (cw (cw t)).
Proof.
intros.
symmetry.
rewrite <- cw'cw'cw'cw.
rewrite cw'ccw.
reflexivity.
Qed.

Axiom flipH : Tile -> Tile.

Axiom flipH'flipH :
  forall t : Tile,
    flipH (flipH t) = t.

Axiom flipH'cw'cw'flipH :
  forall t : Tile,
    flipH (cw (cw (flipH t))) = cw (cw t).

(* I need to define repeated composition *)
Fixpoint repeat_compose {a : Type}
  (n : nat) (f : a -> a) (x : a) :=
match n with
| 0 => x
| S n => f (repeat_compose n f x)
end.
(* And give it the same ^ notation from the book *)
Notation "f ^ n" := (repeat_compose n f).

Theorem flipH_2ncw_flipH :
  forall (n : nat) (t : Tile),
    flipH ((cw^(n*2)) (flipH t)) = (cw^(n*2)) t.
Proof.
intros.
induction n.
simpl.
rewrite flipH'flipH.
reflexivity.
simpl.
rewrite <- IHn.
symmetry.
rewrite <- flipH'cw'cw'flipH.
rewrite flipH'flipH.
reflexivity.
Qed.

Axiom x_symmetry :
  forall t : Tile,
    flipH (cw t) = ccw (flipH t).

Axiom flipV : Tile -> Tile.

(*
Axiom flipV'flipV :
  forall t : Tile,
    flipV (flipV t) = t.
*)

Axiom ccw'flipH'cw :
  forall t : Tile,
    flipV t = ccw (flipH (cw t)).

(*
Axiom flipV'flipH :
  forall t : Tile,
    flipV (flipH t) = cw (cw t).
*)

Theorem flipV'flipV :
  forall t : Tile,
    flipV (flipV t) = t.
Proof.
intros.
symmetry.
rewrite ccw'flipH'cw.
rewrite ccw'flipH'cw.
rewrite cw'ccw.
rewrite flipH'flipH.
rewrite ccw'cw.
reflexivity.
Qed.

Theorem flipV'flipH :
  forall t : Tile,
    flipV (flipH t) = cw (cw t).
Proof.
intros.
rewrite ccw'flipH'cw.
rewrite <- flipH'cw'cw'flipH.
rewrite <- x_symmetry.
reflexivity.
Qed.


(* Exerpt from https://softwarefoundations.cis.upenn.edu/ for INF3034L course *)

Set Warnings "-notation-overridden,-parsing".
Require Import Coq.Strings.String.
Require Import Coq.Bool.Bool.
Require Import Coq.Arith.Arith.
Require Import Coq.Arith.EqNat.
Require Import Coq.omega.Omega.
Require Import Coq.Lists.List.
Require Import Coq.omega.Omega.

(* From Maps.v to have states *)
Inductive id : Type :=
  | Id : string -> id.

Definition beq_id x y :=
  match x,y with
    | Id n1, Id n2 => if string_dec n1 n2 then true else false
  end.

Theorem beq_id_refl : forall id, true = beq_id id id.
Proof.
  intros [n]. simpl. destruct (string_dec n n).
  - reflexivity.
  - destruct n0. reflexivity.
Qed.

(** The following useful property of [beq_id] follows from an
    analogous lemma about strings: *)

Theorem beq_id_true_iff : forall x y : id,
  beq_id x y = true <-> x = y.
Proof.
   intros [n1] [n2].
   unfold beq_id.
   destruct (string_dec n1 n2).
   - subst. split. reflexivity. reflexivity.
   - split.
     + intros contra. inversion contra.
     + intros H. inversion H. subst. destruct n. reflexivity.
Qed.

(** Similarly: *)

Theorem beq_id_false_iff : forall x y : id,
  beq_id x y = false
  <-> x <> y.
Proof.
  intros x y. rewrite <- beq_id_true_iff.
  rewrite not_true_iff_false. reflexivity. Qed.

(** This useful variant follows just by rewriting: *)

Theorem false_beq_id : forall x y : id,
   x <> y
   -> beq_id x y = false.
Proof.
  intros x y. rewrite beq_id_false_iff.
  intros H. apply H. Qed.

Definition total_map (A:Type) := id -> A.

Definition t_empty {A:Type} (v : A) : total_map A :=
  (fun _ => v).

Definition t_update {A:Type} (m : total_map A)
                    (x : id) (v : A) :=
  fun x' => if beq_id x x' then v else m x'.

(******************************************************************)
(* From Imp.v for the course                                      *)
(******************************************************************)

Import ListNotations.

Definition state := total_map nat.

Definition empty_state : state :=
  t_empty 0.

Inductive aexp : Type :=
  | ANum : nat -> aexp
  | AId : id -> aexp
  | APlus : aexp -> aexp -> aexp
  | AMinus : aexp -> aexp -> aexp
  | AMult : aexp -> aexp -> aexp.

Inductive bexp : Type :=
  | BTrue : bexp
  | BFalse : bexp
  | BEq : aexp -> aexp -> bexp
  | BLe : aexp -> aexp -> bexp
  | BNot : bexp -> bexp
  | BAnd : bexp -> bexp -> bexp.

Fixpoint aeval (st : state) (a : aexp) : nat :=
  match a with
  | ANum n => n
  | AId x => st x
  | APlus a1 a2 => (aeval st a1) + (aeval st a2)
  | AMinus a1 a2  => (aeval st a1) - (aeval st a2)
  | AMult a1 a2 => (aeval st a1) * (aeval st a2)
  end.

About leb.

Fixpoint beval (st : state) (b : bexp) : bool :=
  match b with
  | BTrue       => true
  | BFalse      => false
  | BEq a1 a2   => beq_nat (aeval st a1) (aeval st a2)
  | BLe a1 a2   => leb (aeval st a1) (aeval st a2)
  | BNot b1     => negb (beval st b1)
  | BAnd b1 b2  => andb (beval st b1) (beval st b2)
  end.

(** Defining a few variable names as notational shorthands will make
    examples easier to read: *)

Definition W : id := Id "W".
Definition X : id := Id "X".
Definition Y : id := Id "Y".
Definition Z : id := Id "Z".

Example aexp1 :
  aeval (t_update empty_state X 5)
        (APlus (ANum 3) (AMult (AId X) (ANum 2)))
  = 13.
Proof. reflexivity. Qed.

Example bexp1 :
  beval (t_update empty_state X 5)
        (BAnd BTrue (BNot (BLe (AId X) (ANum 4))))
  = true.
Proof. reflexivity. Qed.

Fixpoint optimize_0plus (a:aexp) : aexp :=
  match a with
  | ANum n =>
      ANum n
  | AId x =>
      AId x
  | APlus (ANum 0) e2 =>
      optimize_0plus e2
  | APlus e1 e2 =>
      APlus (optimize_0plus e1) (optimize_0plus e2)
  | AMinus e1 e2 =>
      AMinus (optimize_0plus e1) (optimize_0plus e2)
  | AMult e1 e2 =>
      AMult (optimize_0plus e1) (optimize_0plus e2)
  end.

Example test_optimize_0plus:
  optimize_0plus (APlus (ANum 2)
                        (APlus (ANum 0)
                               (APlus (ANum 0) (ANum 1))))
  = APlus (ANum 2) (ANum 1).
Proof. reflexivity. Qed.

Theorem optimize_0plus_sound: forall a, forall st,
  aeval st (optimize_0plus a) = aeval st a.
Proof.
  intros a st.
  induction a.
  - simpl.
    reflexivity.
  - simpl.
    reflexivity.
  - destruct a1.
    + destruct n.
      * simpl.
        rewrite IHa2.
        reflexivity.
      * simpl.
        rewrite IHa2.
        reflexivity.
    + simpl.
      rewrite IHa2.
      reflexivity.
    + simpl.
      simpl in IHa1.
      rewrite IHa1.
      rewrite IHa2.
      reflexivity.
    + simpl.
      simpl in IHa1.
      rewrite IHa1.
      rewrite IHa2.
      reflexivity.
    + simpl.
      simpl in IHa1.
      rewrite IHa1.
      rewrite IHa2.
      reflexivity.
  - simpl.
    rewrite IHa1.
    rewrite IHa2.
    reflexivity.
  - simpl.
    rewrite IHa1.
    rewrite IHa2.
    reflexivity.
Qed.

Inductive com : Type :=
  | CSkip : com
  | CAss : id -> aexp -> com
  | CSeq : com -> com -> com
  | CIf : bexp -> com -> com -> com
  | CWhile : bexp -> com -> com.

(** As usual, we can use a few [Notation] declarations to make things
    more readable.  To avoid conflicts with Coq's built-in notations,
    we keep this light -- in particular, we don't introduce any
    notations for [aexps] and [bexps] to avoid confusion with the
    numeric and boolean operators we've already defined. *)

Notation "'SKIP'" :=
  CSkip.
Notation "x '::=' a" :=
  (CAss x a) (at level 60).
Notation "c1 ;; c2" :=
  (CSeq c1 c2) (at level 80, right associativity).
Notation "'WHILE' b 'DO' c 'END'" :=
  (CWhile b c) (at level 80, right associativity).
Notation "'IFB' c1 'THEN' c2 'ELSE' c3 'FI'" :=
  (CIf c1 c2 c3) (at level 80, right associativity).

Reserved Notation "c1 '/' st '\\' st'"
                  (at level 40, st at level 39).

Inductive ceval : com -> state -> state -> Prop :=
  | E_Skip : forall st,
      SKIP / st \\ st
  | E_Ass  : forall st a1 n x,
      aeval st a1 = n ->
      (x ::= a1) / st \\ (t_update st x n)
  | E_Seq : forall c1 c2 st st' st'',
      c1 / st  \\ st' ->
      c2 / st' \\ st'' ->
      (c1 ;; c2) / st \\ st''
  | E_IfTrue : forall st st' b c1 c2,
      beval st b = true ->
      c1 / st \\ st' ->
      (IFB b THEN c1 ELSE c2 FI) / st \\ st'
  | E_IfFalse : forall st st' b c1 c2,
      beval st b = false ->
      c2 / st \\ st' ->
      (IFB b THEN c1 ELSE c2 FI) / st \\ st'
  | E_WhileFalse : forall b st c,
      beval st b = false ->
      (WHILE b DO c END) / st \\ st
  | E_WhileTrue : forall st st' st'' b c,
      beval st b = true ->
      c / st \\ st' ->
      (WHILE b DO c END) / st' \\ st'' ->
      (WHILE b DO c END) / st \\ st''

  where "c1 '/' st '\\' st'" := (ceval c1 st st').

Example ceval_example1:
    (X ::= ANum 2;;
     IFB BLe (AId X) (ANum 1)
       THEN Y ::= ANum 3
       ELSE Z ::= ANum 4
     FI)
   / empty_state
   \\ (t_update (t_update empty_state X 2) Z 4).
Proof.
  (* We must supply the intermediate state *)
  apply E_Seq with (t_update empty_state X 2).
  - (* assignment command *)
    apply E_Ass. reflexivity.
  - (* if command *)
    apply E_IfFalse.
      reflexivity.
      apply E_Ass. reflexivity.
Qed.


(** Generated by coq-of-ocaml *)
Require Import OCaml.OCaml.

Local Set Primitive Projections.
Local Open Scope string_scope.
Local Open Scope Z_scope.
Local Open Scope type_scope.
Import ListNotations.

Unset Positivity Checking.
Unset Guard Checking.

Inductive nat : Set :=
| O : nat
| S : nat -> nat.

Inductive natural : Set :=
| Zero : natural
| Succ : natural -> natural.

Fixpoint plus (plus_arg0 : natural) (plus_arg1 : natural) {struct plus_arg0}
  : natural :=
  match plus_arg0 with
  | Zero => plus_arg1
  | Succ n => Succ (plus n plus_arg1)
  end.

Fixpoint mult (mult_arg0 : natural) (mult_arg1 : natural) {struct mult_arg0}
  : natural :=
  match mult_arg0 with
  | Zero => Zero
  | Succ n => plus (mult n mult_arg1) mult_arg1
  end.

Definition synth (lf1 : natural) (y : natural) (z : natural) (x : natural)
  : natural := plus Zero lf1.


Require Import mathcomp.ssreflect.ssreflect.
Require Import mathcomp.ssreflect.seq mathcomp.ssreflect.prime.

(** Example of computation involving Peano nats *)
Time Eval vm_compute in filter prime (iota 1 100).

(** Example of theorem proved using Coq tactics *)
Theorem thm : forall P, not (iff P (not P)).
Proof.
idtac "proof in progress...".
now intros P [H1 H2]; apply H1; apply H2; intros HP; apply H1.
Qed.


Require Import rt.util.all.
Require Import rt.model.arrival.basic.job rt.model.arrival.basic.task.
Require Import rt.model.schedule.global.basic.schedule.
From mathcomp Require Import ssreflect eqtype ssrbool ssrnat seq bigop.

(* Definitions of deadline miss. *)
Module Schedulability.

  Import Schedule SporadicTaskset Job.

  Section SchedulableDefs.

    Context {sporadic_task: eqType}.

    Context {Job: eqType}.
    Variable job_arrival: Job -> time.
    Variable job_cost: Job -> time.
    Variable job_deadline: Job -> time.
    Variable job_task: Job -> sporadic_task.

    (* Consider any job arrival sequence... *)
    Variable arr_seq: arrival_sequence Job.

    (* ...and any multiprocessor schedule of these jobs. *)
    Context {num_cpus: nat}.
    Variable sched: schedule Job num_cpus.

    Section ScheduleOfJobs.

      (* Let j be any job. *)
      Variable j: Job.

      (* We say that job j misses no deadline in sched if it completed by its absolute deadline. *)
      Definition job_misses_no_deadline :=
        completed job_cost sched j (job_arrival j + job_deadline j).

    End ScheduleOfJobs.

    Section ScheduleOfTasks.

      (* Consider any task tsk. *)
      Variable tsk: sporadic_task.

      (* Task tsk doesn't miss its deadline iff all of its jobs don't miss their deadline. *)
      Definition task_misses_no_deadline :=
        forall j,
          arrives_in arr_seq j ->
          job_task j = tsk ->
          job_misses_no_deadline j.

      (* Task tsk doesn't miss its deadline before time t' iff all of its jobs don't miss
         their deadline by that time. *)
      Definition task_misses_no_deadline_before (t': time) :=
        forall j,
          arrives_in arr_seq j ->
          job_task j = tsk ->
          job_arrival j + job_deadline j < t' ->
          job_misses_no_deadline j.

    End ScheduleOfTasks.

  End SchedulableDefs.

  Section BasicLemmas.

    Context {sporadic_task: eqType}.
    Variable task_cost: sporadic_task -> time.
    Variable task_period: sporadic_task -> time.
    Variable task_deadline: sporadic_task -> time.

    Context {Job: eqType}.
    Variable job_arrival: Job -> time.
    Variable job_cost: Job -> time.
    Variable job_deadline: Job -> time.
    Variable job_task: Job -> sporadic_task.

    (* Consider any job arrival sequence... *)
    Variable arr_seq: arrival_sequence Job.

    (* ...and any schedule of these jobs... *)
    Context {num_cpus : nat}.
    Variable sched: schedule Job num_cpus.

    (* ... where jobs dont execute after completion. *)
    Hypothesis H_completed_jobs_dont_execute:
      completed_jobs_dont_execute job_cost sched.

    Section SpecificJob.

      (* Then, for any job j ...*)
      Variable j: Job.
      Hypothesis H_j_arrives: arrives_in arr_seq j.

      (* ...that doesn't miss a deadline in this schedule, ... *)
      Hypothesis no_deadline_miss:
        job_misses_no_deadline job_arrival job_cost job_deadline sched j.

      (* the service received by j at any time t' after its deadline is 0. *)
      Lemma service_after_job_deadline_zero :
        forall t',
          t' >= job_arrival j + job_deadline j ->
          service_at sched j t' = 0.
      Proof.
        intros t' LE.
        rename no_deadline_miss into NOMISS,
               H_completed_jobs_dont_execute into EXEC.
        unfold job_misses_no_deadline, completed, completed_jobs_dont_execute in *.
        apply/eqP; rewrite -leqn0.
        rewrite <- leq_add2l with (p := job_cost j).
        move: NOMISS => /eqP NOMISS; rewrite -{1}NOMISS addn0.
        apply leq_trans with (n := service sched j t'.+1); last by apply EXEC.
        unfold service; rewrite -> big_cat_nat with
                                   (p := t'.+1) (n := job_arrival j + job_deadline j);
            [rewrite leq_add2l /= | by ins | by apply ltnW].
          by rewrite big_nat_recr // /=; apply leq_addl.
      Qed.

      (* The same applies for the cumulative service of job j. *)
      Lemma cumulative_service_after_job_deadline_zero :
        forall t' t'',
          t' >= job_arrival j + job_deadline j ->
          \sum_(t' <= t < t'') service_at sched j t = 0.
      Proof.
        ins; apply/eqP; rewrite -leqn0.
        rewrite big_nat_cond; rewrite -> eq_bigr with (F2 := fun i => 0);
          first by rewrite big_const_seq iter_addn mul0n addn0 leqnn.
        intro i; rewrite andbT; move => /andP [LE _].
        by rewrite service_after_job_deadline_zero;
          [by ins | by apply leq_trans with (n := t')].
      Qed.

    End SpecificJob.

    Section AllJobs.

      (* Consider any task tsk ...*)
      Variable tsk: sporadic_task.

      (* ... that doesn't miss any deadline. *)
      Hypothesis no_deadline_misses:
        task_misses_no_deadline job_arrival job_cost job_deadline job_task arr_seq sched tsk.

      (* Then, for any valid job j of this task, ...*)
      Variable j: Job.
      Hypothesis H_j_arrives: arrives_in arr_seq j.
      Hypothesis H_job_of_task: job_task j = tsk.
      Hypothesis H_valid_job:
        valid_sporadic_job task_cost task_deadline job_cost job_deadline job_task j.

      (* the service received by job j at any time t' after the deadline is 0. *)
      Lemma service_after_task_deadline_zero :
        forall t',
          t' >= job_arrival j + task_deadline tsk ->
          service_at sched j t' = 0.
      Proof.
        rename H_valid_job into PARAMS; unfold valid_sporadic_job in *; des; intros t'.
        rewrite -H_job_of_task -PARAMS1.
        by apply service_after_job_deadline_zero, no_deadline_misses.
      Qed.

      (* The same applies for the cumulative service of job j. *)
      Lemma cumulative_service_after_task_deadline_zero :
        forall t' t'',
          t' >= job_arrival j + task_deadline tsk ->
          \sum_(t' <= t < t'') service_at sched j t = 0.
      Proof.
        rename H_valid_job into PARAMS; unfold valid_sporadic_job in *; des; intros t' t''.
        rewrite -H_job_of_task -PARAMS1.
        by apply cumulative_service_after_job_deadline_zero, no_deadline_misses.
      Qed.

    End AllJobs.

  End BasicLemmas.

End Schedulability.


Require Import prosa.classic.util.all.
Require Import prosa.classic.model.arrival.basic.job prosa.classic.model.arrival.basic.arrival_sequence.
Require Import prosa.classic.model.schedule.uni.schedule.
From mathcomp Require Import ssreflect ssrbool eqtype ssrnat seq fintype bigop.

(* In this file, we show how to compute the time instant after the last
   execution of a job and prove several lemmas about that instant. This
   notion is crucial for defining suspension intervals. *)
Module LastExecution.

  Export Job UniprocessorSchedule.

  (* In this section we define the time after the last execution of a job (if exists). *)
  Section TimeAfterLastExecution.

    Context {Job: eqType}.
    Variable job_arrival: Job -> time.
    Variable job_cost: Job -> time.

    (* Consider any uniprocessor schedule. *)
    Variable sched: schedule Job.

    (* For simplicity, let's define some local names. *)
    Let job_scheduled_at := scheduled_at sched.
    Let job_completed_by := completed_by job_cost sched.

    Section Defs.

      (* Let j be any job in the arrival sequence. *)
      Variable j: Job.

      (* Next, we will show how to find the time after the most recent
         execution of a given job j in the interval [job_arrival j, t).
         (Note that this instant can be time t itself.) *)
      Variable t: time.

      (* Let scheduled_before denote whether job j was scheduled in the interval [0, t). *)
      Let scheduled_before :=
        [exists t0: 'I_t, job_scheduled_at j t0].

      (* In case j was scheduled before, we define the last time in which j was scheduled. *)
      Let last_time_scheduled :=
        \max_(t_last < t | job_scheduled_at j t_last) t_last.

      (* Then, the time after the last execution of job j in the interval [0, t), if exists,
         occurs:
           (a) immediately after the last time in which job j was scheduled, or,
           (b) if j was never scheduled, at the arrival time of j. *)
      Definition time_after_last_execution :=
        if scheduled_before then
          last_time_scheduled + 1
        else job_arrival j.

    End Defs.

    (* Next, we prove lemmas about the time after the last execution. *)
    Section Lemmas.

      (* Assume that jobs do not execute before they arrived. *)
      Hypothesis H_jobs_must_arrive_to_execute:
        jobs_must_arrive_to_execute job_arrival sched.

      (* Let j be any job. *)
      Variable j: Job.

      (* In this section, we show that the time after the last execution occurs
           no earlier than the arrival of the job. *)
      Section JobHasArrived.

        (* Then, the time following the last execution of job j in the
             interval [0, t) occurs no earlier than the arrival of j. *)
        Lemma last_execution_after_arrival:
          forall t,
            has_arrived job_arrival j (time_after_last_execution j t).
        Proof.
          unfold time_after_last_execution, has_arrived; intros t.
          case EX: [exists _, _]; last by done.
          move: EX => /existsP [t0 SCHED].
          apply leq_trans with (n := t0 + 1);
            last by rewrite leq_add2r; apply leq_bigmax_cond.
          apply leq_trans with (n := t0); last by rewrite addn1.
            by apply H_jobs_must_arrive_to_execute.
        Qed.

      End JobHasArrived.

      (* Next, we establish the monotonicity of the function. *)
      Section Monotonicity.

        (* Let t1 be any time no earlier than the arrival of job j. *)
        Variable t1: time.
        Hypothesis H_after_arrival: has_arrived job_arrival j t1.

        (* Then, (time_after_last_execution j) grows monotonically
             after that point. *)
        Lemma last_execution_monotonic:
          forall t2,
            t1 <= t2 ->
            time_after_last_execution j t1 <= time_after_last_execution j t2.
        Proof.
          rename H_jobs_must_arrive_to_execute into ARR.
          intros t2 LE12.
          rewrite /time_after_last_execution.
          case EX1: [exists _, _].
          {
            move: EX1 => /existsP [t0 SCHED0].
            have EX2: [exists t:'I_t2, job_scheduled_at j t].
            {
              have LT: t0 < t2 by apply: (leq_trans _ LE12).
                by apply/existsP; exists (Ordinal LT).
            }
            rewrite EX2 2!addn1.
            set m1 := \max_(_ < t1 | _)_.
            have LTm1: m1 < t2.
            {
              apply: (leq_trans _ LE12).
                by apply bigmax_ltn_ord with (i0 := t0).
            }
            apply leq_ltn_trans with (n := Ordinal LTm1); first by done.
              by apply leq_bigmax_cond, (bigmax_pred _ _ t0).
          }
          {
            case EX2: [exists _, _]; last by done.
            move: EX2 => /existsP [t0 SCHED0].
            set m2 := \max_(_ < t2 | _)_.
            rewrite addn1 ltnW // ltnS.
            have SCHED2: scheduled_at sched j m2 by apply (bigmax_pred _ _ t0).
              by apply ARR in SCHED2.
          }
        Qed.

      End Monotonicity.

      (* Next, we prove that the function is idempotent. *)
      Section Idempotence.

        (* The time after the last execution of job j is an idempotent function. *)
        Lemma last_execution_idempotent:
          forall t,
            time_after_last_execution j (time_after_last_execution j t)
            = time_after_last_execution j t.
        Proof.
          rename H_jobs_must_arrive_to_execute into ARR.
          intros t.
          rewrite {2 3}/time_after_last_execution.
          case EX: [exists _,_].
          {
            move: EX => /existsP [t0 SCHED].
            rewrite /time_after_last_execution.
            set ex := [exists t0, _].
            have EX': ex.
            {
              apply/existsP; rewrite addn1.
              exists (Ordinal (ltnSn _)).
                by apply bigmax_pred with (i0 := t0).
            }
            rewrite EX'; f_equal.
            rewrite addn1; apply/eqP.
            set m := \max_(_ < t | _)_.
            have LT: m < m.+1 by done.
            rewrite eqn_leq; apply/andP; split.
            {
              rewrite -ltnS; apply bigmax_ltn_ord with (i0 := Ordinal LT).
                by apply bigmax_pred with (i0 := t0).
            }
            {
              apply leq_trans with (n := Ordinal LT); first by done.
                by apply leq_bigmax_cond, bigmax_pred with (i0 := t0).
            }
          }
          {
            apply negbT in EX; rewrite negb_exists in EX.
            move: EX => /forallP EX.
            rewrite /time_after_last_execution.
            set ex := [exists _, _].
            suff EX': ex = false; first by rewrite EX'.
            apply negbTE; rewrite negb_exists; apply/forallP.
            intros x.
            apply/negP; intro SCHED.
            apply ARR in SCHED.
              by apply leq_ltn_trans with (p := job_arrival j) in SCHED;
                first by rewrite ltnn in SCHED.
          }
        Qed.

      End Idempotence.

      (* Next, we show that time_after_last_execution is bounded by the identity function. *)
      Section BoundedByIdentity.

        (* Let t be any time no earlier than the arrival of j. *)
        Variable t: time.
        Hypothesis H_after_arrival: has_arrived job_arrival j t.

        (* Then, the time following the last execution of job j in the interval [0, t)
           occurs no later than time t. *)
        Lemma last_execution_bounded_by_identity:
          time_after_last_execution j t <= t.
        Proof.
          unfold time_after_last_execution.
          case EX: [exists _, _]; last by done.
          move: EX => /existsP [t0 SCHED].
            by rewrite addn1; apply bigmax_ltn_ord with (i0 := t0).
        Qed.

      End BoundedByIdentity.

      (* In this section, we show that if the service received by a job
           remains the same, the time after last execution also doesn't change. *)
      Section SameLastExecution.

        (* Consider any time instants t and t'... *)
        Variable t t': time.

        (* ...in which job j has received the same amount of service. *)
        Hypothesis H_same_service: service sched j t = service sched j t'.

        (* Then, we prove that the times after last execution relative to
             instants t and t' are exactly the same. *)
        Lemma same_service_implies_same_last_execution:
          time_after_last_execution j t = time_after_last_execution j t'.
        Proof.
          rename H_same_service into SERV.
          have IFF := same_service_implies_scheduled_at_earlier_times
                        sched j t t' SERV.
          rewrite /time_after_last_execution.
          rewrite IFF; case EX2: [exists _, _]; [f_equal | by done].
          have EX1: [exists x: 'I_t, job_scheduled_at j x] by rewrite IFF.
          clear IFF.
          move: t t' SERV EX1 EX2 => t1 t2; clear t t'.
          wlog: t1 t2 / t1 <= t2 => [EQ SERV EX1 EX2 | LE].
            by case/orP: (leq_total t1 t2); ins; [|symmetry]; apply EQ.

            set m1 := \max_(t < t1 | job_scheduled_at j t) t.
            set m2 := \max_(t < t2 | job_scheduled_at j t) t.
            move => SERV /existsP [t1' SCHED1'] /existsP [t2' SCHED2'].
            apply/eqP; rewrite eqn_leq; apply/andP; split.
            {
              have WID := big_ord_widen_cond t2
                                             (fun x => job_scheduled_at j x) (fun x => x).
                          rewrite /m1 /m2 {}WID //.
                          rewrite big_mkcond [\max_(t < t2 | _) _]big_mkcond.
                          apply leq_big_max; intros i _.
                          case AND: (_ && _); last by done.
                            by move: AND => /andP [SCHED _]; rewrite SCHED.
            }
            {
              destruct (leqP t2 m1) as [GEm1 | LTm1].
              {
                apply leq_trans with (n := t2); last by done.
                  by apply ltnW, bigmax_ltn_ord with (i0 := t2').
              }
              destruct (ltnP m2 t1) as [LTm2 | GEm2].
              {
                apply leq_trans with (n := Ordinal LTm2); first by done.
                  by apply leq_bigmax_cond, bigmax_pred with (i0 := t2').
              }
              have LTm2: m2 < t2 by apply bigmax_ltn_ord with (i0 := t2').
              have SCHEDm2: job_scheduled_at j m2 by apply bigmax_pred with (i0 := t2').
              exfalso; move: SERV => /eqP SERV.
              rewrite -[_ == _]negbK in SERV.
              move: SERV => /negP SERV; apply SERV; clear SERV.
              rewrite neq_ltn; apply/orP; left.
              rewrite /service /service_during.
              rewrite -> big_cat_nat with (n := m2) (p := t2);
                [simpl | by done | by apply ltnW].
              rewrite -addn1; apply leq_add; first by apply extend_sum.
              destruct t2; first by rewrite ltn0 in LTm1.
              rewrite big_nat_recl; last by done.
                by rewrite /service_at -/job_scheduled_at SCHEDm2.
            }
        Qed.

      End SameLastExecution.

      (* In this section, we show that the service received by a job
         does not change since the last execution. *)
      Section SameService.

        (* We prove that, for any time t, the service received by job j
           before (time_after_last_execution j t) is the same as the service
           by j before time t. *)
        Lemma same_service_since_last_execution:
          forall t,
            service sched j (time_after_last_execution j t) = service sched j t.
        Proof.
          intros t; rewrite /time_after_last_execution.
          case EX: [exists _, _].
          {
            move: EX => /existsP [t0 SCHED0].
            set m := \max_(_ < _ | _) _; rewrite addn1.
            have LTt: m < t by apply: (bigmax_ltn_ord _ _ t0).
            rewrite leq_eqVlt in LTt.
            move: LTt => /orP [/eqP EQ | LTt]; first by rewrite EQ.
            rewrite {2}/service/service_during.
            rewrite -> big_cat_nat with (n := m.+1);
              [simpl | by done | by apply ltnW].
            rewrite [X in _ + X]big_nat_cond [X in _ + X]big1 ?addn0 //.
            move => i /andP [/andP [GTi LTi] _].
            apply/eqP; rewrite eqb0; apply/negP; intro BUG.
            have LEi: (Ordinal LTi) <= m by apply leq_bigmax_cond.
              by apply (leq_ltn_trans LEi) in GTi; rewrite ltnn in GTi.
          }
          {
            apply negbT in EX; rewrite negb_exists in EX.
            move: EX => /forallP ALL.
            rewrite /service /service_during.
            rewrite (ignore_service_before_arrival job_arrival) // big_geq //.
            rewrite big_nat_cond big1 //; move => i /andP [/= LTi _].
            by apply/eqP; rewrite eqb0; apply (ALL (Ordinal LTi)).
          }
        Qed.

      End SameService.

      (* In this section, we show that for any smaller value of service, we can
         always find the last execution that corresponds to that service. *)
      Section ExistsIntermediateExecution.

        (* Assume that job j has completed by time t. *)
        Variable t: time.
        Hypothesis H_j_has_completed: completed_by job_cost sched j t.

        (* Then, for any value of service less than the cost of j, ...*)
        Variable s: time.
        Hypothesis H_less_than_cost: s < job_cost j.

        (* ...there exists a last execution where the service received
           by job j equals s. *)
        Lemma exists_last_execution_with_smaller_service:
          exists t0,
            service sched j (time_after_last_execution j t0) = s.
        Proof.
          have SAME := same_service_since_last_execution.
          rename H_jobs_must_arrive_to_execute into ARR.
          move: H_j_has_completed => COMP.
          feed (exists_intermediate_point (service sched j));
            first by apply service_is_a_step_function.
          move => EX; feed (EX (job_arrival j) t).
          { feed (cumulative_service_implies_scheduled sched j 0 t).
            apply leq_ltn_trans with (n := s); first by done.
            apply leq_trans with (job_cost j); by done.
            move => [t' [/= LTt SCHED]].
            apply leq_trans with (n := t'); last by apply ltnW.
              by apply ARR in SCHED.
          }
          feed (EX s).
          { apply/andP; split.
            - rewrite /service /service_during.
                by rewrite (ignore_service_before_arrival job_arrival) // big_geq.
            - apply leq_ltn_trans with (n := s); first by done.
                by apply leq_trans with (job_cost j).
          }
          move: EX => [x_mid [_ SERV]]; exists x_mid.
          by rewrite -SERV SAME.
        Qed.

      End ExistsIntermediateExecution.

      (* In this section we prove that before the last execution the job
         must have received strictly less service. *)
      Section LessServiceBeforeLastExecution.

        (* Let t be any time... *)
        Variable t: time.

        (* ...and consider any earlier time t0 no earlier than the arrival of job j... *)
        Variable t0: time.
        Hypothesis H_no_earlier_than_arrival: has_arrived job_arrival j t0.

        (* ...and before the last execution of job j (with respect to time t). *)
        Hypothesis H_before_last_execution: t0 < time_after_last_execution j t.

        (* Then, we can prove that the service received by j before time t0
           is strictly less than the service received by j before time t. *)
        Lemma less_service_before_start_of_suspension:
          service sched j t0 < service sched j t.
        Proof.
          rename H_no_earlier_than_arrival into ARR, H_before_last_execution into LT.
          set ex := time_after_last_execution in LT.
          set S := service sched.
          case EX:([exists t0:'I_t, scheduled_at sched j t0]); last first.
          {
            rewrite /ex /time_after_last_execution EX in LT.
            apply leq_trans with (p := t0) in LT; last by done.
            by rewrite ltnn in LT.
          }
          {
            rewrite /ex /time_after_last_execution EX in LT.
            set m := (X in _ < X + 1) in LT.
            apply leq_ltn_trans with (n := S j m);
              first by rewrite -/m addn1 ltnS in LT; apply extend_sum.
            move: EX => /existsP [t' SCHED'].
            have LTt: m < t by apply bigmax_ltn_ord with (i0 := t').
            rewrite /S /service /service_during.
            rewrite -> big_cat_nat with (p := t) (n := m); [simpl | by done | by apply ltnW].
            rewrite -addn1 leq_add2l; destruct t; first by done.
            rewrite big_nat_recl //.
            apply leq_trans with (n := scheduled_at sched j m); last by apply leq_addr.
            rewrite lt0n eqb0 negbK.
            by apply bigmax_pred with (i0 := t').
          }
        Qed.

      End LessServiceBeforeLastExecution.

    End Lemmas.

  End TimeAfterLastExecution.

End LastExecution.


(* Copyright (c) 2014, Robert Dockins *)

Require Import NArith.

Local Open Scope N_scope.

(**  * Bitwise pairing function on [N].

       Here we define a "bitwise" pairing isomorphism
       on the nonnegative integers.  This is one of
       many different ways to witness the isormporphism
       between [N] and [N×N].

       We will use the pairing function to define the union
       of countable sets and other similar constructions.
       The details of the isomorphism are unimportant
       once the isomorphism is defined.  We prove this particular
       isomorphism because it requires almost no facts about
       arithmetic, and the proofs go by simple inductions on the
       binary representation of positives.
  *)

Fixpoint inflate (x:positive) : positive :=
  match x with
  | xH    => xO xH
  | xO x' => xO (xO (inflate x'))
  | xI x' => xO (xI (inflate x'))
  end.

Fixpoint inflate' (x:positive) : positive :=
  match x with
  | xH    => xH
  | xO x' => xO (xO (inflate' x'))
  | xI x' => xI (xO (inflate' x'))
  end.

Fixpoint deflate (x:positive) : N :=
  match x with
  | xH    => N0
  | xO xH => Npos xH
  | xI xH => Npos xH
  | xO (xO x') => match deflate x' with N0 => N0 | Npos q => Npos (xO q) end
  | xI (xO x') => match deflate x' with N0 => N0 | Npos q => Npos (xO q) end
  | xO (xI x') => match deflate x' with N0 => Npos xH | Npos q => Npos (xI q) end
  | xI (xI x') => match deflate x' with N0 => Npos xH | Npos q => Npos (xI q) end
  end.

Fixpoint deflate' (x:positive) : N :=
  match x with
  | xH    => Npos xH
  | xO xH => N0
  | xI xH => Npos xH
  | xO (xO x') => match deflate' x' with N0 => N0 | Npos q => Npos (xO q) end
  | xI (xO x') => match deflate' x' with N0 => Npos xH | Npos q => Npos (xI q) end
  | xO (xI x') => match deflate' x' with N0 => N0 | Npos q => Npos (xO q) end
  | xI (xI x') => match deflate' x' with N0 => Npos xH | Npos q => Npos (xI q) end
  end.

Lemma deflate_inflate0 : forall x,
  deflate (inflate x) = Npos x.
Proof.
  induction x; simpl; intros; auto.
  rewrite IHx; auto.
  rewrite IHx; auto.
Qed.

Lemma deflate_inflate0' : forall y,
  deflate' (inflate' y) = Npos y.
Proof.
  induction y; simpl; intros; auto.
  rewrite IHy; auto.
  rewrite IHy; auto.
Qed.

Lemma deflate_inflate1 : forall y,
  deflate (inflate' y) = 0.
Proof.
  induction y; simpl; auto.
  rewrite IHy; auto.
  rewrite IHy; auto.
Qed.

Lemma deflate_inflate1' : forall x,
  deflate' (inflate x) = 0.
Proof.
  induction x; simpl; auto.
  rewrite IHx; auto.
  rewrite IHx; auto.
Qed.

Lemma deflate_inflate : forall x y,
  deflate (inflate x + inflate' y) = Npos x.
Proof.
  induction x; simpl; intros.
  destruct y; simpl; f_equal; auto.
  rewrite IHx. auto.
  rewrite IHx. auto.
  rewrite deflate_inflate0. auto.
  destruct y; simpl; f_equal; auto.
  rewrite IHx. auto.
  rewrite IHx. auto.
  rewrite deflate_inflate0. auto.
  destruct y; simpl; f_equal; auto.
  rewrite deflate_inflate1. auto.
  rewrite deflate_inflate1. auto.
Qed.

Lemma deflate_inflate' : forall y x,
  deflate' (inflate x + inflate' y) = Npos y.
Proof.
  induction y; simpl; intros; auto.
  destruct x; simpl.
  rewrite IHy. auto.
  rewrite IHy. auto.
  rewrite deflate_inflate0'. auto.
  destruct x; simpl.
  rewrite IHy. auto.
  rewrite IHy. auto.
  rewrite deflate_inflate0'. auto.
  destruct x; simpl.
  rewrite deflate_inflate1'. auto.
  rewrite deflate_inflate1'. auto.
  auto.
Qed.

Lemma deflate00 : forall p, deflate (p~0~0) = 2*(deflate p).
Proof.
  intros. simpl.
  case_eq (deflate p); auto.
Qed.

Lemma deflate01 : forall p, deflate (p~0~1) = 2*(deflate p).
Proof.
  intros. simpl.
  case_eq (deflate p); auto.
Qed.

Lemma deflate10 : forall p, deflate (p~1~0) = 2*(deflate p) + 1 .
Proof.
  intros. simpl.
  case_eq (deflate p); auto.
Qed.

Lemma deflate11 : forall p, deflate (p~1~1) = 2*(deflate p) + 1 .
Proof.
  intros. simpl.
  case_eq (deflate p); auto.
Qed.

Lemma deflate00' : forall p, deflate' (p~0~0) = 2*(deflate' p).
Proof.
  intros. simpl.
  case_eq (deflate' p); auto.
Qed.

Lemma deflate01' : forall p, deflate' (p~0~1) = 2*(deflate' p)+1.
Proof.
  intros. simpl.
  case_eq (deflate' p); auto.
Qed.

Lemma deflate10' : forall p, deflate' (p~1~0) = 2*(deflate' p).
Proof.
  intros. simpl.
  case_eq (deflate' p); auto.
Qed.

Lemma deflate11' : forall p, deflate' (p~1~1) = 2*(deflate' p) + 1 .
Proof.
  intros. simpl.
  case_eq (deflate' p); auto.
Qed.

Definition pairing (p:N*N) : N :=
  match p with
  | (N0, N0) => N0
  | (N0, Npos y) => Npos (inflate' y)
  | (Npos x, N0) => Npos (inflate x)
  | (Npos x, Npos y) => Npos (inflate x + inflate' y)
  end.

Definition unpairing (z:N) : N*N :=
  match z with
  | N0 => (N0,N0)
  | Npos z => (deflate z, deflate' z)
  end.

Lemma pairing00 : forall p q,
  pairing (2*p, 2*q) = 4*pairing (p,q).
Proof.
  simpl; intros.
  destruct p; destruct q; simpl; auto.
Qed.

Lemma pairing10 : forall p q,
  pairing (2*p + 1, 2*q) = 4*pairing (p,q)+2.
Proof.
  simpl; intros.
  destruct p; destruct q; simpl; auto.
Qed.

Lemma pairing01 : forall p q,
  pairing (2*p, 2*q + 1) = 4*pairing (p,q)+1.
Proof.
  simpl; intros.
  destruct p; destruct q; simpl; auto.
Qed.

Lemma pairing11 : forall p q,
  pairing (2*p + 1, 2*q + 1) = 4*pairing (p,q)+3.
Proof.
  simpl; intros.
  destruct p; destruct q; simpl; auto.
Qed.

Lemma unpairing_pairing : forall p, unpairing (pairing p) = p.
Proof.
  intros [x y].
  destruct x; destruct y; simpl; auto.
  rewrite deflate_inflate1. rewrite deflate_inflate0'. auto.
  rewrite deflate_inflate1'. rewrite deflate_inflate0. auto.
  rewrite deflate_inflate.
  rewrite deflate_inflate'.
  auto.
Qed.

Lemma pairing_unpairing : forall z, pairing (unpairing z) = z.
Proof.
  intro z. destruct z. simpl; auto.
  unfold unpairing.
  revert p. fix 1. intro p.
  destruct p. destruct p.
  rewrite deflate11. rewrite deflate11'.
  rewrite pairing11. rewrite pairing_unpairing.
  auto.

  rewrite deflate01. rewrite deflate01'.
  rewrite pairing01. rewrite pairing_unpairing.
  auto.
  simpl. auto.

  destruct p.
  rewrite deflate10. rewrite deflate10'.
  rewrite pairing10. rewrite pairing_unpairing.
  auto.
  rewrite deflate00. rewrite deflate00'.
  rewrite pairing00. rewrite pairing_unpairing.
  auto.
  simpl. auto.
  simpl. auto.
Qed.

Global Opaque unpairing pairing.


Require Export D.

Theorem mult_assoc : forall n m p : nat,
  n * (m * p) = (n * m) * p.
Proof.
  intros. induction n. simpl. reflexivity.
  simpl.

  Lemma plus_assoc : forall a b c : nat,
   a + (b + c) = (a + b) + c.
   Proof.
     intros. induction a. reflexivity.
     simpl. rewrite <- IHa. reflexivity. Qed.

  Lemma mult_dist : forall a b c : nat,
    a * c + b * c = (a + b) * c.
    Proof.
      intros. induction a. simpl.  reflexivity.
      simpl. rewrite <- plus_assoc. rewrite <- IHa. reflexivity. Qed.

  rewrite <- mult_dist. rewrite -> IHn. reflexivity. Qed.


Require Import List.

Theorem length_is_linear_over_app :
  forall (A : Type) (l1 l2 : list A),
  length (l1 ++ l2) = length l1 + length l2.
Proof.
  intros.
  induction l1.
    simpl.
    reflexivity.

    simpl.
    rewrite IHl1.
    reflexivity.
Qed.


Require Export Subbases.
Require Export Relation_Definitions_Implicit.
Require Export SeparatednessAxioms.

Section OrderTopology.

Variable X:Type.
Variable R:relation X.
Hypothesis R_ord: order R.

Inductive order_topology_subbasis : Family X :=
  | intro_lower_interval: forall x:X,
    In order_topology_subbasis [ y:X | R y x /\ y <> x ]
  | intro_upper_interval: forall x:X,
    In order_topology_subbasis [ y:X | R x y /\ y <> x].

Definition OrderTopology : TopologicalSpace :=
  Build_TopologicalSpace_from_subbasis X order_topology_subbasis.

Section if_total_order.

Hypothesis R_total: forall x y:X, R x y \/ R y x.

Lemma lower_closed_interval_closed: forall x:X,
  closed [ y:X | R y x ] (X:=OrderTopology).
Proof.
intro.
red.
match goal with |- open ?U => cut (U = interior U) end.
intro.
rewrite H; apply interior_open.
apply Extensionality_Ensembles; split.
2:apply interior_deflationary.
intros y ?.
red in H.
red in H.
assert (R x y).
destruct (R_total x y); trivial.
contradiction H.
constructor; trivial.
exists ([z:X | R x z /\ z <> x]).
constructor; split.
apply (Build_TopologicalSpace_from_subbasis_subbasis
  _ order_topology_subbasis).
constructor.
red; intros z ?.
destruct H1.
destruct H1.
intro.
destruct H3.
contradiction H2.
apply (ord_antisym R_ord); trivial.
constructor.
split; trivial.
intro.
contradiction H.
constructor.
destruct H1; apply (ord_refl R_ord).
Qed.

Lemma upper_closed_interval_closed: forall x:X,
  closed [y:X | R x y] (X:=OrderTopology).
Proof.
intro.
red.
match goal with |- open ?U => cut (U = interior U) end.
intro.
rewrite H; apply interior_open.
apply Extensionality_Ensembles; split.
2:apply interior_deflationary.
intros y ?.
red in H.
red in H.
assert (R y x).
destruct (R_total x y); trivial.
contradiction H.
constructor; trivial.
exists ([z:X | R z x /\ z <> x]).
constructor; split.
apply (Build_TopologicalSpace_from_subbasis_subbasis
  _ order_topology_subbasis).
constructor.
red; intros z ?.
destruct H1.
destruct H1.
intro.
destruct H3.
contradiction H2.
apply (ord_antisym R_ord); trivial.
constructor.
split; trivial.
intro.
contradiction H.
constructor.
destruct H1; apply (ord_refl R_ord).
Qed.

Lemma order_topology_Hausdorff: Hausdorff OrderTopology.
Proof.
red.
match goal with |- forall x y:point_set OrderTopology, ?P =>
  cut (forall x y:point_set OrderTopology, R x y -> P)
  end.
intros.
destruct (R_total x y).
exact (H x y H1 H0).
assert (y <> x).
auto.
destruct (H y x H1 H2) as [V [U [? [? [? []]]]]].
exists U; exists V; repeat split; trivial.
transitivity (Intersection V U); trivial.
apply Extensionality_Ensembles; split; red; intros.
destruct H8; constructor; trivial.
destruct H8; constructor; trivial.

intros.
pose proof (Build_TopologicalSpace_from_subbasis_subbasis
  _ order_topology_subbasis).
destruct (classic (exists z:X, R x z /\ R z y /\ z <> x /\ z <> y)).
destruct H2 as [z [? [? []]]].
exists ([w:X | R w z /\ w <> z]);
exists ([w:X | R z w /\ w <> z]).
repeat split; trivial.
apply H1.
constructor.
apply H1.
constructor.
auto.
auto.
apply Extensionality_Ensembles; split; red; intros.
destruct H6.
destruct H6.
destruct H7.
destruct H6.
destruct H7.
contradiction H8.
apply (ord_antisym R_ord); trivial.
destruct H6.

exists ([w:X | R w y /\ w <> y]);
exists ([w:X | R x w /\ w <> x]).
repeat split.
apply H1.
constructor.
apply H1.
constructor.
trivial.
trivial.
trivial.
auto.
apply Extensionality_Ensembles; split; red; intros.
destruct H3.
destruct H3.
destruct H4.
destruct H3.
destruct H4.
contradiction H2.
exists x0; repeat split; trivial.
destruct H3.
Qed.

End if_total_order.

End OrderTopology.

Implicit Arguments OrderTopology [[X]].


Set Implicit Arguments.
Generalizable All Variables.
From TLC Require Import LibTactics.
From iris_time.union_find.math Require Import LibNatExtra Filter.

(* [le m] can be understood as the semi-open interval of the natural numbers
   that are greater than or equal to [m]. The subsets [le m] form a filter
   base; that is, if we close them under inclusion, then we obtain a filter,
   which intuitively represents going to infinity. We call this modality
   [towards_infinity]. *)

Definition towards_infinity (F : nat -> Prop) :=
  exists m, forall n, m <= n -> F n.

Instance filter_towards_infinity : Filter towards_infinity.
Proof.
  unfold towards_infinity. econstructor.
  (* There exists an element in this filter, namely the universe, [le 0]. *)
  exists (fun n => 0 <= n). eauto.
  (* Every set of the form [le m] is nonempty. *)
  introv [ m ? ]. exists m. eauto.
  (* Closure by intersection and subset. *)
  introv [ m1 ? ] [ m2 ? ] ?. exists (max m1 m2). intros.
  max_case; eauto with lia.
Qed.

(* Every subset of the form [le m] is a member of this filter. *)

Lemma towards_infinity_le:
  forall m,
  towards_infinity (le m).
Proof.
  unfold towards_infinity. eauto.
Qed.

Hint Resolve towards_infinity_le : filter.

(* The statement that [f x] tends towards infinity as [x] tends
   towards infinity can be stated in its usual concrete form or
   more abstractly using filters. *)

Lemma prove_tends_towards_infinity:
  forall f : nat -> nat,
  (forall y, exists x0, forall x, x0 <= x -> y <= f x) ->
  limit towards_infinity towards_infinity f.
Proof.
  introv h. intros F [ m ? ].
  generalize (h m); intros [ x0 ? ].
  exists x0. eauto.
Qed.

Lemma exploit_tends_towards_infinity:
  forall f : nat -> nat,
  limit towards_infinity towards_infinity f ->
  (forall y, exists x0, forall x, x0 <= x -> y <= f x).
Proof.
  intros ? hlimit y.
  forwards [ x0 ? ]: hlimit (le y).
    eapply towards_infinity_le.
  eauto.
Qed.