diff --git a/Makefile b/Makefile
index ed72ed36bfe05f2190609a728b737fdf4b860565..f874e7c1803cbba7b5f0c2c18f4632227e31008d 100644
--- a/Makefile
+++ b/Makefile
@@ -14,7 +14,7 @@
 
 #
 # This Makefile was generated by the command line :
-# coq_makefile -f _CoqProject ./util/fixedpoint.v ./util/ssromega.v ./util/bigcat.v ./util/nat.v ./util/seqset.v ./util/notation.v ./util/list.v ./util/powerset.v ./util/all.v ./util/sorting.v ./util/tactics.v ./util/bigord.v ./util/exists.v ./util/induction.v ./util/sum.v ./util/divround.v ./util/counting.v ./implementation/basic/bertogna_edf_example.v ./implementation/basic/task.v ./implementation/basic/schedule.v ./implementation/basic/job.v ./implementation/basic/arrival_sequence.v ./implementation/jitter/bertogna_edf_example.v ./implementation/jitter/task.v ./implementation/jitter/schedule.v ./implementation/jitter/job.v ./implementation/jitter/arrival_sequence.v ./analysis/basic/bertogna_fp_theory.v ./analysis/basic/interference_bound_edf.v ./analysis/basic/interference_bound_fp.v ./analysis/basic/interference_bound.v ./analysis/basic/bertogna_edf_comp.v ./analysis/basic/bertogna_fp_comp.v ./analysis/basic/bertogna_edf_theory.v ./analysis/basic/workload_bound.v ./analysis/parallel/bertogna_fp_theory.v ./analysis/parallel/interference_bound_edf.v ./analysis/parallel/interference_bound_fp.v ./analysis/parallel/interference_bound.v ./analysis/parallel/bertogna_edf_comp.v ./analysis/parallel/bertogna_fp_comp.v ./analysis/parallel/bertogna_edf_theory.v ./analysis/parallel/workload_bound.v ./analysis/jitter/bertogna_fp_theory.v ./analysis/jitter/interference_bound_edf.v ./analysis/jitter/interference_bound_fp.v ./analysis/jitter/interference_bound.v ./analysis/jitter/bertogna_edf_comp.v ./analysis/jitter/bertogna_fp_comp.v ./analysis/jitter/bertogna_edf_theory.v ./analysis/jitter/workload_bound.v ./model/basic/time.v ./model/basic/schedulability.v ./model/basic/task.v ./model/basic/task_arrival.v ./model/basic/platform.v ./model/basic/schedule.v ./model/basic/priority.v ./model/basic/interference_edf.v ./model/basic/interference.v ./model/basic/workload.v ./model/basic/job.v ./model/basic/arrival_sequence.v ./model/basic/response_time.v ./model/basic/platform_fp.v ./model/jitter/time.v ./model/jitter/schedulability.v ./model/jitter/task.v ./model/jitter/task_arrival.v ./model/jitter/platform.v ./model/jitter/schedule.v ./model/jitter/priority.v ./model/jitter/interference_edf.v ./model/jitter/interference.v ./model/jitter/workload.v ./model/jitter/job.v ./model/jitter/arrival_sequence.v ./model/jitter/response_time.v ./model/jitter/platform_fp.v -o Makefile 
+# coq_makefile -f _CoqProject ./util/fixedpoint.v ./util/ssromega.v ./util/bigcat.v ./util/nat.v ./util/seqset.v ./util/notation.v ./util/list.v ./util/powerset.v ./util/all.v ./util/sorting.v ./util/tactics.v ./util/bigord.v ./util/exists.v ./util/induction.v ./util/sum.v ./util/divround.v ./util/counting.v ./implementation/basic/bertogna_edf_example.v ./implementation/basic/task.v ./implementation/basic/schedule.v ./implementation/basic/bertogna_fp_example.v ./implementation/basic/job.v ./implementation/basic/arrival_sequence.v ./implementation/parallel/bertogna_edf_example.v ./implementation/parallel/task.v ./implementation/parallel/schedule.v ./implementation/parallel/bertogna_fp_example.v ./implementation/parallel/job.v ./implementation/parallel/arrival_sequence.v ./implementation/jitter/bertogna_edf_example.v ./implementation/jitter/task.v ./implementation/jitter/schedule.v ./implementation/jitter/bertogna_fp_example.v ./implementation/jitter/job.v ./implementation/jitter/arrival_sequence.v ./implementation/apa/bertogna_edf_example.v ./implementation/apa/task.v ./implementation/apa/schedule.v ./implementation/apa/bertogna_fp_example.v ./implementation/apa/job.v ./implementation/apa/arrival_sequence.v ./analysis/basic/bertogna_fp_theory.v ./analysis/basic/interference_bound_edf.v ./analysis/basic/interference_bound_fp.v ./analysis/basic/interference_bound.v ./analysis/basic/bertogna_edf_comp.v ./analysis/basic/bertogna_fp_comp.v ./analysis/basic/bertogna_edf_theory.v ./analysis/basic/workload_bound.v ./analysis/parallel/bertogna_fp_theory.v ./analysis/parallel/interference_bound_edf.v ./analysis/parallel/interference_bound_fp.v ./analysis/parallel/interference_bound.v ./analysis/parallel/bertogna_edf_comp.v ./analysis/parallel/bertogna_fp_comp.v ./analysis/parallel/bertogna_edf_theory.v ./analysis/parallel/workload_bound.v ./analysis/jitter/bertogna_fp_theory.v ./analysis/jitter/interference_bound_edf.v ./analysis/jitter/interference_bound_fp.v ./analysis/jitter/interference_bound.v ./analysis/jitter/bertogna_edf_comp.v ./analysis/jitter/bertogna_fp_comp.v ./analysis/jitter/bertogna_edf_theory.v ./analysis/jitter/workload_bound.v ./analysis/apa/bertogna_fp_theory.v ./analysis/apa/interference_bound_edf.v ./analysis/apa/interference_bound_fp.v ./analysis/apa/interference_bound.v ./analysis/apa/bertogna_edf_comp.v ./analysis/apa/bertogna_fp_comp.v ./analysis/apa/bertogna_edf_theory.v ./analysis/apa/workload_bound.v ./model/basic/time.v ./model/basic/schedulability.v ./model/basic/task.v ./model/basic/task_arrival.v ./model/basic/platform.v ./model/basic/schedule.v ./model/basic/priority.v ./model/basic/interference_edf.v ./model/basic/interference.v ./model/basic/constrained_deadlines.v ./model/basic/workload.v ./model/basic/job.v ./model/basic/arrival_sequence.v ./model/basic/response_time.v ./model/jitter/time.v ./model/jitter/schedulability.v ./model/jitter/task.v ./model/jitter/task_arrival.v ./model/jitter/platform.v ./model/jitter/schedule.v ./model/jitter/priority.v ./model/jitter/interference_edf.v ./model/jitter/interference.v ./model/jitter/constrained_deadlines.v ./model/jitter/workload.v ./model/jitter/job.v ./model/jitter/arrival_sequence.v ./model/jitter/response_time.v ./model/apa/time.v ./model/apa/schedulability.v ./model/apa/task.v ./model/apa/task_arrival.v ./model/apa/platform.v ./model/apa/schedule.v ./model/apa/priority.v ./model/apa/affinity.v ./model/apa/interference_edf.v ./model/apa/interference.v ./model/apa/constrained_deadlines.v ./model/apa/workload.v ./model/apa/job.v ./model/apa/arrival_sequence.v ./model/apa/response_time.v -o Makefile 
 #
 
 .DEFAULT_GOAL := all
@@ -114,13 +114,27 @@ VFILES:=util/fixedpoint.v\
   implementation/basic/bertogna_edf_example.v\
   implementation/basic/task.v\
   implementation/basic/schedule.v\
+  implementation/basic/bertogna_fp_example.v\
   implementation/basic/job.v\
   implementation/basic/arrival_sequence.v\
+  implementation/parallel/bertogna_edf_example.v\
+  implementation/parallel/task.v\
+  implementation/parallel/schedule.v\
+  implementation/parallel/bertogna_fp_example.v\
+  implementation/parallel/job.v\
+  implementation/parallel/arrival_sequence.v\
   implementation/jitter/bertogna_edf_example.v\
   implementation/jitter/task.v\
   implementation/jitter/schedule.v\
+  implementation/jitter/bertogna_fp_example.v\
   implementation/jitter/job.v\
   implementation/jitter/arrival_sequence.v\
+  implementation/apa/bertogna_edf_example.v\
+  implementation/apa/task.v\
+  implementation/apa/schedule.v\
+  implementation/apa/bertogna_fp_example.v\
+  implementation/apa/job.v\
+  implementation/apa/arrival_sequence.v\
   analysis/basic/bertogna_fp_theory.v\
   analysis/basic/interference_bound_edf.v\
   analysis/basic/interference_bound_fp.v\
@@ -145,6 +159,14 @@ VFILES:=util/fixedpoint.v\
   analysis/jitter/bertogna_fp_comp.v\
   analysis/jitter/bertogna_edf_theory.v\
   analysis/jitter/workload_bound.v\
+  analysis/apa/bertogna_fp_theory.v\
+  analysis/apa/interference_bound_edf.v\
+  analysis/apa/interference_bound_fp.v\
+  analysis/apa/interference_bound.v\
+  analysis/apa/bertogna_edf_comp.v\
+  analysis/apa/bertogna_fp_comp.v\
+  analysis/apa/bertogna_edf_theory.v\
+  analysis/apa/workload_bound.v\
   model/basic/time.v\
   model/basic/schedulability.v\
   model/basic/task.v\
@@ -154,11 +176,11 @@ VFILES:=util/fixedpoint.v\
   model/basic/priority.v\
   model/basic/interference_edf.v\
   model/basic/interference.v\
+  model/basic/constrained_deadlines.v\
   model/basic/workload.v\
   model/basic/job.v\
   model/basic/arrival_sequence.v\
   model/basic/response_time.v\
-  model/basic/platform_fp.v\
   model/jitter/time.v\
   model/jitter/schedulability.v\
   model/jitter/task.v\
@@ -168,11 +190,26 @@ VFILES:=util/fixedpoint.v\
   model/jitter/priority.v\
   model/jitter/interference_edf.v\
   model/jitter/interference.v\
+  model/jitter/constrained_deadlines.v\
   model/jitter/workload.v\
   model/jitter/job.v\
   model/jitter/arrival_sequence.v\
   model/jitter/response_time.v\
-  model/jitter/platform_fp.v
+  model/apa/time.v\
+  model/apa/schedulability.v\
+  model/apa/task.v\
+  model/apa/task_arrival.v\
+  model/apa/platform.v\
+  model/apa/schedule.v\
+  model/apa/priority.v\
+  model/apa/affinity.v\
+  model/apa/interference_edf.v\
+  model/apa/interference.v\
+  model/apa/constrained_deadlines.v\
+  model/apa/workload.v\
+  model/apa/job.v\
+  model/apa/arrival_sequence.v\
+  model/apa/response_time.v
 
 ifneq ($(filter-out archclean clean cleanall printenv,$(MAKECMDGOALS)),)
 -include $(addsuffix .d,$(VFILES))
diff --git a/analysis/apa/bertogna_edf_comp.v b/analysis/apa/bertogna_edf_comp.v
new file mode 100755
index 0000000000000000000000000000000000000000..f0a777da4672957ec19d4308b527cad47b3dde5c
--- /dev/null
+++ b/analysis/apa/bertogna_edf_comp.v
@@ -0,0 +1,999 @@
+Require Import rt.util.all rt.util.divround.
+Require Import rt.analysis.apa.bertogna_edf_theory.
+From mathcomp Require Import ssreflect ssrbool eqtype ssrnat seq fintype bigop div path.
+
+Module ResponseTimeIterationEDF.
+
+  Import ResponseTimeAnalysisEDF.
+
+  (* In this section, we define the algorithm corresponding to the APA-reduction
+     of Bertogna and Cirinei's RTA for EDF scheduling. *)
+  Section Analysis.
+    
+    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_cost: Job -> time.
+    Variable job_deadline: Job -> time.
+    Variable job_task: Job -> sporadic_task.
+
+    (* As input for each iteration of the algorithm, we consider pairs
+       of tasks and response-time bounds. *)
+    Let task_with_response_time := (sporadic_task * time)%type.
+
+    (* Consider a platform with num_cpus processors. *)  
+    Variable num_cpus: nat.
+
+    (* Assume that every task has a processor affinity alpha. *)
+    Variable alpha: task_affinity sporadic_task num_cpus.
+
+    (* For the reduction to subproblems, consider a subaffinity alpha' for each task. *)
+    Variable alpha': task_affinity sporadic_task num_cpus.
+
+    (* First, recall the interference bound under EDF. Note that we use
+       subaffinity (alpha' tsk) when computing the set of interfering tasks. *)
+    Let I (rt_bounds: seq task_with_response_time)
+          (tsk: sporadic_task) (delta: time) :=
+      total_interference_bound_edf task_cost task_period task_deadline alpha tsk
+                                   (alpha' tsk) (* Subproblem alpha' *)
+                                   rt_bounds delta.
+
+    (* Then, we define the response-time recurrence. Note that we divide the
+       interference by the cardinality of (alpha' tsk). *)
+    Definition edf_response_time_bound (rt_bounds: seq task_with_response_time)
+                                           (tsk: sporadic_task) (delta: time) :=
+      task_cost tsk + div_floor (I rt_bounds tsk delta)
+                                #|alpha' tsk|.
+
+    (* Also note that a response-time is only valid if it is no larger
+       than the deadline. *)
+    Definition R_le_deadline (pair: task_with_response_time) :=
+      let (tsk, R) := pair in
+        R <= task_deadline tsk.
+
+    (* Next, we define the fixed-point iteration for computing the
+       response-time bound of each task. *)
+    
+    (* Given a sequence 'rt_bounds' of task and response-time bounds
+       from the previous iteration, we compute the response-time
+       bound of a single task using the RTA for EDF. *)
+    Definition update_bound (rt_bounds: seq task_with_response_time) pair :=
+      let '(tsk, R) := pair in
+        (tsk, edf_response_time_bound rt_bounds tsk R).
+
+    (* To compute the response-time bounds of the entire task set,
+       we start the iteration with a sequence of tasks and costs:
+       <(task1, cost1), (task2, cost2), ...>. *)
+    Let initial_state (ts: seq sporadic_task) :=
+      map (fun tsk => (tsk, task_cost tsk)) ts.
+
+    (* Then, we successively update the the response-time bounds based
+       on the slack computed in the previous iteration. *)
+    Definition edf_rta_iteration (rt_bounds: seq task_with_response_time) :=
+      map (update_bound rt_bounds) rt_bounds.
+
+    (* To ensure that the procedure converges, we stop the iteration
+       after a "sufficient" number of times, which corresponds to
+       the time complexity of the procedure. *)
+    Let max_steps (ts: seq sporadic_task) :=
+      \sum_(tsk <- ts) (task_deadline tsk - task_cost tsk) + 1.
+
+    (* This yields the following definition for the RTA. At the end
+       we check if all computed response-time bounds are less than
+       or equal to the deadline, in which case they are valid. *)
+    Definition edf_claimed_bounds (ts: seq sporadic_task) :=
+      let R_values := iter (max_steps ts) edf_rta_iteration (initial_state ts) in
+        if (all R_le_deadline R_values) then
+          Some R_values
+        else None.
+
+    (* The schedulability test simply checks if we got a list of
+       response-time bounds (i.e., if the computation did not fail). *)
+    Definition edf_schedulable (ts: seq sporadic_task) :=
+      edf_claimed_bounds ts != None.
+
+    (* In the following section, we prove several helper lemmas about the
+       list of tasks/response-time bounds. *)
+    Section SimpleLemmas.
+
+      (* Updating a single response-time bound does not modify the task. *)
+      Lemma edf_claimed_bounds_unzip1_update_bound :
+        forall l rt_bounds,
+          unzip1 (map (update_bound rt_bounds) l) = unzip1 l.
+      Proof.
+        induction l; first by done.
+        intros rt_bounds.
+        simpl; f_equal; last by done.
+        by unfold update_bound; desf.
+      Qed.
+
+      (* At any point of the iteration, the tasks are the same. *)
+      Lemma edf_claimed_bounds_unzip1_iteration :
+        forall l k,
+          unzip1 (iter k edf_rta_iteration (initial_state l)) = l.
+      Proof.
+        intros l k; clear -k.
+        induction k; simpl.
+        {
+          unfold initial_state.
+          induction l; first by done.
+          by simpl; rewrite IHl.
+        }
+        {
+          unfold edf_rta_iteration. 
+          by rewrite edf_claimed_bounds_unzip1_update_bound.
+        }
+      Qed.
+
+      (* The iteration preserves the size of the list. *)
+      Lemma edf_claimed_bounds_size :
+        forall l k,
+          size (iter k edf_rta_iteration (initial_state l)) = size l.
+      Proof.
+        intros l k; clear -k.
+        induction k; simpl; first by rewrite size_map.
+        by rewrite size_map.
+      Qed.
+
+      (* If the analysis succeeds, the computed response-time bounds are no smaller
+         than the task cost. *)
+      Lemma edf_claimed_bounds_ge_cost :
+        forall l k tsk R,
+          (tsk, R) \in (iter k edf_rta_iteration (initial_state l)) ->
+          R >= task_cost tsk.
+      Proof.
+        intros l k tsk R IN.
+        destruct k.
+        {
+          move: IN => /mapP IN; destruct IN as [x IN EQ]; inversion EQ.
+          by apply leqnn.
+        }
+        {
+          rewrite iterS in IN.
+          move: IN => /mapP IN; destruct IN as [x IN EQ].
+          unfold update_bound in EQ; destruct x; inversion EQ.
+          by unfold edf_response_time_bound; apply leq_addr.
+        }
+      Qed.
+
+      (* If the analysis suceeds, the computed response-time bounds are no larger
+         than the deadline. *)
+      Lemma edf_claimed_bounds_le_deadline :
+        forall ts rt_bounds tsk R,
+          edf_claimed_bounds ts = Some rt_bounds ->
+          (tsk, R) \in rt_bounds ->
+          R <= task_deadline tsk.
+      Proof.
+        intros ts rt_bounds tsk R SOME PAIR; unfold edf_claimed_bounds in SOME.
+        destruct (all R_le_deadline (iter (max_steps ts)
+                                          edf_rta_iteration (initial_state ts))) eqn:DEADLINE;
+          last by done.
+        move: DEADLINE => /allP DEADLINE.
+        inversion SOME as [EQ]; rewrite -EQ in PAIR.
+        by specialize (DEADLINE (tsk, R) PAIR).
+      Qed.
+
+      (* The list contains a response-time bound for every task in the task set. *)
+      Lemma edf_claimed_bounds_has_R_for_every_task :
+        forall ts rt_bounds tsk,
+          edf_claimed_bounds ts = Some rt_bounds ->
+          tsk \in ts ->
+          exists R,
+            (tsk, R) \in rt_bounds.
+      Proof.
+        intros ts rt_bounds tsk SOME IN.
+        unfold edf_claimed_bounds in SOME.
+        destruct (all R_le_deadline (iter (max_steps ts) edf_rta_iteration (initial_state ts)));
+          last by done.
+        inversion SOME as [EQ]; clear SOME EQ.
+        generalize dependent tsk.
+        induction (max_steps ts) as [| step]; simpl in *.
+        {
+          intros tsk IN; unfold initial_state.
+          exists (task_cost tsk).
+          by apply/mapP; exists tsk.
+        }
+        {
+          intros tsk IN.
+          set prev_state := iter step edf_rta_iteration (initial_state ts).
+          fold prev_state in IN, IHstep.
+          specialize (IHstep tsk IN); des.
+          exists (edf_response_time_bound prev_state tsk R).
+          by apply/mapP; exists (tsk, R); [by done | by f_equal].
+        }
+      Qed.
+     
+    End SimpleLemmas.
+
+    (* In this section, we prove the convergence of the RTA procedure.
+       Since we define the RTA procedure as the application of a function
+       a fixed number of times, this translates into proving that the value
+       of the iteration at (max_steps ts) is equal to the value at (max_steps ts) + 1. *)
+    Section Convergence.
+
+      (* Consider any sequence of tasks with valid parameters. *)
+      Variable ts: seq sporadic_task.
+      Hypothesis H_valid_task_parameters:
+        valid_sporadic_taskset task_cost task_period task_deadline ts.
+      
+      (* To simplify, let f denote the RTA procedure. *)
+      Let f (k: nat) := iter k edf_rta_iteration (initial_state ts).
+
+      (* Since the iteration is applied directly to a list of tasks and response-times,
+         we define a corresponding relation "<=" over those lists. *)
+
+      (* Let 'all_le' be a binary relation over lists of tasks/response-time bounds.
+         It states that every element of list l1 has a response-time bound R that is less
+         than or equal to the corresponding response-time bound R' in list l2 (point-wise).
+         In addition, the relation states that the tasks of both lists are unchanged. *)
+      Let all_le := fun (l1 l2: list task_with_response_time) =>
+        (unzip1 l1 == unzip1 l2) &&
+        all (fun p => (snd (fst p)) <= (snd (snd p))) (zip l1 l2).
+
+      (* Similarly, we define a strict version of 'all_le' called 'one_lt', which states that
+         there exists at least one element whose response-time bound increases. *)
+      Let one_lt := fun (l1 l2: list task_with_response_time) =>
+        (unzip1 l1 == unzip1 l2) &&
+        has (fun p => (snd (fst p)) < (snd (snd p))) (zip l1 l2).
+
+      (* Next, we prove some basic properties about the relation all_le. *)
+      Section RelationProperties.
+
+        (* The relation is reflexive, ... *)
+        Lemma all_le_reflexive : reflexive all_le.
+        Proof.
+          intros l; unfold all_le; rewrite eq_refl andTb.
+          destruct l; first by done.
+          by apply/(zipP t (fun x y => snd x <= snd y)).
+        Qed.
+
+        (* ... and transitive. *)
+        Lemma all_le_transitive: transitive all_le.
+        Proof.
+          unfold transitive, all_le.
+          move => y x z /andP [/eqP ZIPxy LExy] /andP [/eqP ZIPyz LEyz].
+          apply/andP; split; first by rewrite ZIPxy -ZIPyz.
+          move: LExy => /(zipP _ (fun x y => snd x <= snd y)) LExy.
+          move: LEyz => /(zipP _ (fun x y => snd x <= snd y)) LEyz.
+          assert (SIZExy: size (unzip1 x) = size (unzip1 y)).
+            by rewrite ZIPxy.
+          assert (SIZEyz: size (unzip1 y) = size (unzip1 z)).
+            by rewrite ZIPyz.
+          rewrite 2!size_map in SIZExy; rewrite 2!size_map in SIZEyz.
+          destruct y.
+          {
+            apply size0nil in SIZExy; symmetry in SIZEyz.
+            by apply size0nil in SIZEyz; subst.
+          }
+          apply/(zipP t (fun x y => snd x <= snd y)); first by rewrite SIZExy -SIZEyz. 
+          intros i LTi.
+          exploit LExy; first by rewrite SIZExy.
+          {
+            rewrite size_zip -SIZEyz -SIZExy minnn in LTi.
+            by rewrite size_zip -SIZExy minnn; apply LTi.
+          }
+          instantiate (1 := t); intro LE.
+          exploit LEyz; first by apply SIZEyz.
+          {
+            rewrite size_zip SIZExy SIZEyz minnn in LTi.
+            by rewrite size_zip SIZEyz minnn; apply LTi.
+          }
+          by instantiate (1 := t); intro LE'; apply (leq_trans LE).
+        Qed.
+
+        (* At any step of the iteration, the corresponding list
+           is larger than or equal to the initial state. *)
+        Lemma bertogna_edf_comp_iteration_preserves_minimum :
+          forall step, all_le (initial_state ts) (f step). 
+        Proof.
+          unfold f.
+          intros step; destruct step; first by apply all_le_reflexive.
+          apply/andP; split.
+          {
+            assert (UNZIP0 := edf_claimed_bounds_unzip1_iteration ts 0).
+            by simpl in UNZIP0; rewrite UNZIP0 edf_claimed_bounds_unzip1_iteration.
+          }  
+          destruct ts as [| tsk0 ts'].
+          {
+            clear -step; induction step; first by done.
+            by rewrite iterSr IHstep.
+          }
+
+          apply/(zipP (tsk0,0) (fun x y => snd x <= snd y));
+            first by rewrite edf_claimed_bounds_size size_map.
+
+          intros i LTi; rewrite iterS; unfold edf_rta_iteration at 1.
+          have MAP := @nth_map _ (tsk0,0) _ (tsk0,0).
+          rewrite size_zip edf_claimed_bounds_size size_map minnn in LTi.
+          rewrite MAP; clear MAP; last by rewrite edf_claimed_bounds_size.
+          destruct (nth (tsk0, 0) (initial_state (tsk0 :: ts')) i) as [tsk_i R_i] eqn:SUBST.
+          rewrite SUBST; unfold update_bound.
+          unfold initial_state in SUBST.
+          have MAP := @nth_map _ tsk0 _ (tsk0, 0).
+          rewrite ?MAP // in SUBST; inversion SUBST; clear MAP. 
+          assert (EQtsk: tsk_i = fst (nth (tsk0, 0) (iter step edf_rta_iteration
+                                                         (initial_state (tsk0 :: ts'))) i)).
+          {
+            have MAP := @nth_map _ (tsk0,0) _ tsk0 (fun x => fst x).
+            rewrite -MAP; clear MAP; last by rewrite edf_claimed_bounds_size.
+            have UNZIP := edf_claimed_bounds_unzip1_iteration; unfold unzip1 in UNZIP.
+            by rewrite UNZIP; symmetry. 
+          }
+          destruct (nth (tsk0, 0) (iter step edf_rta_iteration (initial_state (tsk0 :: ts')))) as [tsk_i' R_i'].
+          by simpl in EQtsk; rewrite -EQtsk; subst; apply leq_addr.
+        Qed.
+
+        (* The application of the function is inductive. *)
+        Lemma bertogna_edf_comp_iteration_inductive (P : seq task_with_response_time -> Type) :
+          P (initial_state ts) ->
+          (forall k, P (f k) -> P (f (k.+1))) ->
+          P (f (max_steps ts)).
+        Proof.
+          by intros P0 Pn; induction (max_steps ts); last by apply Pn.
+        Qed.
+
+        (* As a last step, we show that edf_rta_iteration preserves order, i.e., for any
+           list l1 no smaller than the initial state, and list l2 such that
+           l1 <= l2, we have (edf_rta_iteration l1) <= (edf_rta_iteration l2). *)
+        Lemma bertogna_edf_comp_iteration_preserves_order :
+          forall l1 l2,
+            all_le (initial_state ts) l1 ->
+            all_le l1 l2 ->
+            all_le (edf_rta_iteration l1) (edf_rta_iteration l2).
+        Proof.
+          rename H_valid_task_parameters into VALID.
+          intros x1 x2 LEinit LE.
+          move: LE => /andP [/eqP ZIP LE]; unfold all_le.
+
+          assert (UNZIP': unzip1 (edf_rta_iteration x1) = unzip1 (edf_rta_iteration x2)).
+          {
+            by rewrite 2!edf_claimed_bounds_unzip1_update_bound.
+          }
+
+          apply/andP; split; first by rewrite UNZIP'.
+          apply f_equal with (B := nat) (f := fun x => size x) in UNZIP'.
+          rename UNZIP' into SIZE.
+          rewrite size_map [size (unzip1 _)]size_map in SIZE.
+          move: LE => /(zipP _ (fun x y => snd x <= snd y)) LE.
+          destruct x1 as [| p0 x1'], x2 as [| p0' x2']; try (by ins).
+          apply/(zipP p0 (fun x y => snd x <= snd y)); first by done.
+          
+          intros i LTi.
+          exploit LE; first by rewrite 2!size_map in SIZE.
+          {
+            by rewrite size_zip 2!size_map -size_zip in LTi; apply LTi.
+          }
+          rewrite 2!size_map in SIZE.
+          instantiate (1 := p0); intro LEi.
+          rewrite (nth_map p0);
+            last by rewrite size_zip 2!size_map -SIZE minnn in LTi.
+          rewrite (nth_map p0);
+            last by rewrite size_zip 2!size_map SIZE minnn in LTi.
+          unfold update_bound, edf_response_time_bound; desf; simpl.
+          rename s into tsk_i, s0 into tsk_i', t into R_i, t0 into R_i', Heq into EQ, Heq0 into EQ'.
+          assert (EQtsk: tsk_i = tsk_i').
+          {
+            destruct p0 as [tsk0 R0], p0' as [tsk0' R0']; simpl in H2; subst.
+            have MAP := @nth_map _ (tsk0',R0) _ tsk0' (fun x => fst x) i ((tsk0', R0) :: x1').
+            have MAP' := @nth_map _ (tsk0',R0) _ tsk0' (fun x => fst x) i ((tsk0', R0') :: x2').
+            assert (FSTeq: fst (nth (tsk0', R0)((tsk0', R0) :: x1') i) =
+                           fst (nth (tsk0',R0) ((tsk0', R0') :: x2') i)).
+            {
+              rewrite -MAP;
+                last by simpl; rewrite size_zip 2!size_map /= -H0 minnn in LTi.
+              rewrite -MAP';
+                last by simpl; rewrite size_zip 2!size_map /= H0 minnn in LTi.
+              by f_equal; simpl; f_equal.
+            }
+            apply f_equal with (B := sporadic_task) (f := fun x => fst x) in EQ.
+            apply f_equal with (B := sporadic_task) (f := fun x => fst x) in EQ'.
+            by rewrite FSTeq EQ' /= in EQ; rewrite EQ.
+          }
+          subst tsk_i'; rewrite leq_add2l.
+          unfold I, total_interference_bound_edf; apply leq_div2r.
+          rewrite 2!big_cons.
+          destruct p0 as [tsk0 R0], p0' as [tsk0' R0'].
+          simpl in H2; subst tsk0'.
+          rename R_i into delta, R_i' into delta'.
+          rewrite EQ EQ' in LEi; simpl in LEi.
+          rename H0 into SIZE, H1 into UNZIP; clear EQ EQ'.
+
+          assert (SUBST: forall l delta,
+                    \sum_(j <- l | let '(tsk_other, _) := j in
+                      different_task_in alpha tsk_i (alpha' tsk_i) tsk_other)
+                        (let '(tsk_other, R_other) := j in
+                          interference_bound_edf task_cost task_period task_deadline tsk_i delta
+                            (tsk_other, R_other)) =
+                    \sum_(j <- l | different_task_in alpha tsk_i (alpha' tsk_i) (fst j))
+                      interference_bound_edf task_cost task_period task_deadline tsk_i delta j).
+          {
+            intros l x; clear -l.
+            induction l; first by rewrite 2!big_nil.
+            by rewrite 2!big_cons; rewrite IHl; desf; rewrite /= Heq in Heq0.
+          } rewrite 2!SUBST; clear SUBST.
+
+          assert (VALID': valid_sporadic_taskset task_cost task_period task_deadline
+                                                       (unzip1 ((tsk0, R0) :: x1'))).
+          {
+            move: LEinit => /andP [/eqP EQinit _].
+            rewrite -EQinit; unfold valid_sporadic_taskset.
+            move => tsk /mapP IN. destruct IN as [p INinit EQ]; subst.
+            by move: INinit => /mapP INinit; destruct INinit as [tsk INtsk]; subst; apply VALID.
+          }
+
+          assert (GE_COST: all (fun p => task_cost (fst p) <= snd p) ((tsk0, R0) :: x1')). 
+          {
+            clear LE; move: LEinit => /andP [/eqP UNZIP' LE].
+            move: LE => /(zipP _ (fun x y => snd x <= snd y)) LE.
+            specialize (LE (tsk0, R0)).
+            apply/(all_nthP (tsk0,R0)).
+            intros j LTj; generalize UNZIP'; simpl; intro SIZE'.
+            have F := @f_equal _ _ size (unzip1 (initial_state ts)).
+            apply F in SIZE'; clear F; rewrite /= 3!size_map in SIZE'.
+            exploit LE; [by rewrite size_map /= | |].
+            {
+              rewrite size_zip size_map /= SIZE' minnn.
+              by simpl in LTj; apply LTj.
+            }
+            clear LE; intro LE.
+            unfold initial_state in LE.
+            have MAP := @nth_map _ tsk0 _ (tsk0,R0).
+            rewrite MAP /= in LE;
+              [clear MAP | by rewrite SIZE'; simpl in LTj].
+            apply leq_trans with (n := task_cost (nth tsk0 ts j));
+              [apply eq_leq; f_equal | by done].
+            have MAP := @nth_map _ (tsk0, R0) _ tsk0 (fun x => fst x).
+            rewrite -MAP; [clear MAP | by done].
+            unfold unzip1 in UNZIP'; rewrite -UNZIP'; f_equal.
+            clear -ts; induction ts; [by done | by simpl; f_equal].
+          }
+          move: GE_COST => /allP GE_COST.
+
+          assert (LESUM: \sum_(j <- x1' | different_task_in alpha tsk_i (alpha' tsk_i) (fst j))
+                        interference_bound_edf task_cost task_period task_deadline tsk_i delta j <=                                  \sum_(j <- x2' | different_task_in alpha tsk_i (alpha' tsk_i) (fst j))
+                        interference_bound_edf task_cost task_period task_deadline tsk_i delta' j).
+          {
+            set elem := (tsk0, R0); rewrite 2!(big_nth elem).
+            rewrite -SIZE.
+            rewrite big_mkcond [\sum_(_ <- _ | different_task_in _ _ _ _)_]big_mkcond.
+            rewrite big_seq_cond [\sum_(_ <- _ | true) _]big_seq_cond.
+            apply leq_sum; intros j; rewrite andbT; intros INj.
+            rewrite mem_iota add0n subn0 in INj; move: INj => /andP [_ INj].
+            assert (FSTeq: fst (nth elem x1' j) = fst (nth elem x2' j)).
+            {
+              have MAP := @nth_map _ elem _ tsk0 (fun x => fst x).
+              by rewrite -2?MAP -?SIZE //; f_equal.
+            } rewrite -FSTeq.
+            destruct (different_task_in alpha tsk_i (alpha' tsk_i) (fst (nth elem x1' j))) eqn:INTERF;
+              last by done.
+            {
+              exploit (LE elem); [by rewrite /= SIZE | | intro LEj].
+              {
+                rewrite size_zip 2!size_map /= -SIZE minnn in LTi.
+                by rewrite size_zip /= -SIZE minnn; apply (leq_ltn_trans INj).
+              }
+              simpl in LEj.
+              exploit (VALID' (fst (nth elem x1' j))); last intro VALIDj.
+              {
+                apply/mapP; exists (nth elem x1' j); last by done.
+                by rewrite in_cons; apply/orP; right; rewrite mem_nth.
+              }
+              exploit (GE_COST (nth elem x1' j)); last intro GE_COSTj.
+              {
+                by rewrite in_cons; apply/orP; right; rewrite mem_nth.
+              }
+              unfold is_valid_sporadic_task in *.
+              destruct (nth elem x1' j) as [tsk_j R_j] eqn:SUBST1,
+                       (nth elem x2' j) as [tsk_j' R_j'] eqn:SUBST2.
+              rewrite SUBST1 SUBST2 in LEj; clear SUBST1 SUBST2.
+              simpl in FSTeq; rewrite -FSTeq; simpl in LEj; simpl in VALIDj; des.
+              by apply interference_bound_edf_monotonic.
+            }
+          }
+          destruct (different_task_in alpha tsk_i (alpha' tsk_i) tsk0) eqn:INTERFtsk0; last by done.
+          apply leq_add; last by done.
+          {             
+            exploit (LE (tsk0, R0)); [by rewrite /= SIZE | | intro LEj];
+              first by instantiate (1 := 0); rewrite size_zip /= -SIZE minnn.
+            exploit (VALID' tsk0); first by rewrite in_cons; apply/orP; left.
+            exploit (GE_COST (tsk0, R0)); first by rewrite in_cons eq_refl orTb.
+            unfold is_valid_sporadic_task; intros GE_COST0 VALID0; des; simpl in LEj.
+            by apply interference_bound_edf_monotonic.
+          }
+        Qed.
+
+        (* It follows from the properties above that the iteration is monotonically increasing. *)
+        Lemma bertogna_edf_comp_iteration_monotonic: forall k, all_le (f k) (f k.+1).
+        Proof.
+          unfold f; intros k.
+          apply fun_mon_iter_mon_generic with (x1 := k) (x2 := k.+1);
+            try (by done);
+            [ by apply all_le_reflexive
+            | by apply all_le_transitive
+            | by apply bertogna_edf_comp_iteration_preserves_order
+            | by apply bertogna_edf_comp_iteration_preserves_minimum].
+        Qed.
+
+      End RelationProperties.
+
+      (* Knowing that the iteration is monotonically increasing (with respect to all_le),
+         we show that the RTA procedure converges to a fixed point. *)
+
+      (* First, note that when there are no tasks, the iteration trivially converges. *)
+      Lemma bertogna_edf_comp_f_converges_with_no_tasks :
+        size ts = 0 ->
+        f (max_steps ts) = f (max_steps ts).+1.
+      Proof.
+        intro SIZE; destruct ts; last by inversion SIZE.
+        unfold max_steps; rewrite big_nil /=.
+        by unfold edf_rta_iteration.
+      Qed.
+
+      (* Otherwise, if the iteration reached a fixed point before (max_steps ts), then
+         the value at (max_steps ts) is still at a fixed point. *)
+      Lemma bertogna_edf_comp_f_converges_early :
+        (exists k, k <= max_steps ts /\ f k = f k.+1) ->
+        f (max_steps ts) = f (max_steps ts).+1.
+      Proof.
+        by intros EX; des; apply iter_fix with (k := k).
+      Qed.
+
+      (* Else, we derive a contradiction. *)
+      Section DerivingContradiction.
+
+        (* Assume that there are tasks. *)
+        Hypothesis H_at_least_one_task: size ts > 0.
+
+        (* Assume that the iteration continued to diverge. *)
+        Hypothesis H_keeps_diverging:
+          forall k,
+            k <= max_steps ts -> f k != f k.+1.
+
+        (* Since the iteration is monotonically increasing, it must be
+           strictly increasing. *)
+        Lemma bertogna_edf_comp_f_increases :
+          forall k,
+            k <= max_steps ts -> one_lt (f k) (f k.+1).
+        Proof.
+          rename H_at_least_one_task into NONEMPTY.
+          intros step LEstep; unfold one_lt; apply/andP; split;
+            first by rewrite 2!edf_claimed_bounds_unzip1_iteration.
+          rewrite -[has _ _]negbK; apply/negP; unfold not; intro ALL.
+          rewrite -all_predC in ALL.
+          move: ALL => /allP ALL.
+          exploit (H_keeps_diverging step); [by done | intro DIFF].
+          assert (DUMMY: exists tsk: sporadic_task, True).
+          {
+            destruct ts as [|tsk0]; first by rewrite ltnn in NONEMPTY.
+            by exists tsk0.
+          }
+          des; clear DUMMY.
+          move: DIFF => /eqP DIFF; apply DIFF.
+          apply eq_from_nth with (x0 := (tsk, 0));
+            first by simpl; rewrite size_map.
+          {
+            intros i LTi.
+            remember (nth (tsk, 0)(f step) i) as p_i;rewrite -Heqp_i.
+            remember (nth (tsk, 0)(f step.+1) i) as p_i';rewrite -Heqp_i'.
+            rename Heqp_i into EQ, Heqp_i' into EQ'.
+            exploit (ALL (p_i, p_i')).
+            {
+              rewrite EQ EQ'.
+              rewrite -nth_zip; last by unfold f; rewrite iterS size_map.
+              apply mem_nth; rewrite size_zip.
+              unfold f; rewrite iterS size_map.
+              by rewrite minnn.
+            }
+            unfold predC; simpl; rewrite -ltnNge; intro LTp.
+
+            have GROWS := bertogna_edf_comp_iteration_monotonic step.
+            move: GROWS => /andP [_ /allP GROWS].
+            exploit (GROWS (p_i, p_i')).
+            {
+              rewrite EQ EQ'.
+              rewrite -nth_zip; last by unfold f; rewrite iterS size_map.
+              apply mem_nth; rewrite size_zip.
+              unfold f; rewrite iterS size_map.
+              by rewrite minnn.
+            }
+            simpl; intros LE.
+            destruct p_i as [tsk_i R_i], p_i' as [tsk_i' R_i'].
+            simpl in *.
+            assert (EQtsk: tsk_i = tsk_i').
+            {
+              unfold edf_rta_iteration in EQ'.
+              rewrite (nth_map (tsk, 0)) in EQ'; last by done.
+              by unfold update_bound in EQ'; desf.
+            }
+            rewrite EQtsk; f_equal.
+            by apply/eqP; rewrite eqn_leq; apply/andP; split.
+          }
+        Qed.
+
+        (* In the end, each response-time bound is so high that the sum
+           of all response-time bounds exceeds the sum of all deadlines.
+           Contradiction! *)
+        Lemma bertogna_edf_comp_rt_grows_too_much :
+          forall k,
+            k <= max_steps ts ->
+            \sum_((tsk, R) <- f k) (R - task_cost tsk) + 1 > k.
+        Proof.
+          have LT := bertogna_edf_comp_f_increases.
+          have MONO := bertogna_edf_comp_iteration_monotonic.
+          rename H_at_least_one_task into NONEMPTY.
+          unfold valid_sporadic_taskset, is_valid_sporadic_task in *.
+          rename H_valid_task_parameters into VALID.
+          intros step LE.
+          assert (DUMMY: exists tsk: sporadic_task, True).
+          {
+            destruct ts as [|tsk0]; first by rewrite ltnn in NONEMPTY.
+            by exists tsk0.
+          } destruct DUMMY as [elem _].
+
+          induction step.
+          {
+            by rewrite addn1.
+          }
+          {
+            rewrite -addn1 ltn_add2r.
+            apply leq_ltn_trans with (n := \sum_(i <- f step) (let '(tsk, R) := i in R - task_cost tsk)).
+            {
+              rewrite -ltnS; rewrite addn1 in IHstep.
+              by apply IHstep, ltnW.
+            }
+            rewrite (eq_bigr (fun x => snd x - task_cost (fst x)));
+              last by ins; destruct i.
+            rewrite [\sum_(_ <- f step.+1)_](eq_bigr (fun x => snd x - task_cost (fst x)));
+              last by ins; destruct i.
+            unfold f at 2; rewrite iterS.
+            rewrite big_map; fold (f step).
+            rewrite -(ltn_add2r (\sum_(i <- f step) task_cost (fst i))).
+            rewrite -2!big_split /=.
+            rewrite big_seq_cond [\sum_(_ <- _ | true)_]big_seq_cond.
+            rewrite (eq_bigr (fun i => snd i)); last first.
+            {
+              intro i; rewrite andbT; intro IN;
+              rewrite subh1; first by rewrite -addnBA // subnn addn0.
+              have GE_COST := edf_claimed_bounds_ge_cost ts step.
+              by destruct i; apply GE_COST.
+            }
+            rewrite [\sum_(_ <- _ | _)(_ - _ + _)](eq_bigr (fun i => snd (update_bound (f step) i))); last first.
+            {
+              intro i; rewrite andbT; intro IN.
+              unfold update_bound; destruct i; simpl.
+              rewrite subh1; first by rewrite -addnBA // subnn addn0.
+              apply (edf_claimed_bounds_ge_cost ts step.+1).
+              by rewrite iterS; apply/mapP; exists (s, t).
+            }
+            rewrite -2!big_seq_cond.
+           
+            specialize (LT step (ltnW LE)).
+            specialize (MONO step).
+            move: LT => /andP [_ LT]; move: LT => /hasP LT.
+            destruct LT as [[x1 x2] INzip LT]; simpl in *.
+            move: MONO => /andP [_ /(zipP _ (fun x y => snd x <= snd y)) MONO].
+            rewrite 2!(big_nth (elem, 0)).
+            apply mem_zip_exists with (elem := (elem, 0)) (elem' := (elem, 0)) in INzip; des;
+              last by rewrite size_map.
+            rewrite -> big_cat_nat with (m := 0) (n := idx) (p := size (f step));
+              [simpl | by done | by apply ltnW].
+            rewrite -> big_cat_nat with (m := idx) (n := idx.+1) (p := size (f step));
+              [simpl | by done | by done].
+            rewrite big_nat_recr /=; last by done.
+            rewrite -> big_cat_nat with (m := 0) (n := idx) (p := size (f step));
+              [simpl | by done | by apply ltnW].
+            rewrite -> big_cat_nat with (m := idx) (n := idx.+1) (p := size (f step));
+              [simpl | by done | by done].
+            rewrite big_nat_recr /=; last by done.
+            rewrite [\sum_(idx <= i < idx) _]big_geq // add0n.
+            rewrite [\sum_(idx <= i < idx) _]big_geq // add0n.
+            rewrite -addn1 -addnA; apply leq_add.
+            {
+              rewrite big_nat_cond [\sum_(_ <= _ < _ | true) _]big_nat_cond.
+              apply leq_sum; move => i /andP [/andP [LT1 LT2] _].
+              exploit (MONO (elem,0)); [by rewrite size_map | | intro LEi].
+              {
+                rewrite size_zip; apply (ltn_trans LT2).
+                by apply leq_trans with (n := size (f step));
+                  [by done | by rewrite size_map minnn].
+              }
+              unfold edf_rta_iteration in LEi.
+              by rewrite -> nth_map with (x1 := (elem, 0)) in LEi;
+                last by apply (ltn_trans LT2).
+            }
+            rewrite -addnA [_ + 1]addnC addnA; apply leq_add.
+            {
+              unfold edf_rta_iteration in INzip2; rewrite addn1.
+              rewrite -> nth_map with (x1 := (elem, 0)) in INzip2; last by done.
+              by rewrite -INzip2 -INzip1.
+            }
+            {
+              rewrite big_nat_cond [\sum_(_ <= _ < _ | true) _]big_nat_cond.
+              apply leq_sum; move => i /andP [/andP [LT1 LT2] _].
+              exploit (MONO (elem,0));
+                [ by rewrite size_map
+                | by rewrite size_zip; apply (leq_trans LT2); rewrite size_map minnn | intro LEi ].
+              unfold edf_rta_iteration in LEi.
+              by rewrite -> nth_map with (x1 := (elem, 0)) in LEi; last by done.
+            }
+          }
+        Qed.
+
+      End DerivingContradiction. 
+
+      (* Using the lemmas above, we prove that edf_rta_iteration reaches
+         a fixed point after (max_steps ts) step, ... *)
+      Lemma edf_claimed_bounds_finds_fixed_point_of_list :
+        forall rt_bounds,
+          edf_claimed_bounds ts = Some rt_bounds ->
+          valid_sporadic_taskset task_cost task_period task_deadline ts ->
+          f (max_steps ts) = edf_rta_iteration (f (max_steps ts)). 
+      Proof.
+        intros rt_bounds SOME VALID.
+        unfold valid_sporadic_taskset, is_valid_sporadic_task in *.
+        unfold edf_claimed_bounds in SOME; desf.
+        rename Heq into LE.
+        fold (f (max_steps ts)) in *; fold (f (max_steps ts).+1).
+
+        (* Either the task set is empty or not. *)
+        destruct (size ts == 0) eqn:EMPTY;
+          first by apply bertogna_edf_comp_f_converges_with_no_tasks; apply/eqP.
+        apply negbT in EMPTY; rewrite -lt0n in EMPTY.
+
+        (* Either f converges by the deadline or not. *)
+        destruct ([exists k in 'I_((max_steps ts).+1), f k == f k.+1]) eqn:EX.
+        {
+          move: EX => /exists_inP EX; destruct EX as [k _ ITERk].
+          destruct k as [k LTk]; simpl in ITERk.
+          apply bertogna_edf_comp_f_converges_early.
+          exists k; split; [by apply LTk | by apply/eqP].
+        }
+
+        (* If not, then we reach a contradiction *)
+        apply negbT in EX; rewrite negb_exists_in in EX.
+        move: EX => /forall_inP EX.
+
+        assert (SAMESUM: \sum_(tsk <- ts) task_cost tsk = \sum_(p <- f (max_steps ts)) task_cost (fst p)).
+        {
+          have MAP := @big_map _ 0 addn _ _ (fun x => fst x) (f (max_steps ts))
+                               (fun x => true) (fun x => task_cost x).
+          have UNZIP := edf_claimed_bounds_unzip1_iteration ts (max_steps ts).
+          fold (f (max_steps ts)) in UNZIP; unfold unzip1 in UNZIP.
+          by rewrite UNZIP in MAP; rewrite MAP.
+        }
+        
+        (* Show that the sum is less than the sum of all deadlines. *)
+        assert (SUM: \sum_(p <- f (max_steps ts)) (snd p - task_cost (fst p)) + 1 <= max_steps ts). 
+        {
+          unfold max_steps at 2; rewrite leq_add2r.
+          rewrite -(leq_add2r (\sum_(tsk <- ts) task_cost tsk)).
+          rewrite {1}SAMESUM -2!big_split /=.
+          rewrite big_seq_cond [\sum_(_ <- _ | true)_]big_seq_cond.
+          rewrite (eq_bigr (fun x => snd x)); last first.
+          {
+            intro i; rewrite andbT; intro IN.
+            rewrite subh1; first by rewrite -addnBA // subnn addn0.
+            have GE_COST := edf_claimed_bounds_ge_cost ts (max_steps ts).
+            fold (f (max_steps ts)) in GE_COST.
+            by destruct i; apply GE_COST.
+          }
+          rewrite (eq_bigr (fun x => task_deadline x)); last first.
+          {
+            intro i; rewrite andbT; intro IN.
+            rewrite subh1; first by rewrite -addnBA // subnn addn0.
+            by specialize (VALID i IN); des.
+          }
+          rewrite -2!big_seq_cond.
+          have MAP := @big_map _ 0 addn _ _ (fun x => fst x) (f (max_steps ts))
+                               (fun x => true) (fun x => task_deadline x).
+          have UNZIP := edf_claimed_bounds_unzip1_iteration ts (max_steps ts).
+          fold (f (max_steps ts)) in UNZIP; unfold unzip1 in UNZIP.
+          rewrite UNZIP in MAP; rewrite MAP.
+          rewrite big_seq_cond [\sum_(_ <- _|true)_]big_seq_cond.
+          apply leq_sum; intro i; rewrite andbT; intro IN.
+          move: LE => /allP LE; unfold R_le_deadline in LE.
+          by specialize (LE i IN); destruct i.
+        }
+
+        have TOOMUCH :=
+          bertogna_edf_comp_rt_grows_too_much EMPTY _ (max_steps ts) (leqnn (max_steps ts)).
+        exploit TOOMUCH; [| intro BUG].
+        {
+          intros k LEk; rewrite -ltnS in LEk.
+          by exploit (EX (Ordinal LEk)); [by done | by ins].
+        }
+        rewrite (eq_bigr (fun i => snd i - task_cost (fst i))) in BUG;
+          last by ins; destruct i.
+        by apply (leq_ltn_trans SUM) in BUG; rewrite ltnn in BUG. 
+      Qed.
+
+      (* ...and since there cannot be a vector of response-time bounds with values less than
+         the task costs, this solution is also the least fixed point. *)
+      Lemma edf_claimed_bounds_finds_least_fixed_point :
+        forall v,
+          all_le (initial_state ts) v ->
+          v = edf_rta_iteration v ->
+          all_le (f (max_steps ts)) v.
+      Proof.
+        intros v GE0 EQ.
+        apply bertogna_edf_comp_iteration_inductive; first by done.
+        intros k GEk.
+        rewrite EQ.
+        apply bertogna_edf_comp_iteration_preserves_order; last by done.
+        by apply bertogna_edf_comp_iteration_preserves_minimum.
+      Qed.
+
+      
+      (* Therefore, with regard to the response-time bound recurrence,  
+         the individual response-time bounds (elements of the list) are
+         also fixed points. *)
+      Theorem edf_claimed_bounds_finds_fixed_point_for_each_bound :
+        forall tsk R rt_bounds,
+          edf_claimed_bounds ts = Some rt_bounds ->
+          (tsk, R) \in rt_bounds ->
+          R = edf_response_time_bound rt_bounds tsk R. 
+      Proof.
+        intros tsk R rt_bounds SOME IN.
+        have CONV := edf_claimed_bounds_finds_fixed_point_of_list rt_bounds.
+        rewrite -iterS in CONV; fold (f (max_steps ts).+1) in CONV.
+        unfold edf_claimed_bounds in *; desf.
+        exploit (CONV); [by done | by done | intro ITER; clear CONV].
+        unfold f in ITER.
+
+        cut (update_bound (iter (max_steps ts)
+               edf_rta_iteration (initial_state ts)) (tsk,R) = (tsk, R)).
+        {
+          intros EQ.
+          have F := @f_equal _ _ (fun x => snd x) _ (tsk, R).
+          by apply F in EQ; simpl in EQ.
+        }
+        set s := iter (max_steps ts) edf_rta_iteration (initial_state ts).
+        fold s in ITER, IN.
+        move: IN => /(nthP (tsk,0)) IN; destruct IN as [i LT EQ].
+        generalize EQ; rewrite ITER iterS in EQ; intro EQ'.
+        fold s in EQ.
+        unfold edf_rta_iteration in EQ.
+        have MAP := @nth_map _ (tsk,0) _ _ (update_bound s). 
+        by rewrite MAP // EQ' in EQ; rewrite EQ.
+      Qed.
+      
+    End Convergence.
+
+    (* Now we prove the correctness of the response-time bounds. *)
+    Section MainProof.
+
+      (* Consider a task set ts where... *)
+      Variable ts: taskset_of sporadic_task.
+      
+      (* ... all tasks have valid parameters ... *)
+      Hypothesis H_valid_task_parameters:
+        valid_sporadic_taskset task_cost task_period task_deadline ts.
+
+      (* ... and constrained deadlines. *)
+      Hypothesis H_constrained_deadlines:
+        forall tsk, tsk \in ts -> task_deadline tsk <= task_period tsk.
+
+      (* Assume that alpha' is a non-empty subaffinity of alpha. *)
+      Hypothesis H_non_empty_affinity:
+        forall tsk, tsk \in ts -> #|alpha' tsk| > 0.
+      Hypothesis H_subaffinity:
+        forall tsk, tsk \in ts -> is_subaffinity (alpha' tsk) (alpha tsk).
+                                      
+      (* Next, consider any arrival sequence such that...*)
+      Context {arr_seq: arrival_sequence Job}.
+
+     (* ...all jobs come from task set ts, ...*)
+      Hypothesis H_all_jobs_from_taskset:
+        forall (j: JobIn arr_seq), job_task j \in ts.
+      
+      (* ...they have valid parameters,...*)
+      Hypothesis H_valid_job_parameters:
+        forall (j: JobIn arr_seq),
+          valid_sporadic_job task_cost task_deadline job_cost job_deadline job_task j.
+      
+      (* ... and satisfy the sporadic task model.*)
+      Hypothesis H_sporadic_tasks:
+        sporadic_task_model task_period arr_seq job_task.
+      
+      (* Then, consider any schedule with at least one CPU such that...*)
+      Variable sched: schedule num_cpus arr_seq.
+
+      (* ...jobs only execute after they arrived and no longer
+         than their execution costs,... *)
+      Hypothesis H_jobs_must_arrive_to_execute:
+        jobs_must_arrive_to_execute sched.
+      Hypothesis H_completed_jobs_dont_execute:
+        completed_jobs_dont_execute job_cost sched.
+
+      (* ...and jobs are sequential. *)
+      Hypothesis H_sequential_jobs: sequential_jobs sched.
+      
+      (* Assume a work-conserving APA scheduler that enforces EDF policy. *)
+      Hypothesis H_respects_affinity: respects_affinity job_task sched alpha.
+      Hypothesis H_work_conserving: apa_work_conserving job_cost job_task sched alpha.
+      Hypothesis H_edf_policy: enforces_JLDP_policy_under_weak_APA job_cost job_task sched alpha (EDF job_deadline).
+
+      (* To avoid a long list of parameters, we provide some local definitions. *)      
+      Definition no_deadline_missed_by_task (tsk: sporadic_task) :=
+        task_misses_no_deadline job_cost job_deadline job_task sched tsk.
+      Definition no_deadline_missed_by_job :=
+        job_misses_no_deadline job_cost job_deadline sched.
+      Let response_time_bounded_by (tsk: sporadic_task) :=
+        is_response_time_bound_of_task job_cost job_task tsk sched.
+      
+      (* In the following theorem, we prove that any response-time bound contained
+         in edf_claimed_bounds is safe. The proof follows by direct application of
+         the main Theorem from bertogna_edf_theory.v. *)
+      Theorem edf_analysis_yields_response_time_bounds :
+        forall tsk R,
+          (tsk, R) \In edf_claimed_bounds ts ->
+          response_time_bounded_by tsk R.
+      Proof.
+        have BOUND := bertogna_cirinei_response_time_bound_edf.
+        intros tsk R IN j JOBj.
+        destruct (edf_claimed_bounds ts) as [rt_bounds |] eqn:SOME; last by done.
+        unfold edf_rta_iteration in *.
+        unfold is_response_time_bound_of_task in *.
+        apply BOUND with (task_cost := task_cost) (task_period := task_period)
+           (task_deadline := task_deadline) (job_deadline := job_deadline)
+           (job_task := job_task) (ts := ts) (tsk := tsk) (rt_bounds := rt_bounds) (alpha := alpha) (alpha' := alpha'); try (by ins).
+        by unfold edf_claimed_bounds in SOME; desf; rewrite edf_claimed_bounds_unzip1_iteration.
+        by ins; apply edf_claimed_bounds_finds_fixed_point_for_each_bound with (ts := ts).
+        by ins; rewrite (edf_claimed_bounds_le_deadline ts rt_bounds).
+      Qed.
+      
+      (* Therefore, if the schedulability test suceeds, ...*)
+      Hypothesis H_test_succeeds: edf_schedulable ts.
+      
+      (*... no task misses its deadline. *)
+      Theorem taskset_schedulable_by_edf_rta :
+        forall tsk, tsk \in ts -> no_deadline_missed_by_task tsk.
+      Proof.
+        have RLIST := (edf_analysis_yields_response_time_bounds).
+        have DL := (edf_claimed_bounds_le_deadline ts).
+        have HAS := (edf_claimed_bounds_has_R_for_every_task ts).
+        unfold no_deadline_missed_by_task, task_misses_no_deadline,
+               job_misses_no_deadline, completed,
+               edf_schedulable,
+               valid_sporadic_job in *.
+        rename H_valid_job_parameters into JOBPARAMS.
+        intros tsk INtsk j JOBtsk.
+        destruct (edf_claimed_bounds ts) as [rt_bounds |] eqn:SOME; last by ins.
+        exploit (HAS rt_bounds tsk); [by ins | by ins | clear HAS; intro HAS; des].
+        have COMPLETED := RLIST tsk R HAS j JOBtsk.
+        exploit (DL rt_bounds tsk R);
+          [by ins | by ins | clear DL; intro DL].
+   
+        rewrite eqn_leq; apply/andP; split; first by apply cumulative_service_le_job_cost.
+        apply leq_trans with (n := service sched j (job_arrival j + R)); last first.
+        {
+          unfold valid_sporadic_taskset, is_valid_sporadic_task in *.
+          apply extend_sum; rewrite // leq_add2l.
+          specialize (JOBPARAMS j); des; rewrite JOBPARAMS1.
+          by rewrite JOBtsk.
+        }
+        rewrite leq_eqVlt; apply/orP; left; rewrite eq_sym.
+        by apply COMPLETED.
+      Qed.
+
+      (* For completeness, since all jobs of the arrival sequence
+         are spawned by the task set, we conclude that no job misses
+         its deadline. *)
+      Theorem jobs_schedulable_by_edf_rta :
+        forall (j: JobIn arr_seq), no_deadline_missed_by_job j.
+      Proof.
+        intros j.
+        have SCHED := taskset_schedulable_by_edf_rta.
+        unfold no_deadline_missed_by_task, task_misses_no_deadline in *.
+        apply SCHED with (tsk := job_task j); last by done.
+        by apply H_all_jobs_from_taskset.
+      Qed.
+     
+    End MainProof.
+
+  End Analysis.
+
+End ResponseTimeIterationEDF.
\ No newline at end of file
diff --git a/analysis/apa/bertogna_edf_theory.v b/analysis/apa/bertogna_edf_theory.v
new file mode 100644
index 0000000000000000000000000000000000000000..97485a09c0f93da117bdc49457d5bbe50c75f7da
--- /dev/null
+++ b/analysis/apa/bertogna_edf_theory.v
@@ -0,0 +1,985 @@
+Require Import rt.util.all rt.util.divround.
+Require Import rt.model.apa.task rt.model.apa.job rt.model.apa.task_arrival
+               rt.model.apa.schedule rt.model.apa.platform rt.model.apa.interference
+               rt.model.apa.workload rt.model.apa.schedulability rt.model.apa.priority
+               rt.model.apa.platform rt.model.apa.response_time
+               rt.model.apa.affinity rt.model.apa.constrained_deadlines.
+Require Import rt.analysis.apa.workload_bound rt.analysis.apa.interference_bound_edf.
+From mathcomp Require Import ssreflect ssrbool eqtype ssrnat seq fintype bigop div path.
+
+Module ResponseTimeAnalysisEDF.
+
+  Export Job SporadicTaskset ScheduleOfSporadicTask Workload Schedulability ResponseTime
+         Priority SporadicTaskArrival WorkloadBound InterferenceBoundEDF
+         Interference Platform Affinity ConstrainedDeadlines.
+
+  (* In this section, we prove that any fixed point in the APA-reduction of
+     Bertogna and Cirinei's RTA for EDF scheduling is a safe response-time bound.
+     This result corresponds to Lemma 10 in the revised version of the APA paper:
+     http://www.mpi-sws.org/~bbb/papers/pdf/ecrts13a-rev1.pdf *)
+  Section ResponseTimeBound.
+
+    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_cost: Job -> time.
+    Variable job_deadline: Job -> time.
+    Variable job_task: Job -> sporadic_task.
+    
+    (* Assume any job arrival sequence... *)
+    Context {arr_seq: arrival_sequence Job}.
+
+    (* ... in which jobs arrive sporadically and have valid parameters. *)
+    Hypothesis H_sporadic_tasks:
+      sporadic_task_model task_period arr_seq job_task.
+    Hypothesis H_valid_job_parameters:
+      forall (j: JobIn arr_seq),
+        valid_sporadic_job task_cost task_deadline job_cost job_deadline job_task j.
+
+    (* Consider a task set ts where all tasks have valid parameters
+       and constrained deadlines, ... *)
+    Variable ts: taskset_of sporadic_task.
+    Hypothesis H_valid_task_parameters:
+      valid_sporadic_taskset task_cost task_period task_deadline ts.
+    Hypothesis H_constrained_deadlines:
+      forall tsk, tsk \in ts -> task_deadline tsk <= task_period tsk.
+
+    (* ... and assume that all jobs in the arrival sequence come from the task set. *)
+    Hypothesis H_all_jobs_from_taskset:
+      forall (j: JobIn arr_seq), job_task j \in ts.
+    
+    (* Also assume that every task has a non-empty processor affinity alpha. *)
+    Context {num_cpus: nat}.
+    Variable alpha: task_affinity sporadic_task num_cpus.
+
+    (* Next, consider any schedule such that...*)
+    Variable sched: schedule num_cpus arr_seq.
+
+    (* ...jobs are sequential and do not execute before their
+       arrival times nor longer than their execution costs. *)
+    Hypothesis H_sequential_jobs: sequential_jobs sched.
+    Hypothesis H_jobs_must_arrive_to_execute:
+      jobs_must_arrive_to_execute sched.
+    Hypothesis H_completed_jobs_dont_execute:
+      completed_jobs_dont_execute job_cost sched.
+
+    (* Assume that the schedule is an work-conserving APA schedule that
+       enforces EDF priorities. *)
+    Hypothesis H_respects_affinity: respects_affinity job_task sched alpha.
+    Hypothesis H_work_conserving: apa_work_conserving job_cost job_task sched alpha.
+    Hypothesis H_edf_policy:
+      enforces_JLDP_policy_under_weak_APA job_cost job_task sched alpha (EDF job_deadline).
+
+    (* Let's define some local names to avoid passing many parameters. *)
+    Let no_deadline_is_missed_by_tsk (tsk: sporadic_task) :=
+      task_misses_no_deadline job_cost job_deadline job_task sched tsk.
+    Let response_time_bounded_by (tsk: sporadic_task) :=
+      is_response_time_bound_of_task job_cost job_task tsk sched.
+
+    (* Now we consider the response-time recurrence. In the computation of
+       the response-time bound, we assume that each task under analysis has
+       a non-empty subaffinity alpha'.
+       Note that the notation #|...| expresses the cardinality of the set. *)
+    Variable alpha': task_affinity sporadic_task num_cpus.
+    Hypothesis H_affinity_subset: forall tsk, tsk \in ts -> is_subaffinity (alpha' tsk) (alpha tsk).
+    Hypothesis H_at_least_one_cpu : forall tsk, tsk \in ts -> #|alpha' tsk| > 0.
+    
+    (* Assume that a response-time bound R is known...  *)
+    Let task_with_response_time := (sporadic_task * time)%type.
+    Variable rt_bounds: seq task_with_response_time.
+
+    (* ...for any task in the task set, ... *)
+    Hypothesis H_rt_bounds_contains_all_tasks: unzip1 rt_bounds = ts.
+
+    (* ... where R is a fixed-point of the response-time recurrence (with
+           alpha' as the reduced affinity mask), ... *)
+    Let I (tsk: sporadic_task) (delta: time) :=
+      total_interference_bound_edf task_cost task_period task_deadline alpha
+                                   tsk (alpha' tsk) rt_bounds delta.
+    Hypothesis H_response_time_is_fixed_point :
+      forall tsk R,
+        (tsk, R) \in rt_bounds ->
+        R = task_cost tsk + div_floor (I tsk R) #|alpha' tsk|.
+    
+    (* ..., and R is no larger than the deadline. *)
+    Hypothesis H_tasks_miss_no_deadlines:
+      forall tsk R,
+        (tsk, R) \in rt_bounds -> R <= task_deadline tsk.
+
+    (* In order to prove that R is a response-time bound, we first provide some lemmas. *)
+    Section Lemmas.
+
+      (* Let (tsk, R) be any task to be analyzed, with its response-time bound R. *)
+      Variable tsk: sporadic_task.
+      Variable R: time.
+      Hypothesis H_tsk_R_in_rt_bounds: (tsk, R) \in rt_bounds.
+
+      (* Consider any job j of tsk ... *)
+      Variable j: JobIn arr_seq.
+      Hypothesis H_job_of_tsk: job_task j = tsk.
+
+      (* ... that did not complete on time, ... *)
+      Hypothesis H_j_not_completed: ~~ completed job_cost sched j (job_arrival j + R).
+
+      (* ... and that is the first job not to satisfy its response-time bound. *)
+      Hypothesis H_all_previous_jobs_completed_on_time :
+        forall (j_other: JobIn arr_seq) tsk_other R_other,
+          job_task j_other = tsk_other ->
+          (tsk_other, R_other) \in rt_bounds ->
+          job_arrival j_other + R_other < job_arrival j + R ->
+          completed job_cost sched j_other (job_arrival j_other + R_other).
+
+      (* Let's call x the interference incurred by job j due to tsk_other, ...*)
+      Let x (tsk_other: sporadic_task) :=
+        task_interference job_cost job_task sched alpha j
+                          tsk_other (job_arrival j) (job_arrival j + R).
+
+      (* and X the total interference incurred by job j due to any task. *)
+      Let X := total_interference job_cost sched j (job_arrival j) (job_arrival j + R).
+
+      (* Recall Bertogna and Cirinei's workload bound ... *)
+      Let workload_bound (tsk_other: sporadic_task) (R_other: time) :=
+        W task_cost task_period tsk_other R_other R.
+
+      (*... and the EDF-specific bound, ... *)
+      Let edf_specific_bound (tsk_other: sporadic_task) (R_other: time) :=
+        edf_specific_interference_bound task_cost task_period task_deadline tsk tsk_other R_other.
+
+      (* ... which combined form the interference bound. *)
+      Let interference_bound (tsk_other: sporadic_task) (R_other: time) :=
+        interference_bound_edf task_cost task_period task_deadline tsk R (tsk_other, R_other). 
+      
+      (* Based on the definition of a different task in subaffinity alpha', ... *)
+      Let other_task_in alpha' := different_task_in alpha tsk alpha'.
+
+      (* ...let (other_tasks_in alpha') denote the set of tasks that are different from tsk
+         and that can be scheduled on subaffinity alpha'. *)
+      Let other_tasks_in alpha' :=
+        [seq tsk_other <- ts | other_task_in (alpha' tsk) tsk_other].
+
+      (* Now we establish results the interfering tasks. *)
+      Section LemmasAboutInterferingTasks.
+        
+        (* Let (tsk_other, R_other) be any pair of higher-priority task and
+           response-time bound computed in previous iterations. *)
+        Variable tsk_other: sporadic_task.
+        Variable R_other: time.
+        Hypothesis H_response_time_of_tsk_other: (tsk_other, R_other) \in rt_bounds.
+
+        (* Note that tsk_other is in the task set, ...*)
+        Lemma bertogna_edf_tsk_other_in_ts: tsk_other \in ts.
+        Proof.
+          by rewrite set_mem -H_rt_bounds_contains_all_tasks; apply/mapP; exists (tsk_other, R_other).
+        Qed.
+
+        (* ... and R_other is larger than the cost of tsk_other. *)
+        Lemma bertogna_edf_R_other_ge_cost :
+          R_other >= task_cost tsk_other.
+        Proof.
+          by rewrite [R_other](H_response_time_is_fixed_point tsk_other);
+            first by apply leq_addr.
+        Qed.
+
+        (* Since tsk_other cannot interfere more than it executes, we show that
+           the interference caused by tsk_other is no larger than workload bound W. *)
+        Lemma bertogna_edf_workload_bounds_interference :
+          x tsk_other <= workload_bound tsk_other R_other.
+        Proof.
+          unfold valid_sporadic_job in *.
+          rename H_all_previous_jobs_completed_on_time into BEFOREok,
+                 H_valid_job_parameters into PARAMS,
+                 H_valid_task_parameters into TASK_PARAMS,
+                 H_constrained_deadlines into RESTR,
+                 H_tasks_miss_no_deadlines into NOMISS.
+          unfold x, task_interference.
+          have INts := bertogna_edf_tsk_other_in_ts.
+          apply leq_trans with (n := workload job_task sched tsk_other
+                                         (job_arrival j) (job_arrival j + R));
+            first by apply task_interference_le_workload.
+          by apply workload_bounded_by_W with (task_deadline0 := task_deadline)
+               (job_cost0 := job_cost) (job_deadline0 := job_deadline); try (by ins); last 2 first;
+            [ by apply bertogna_edf_R_other_ge_cost
+            | by ins; apply NOMISS
+            | by ins; apply TASK_PARAMS
+            | by ins; apply RESTR
+            | by ins; apply BEFOREok with (tsk_other := tsk_other)].
+        Qed.
+
+        (* Recall that the edf-specific interference bound also holds for tsk_other. *)
+        Lemma bertogna_edf_specific_bound_holds :
+          x tsk_other <= edf_specific_bound tsk_other R_other.
+        Proof.
+          apply interference_bound_edf_bounds_interference with (job_deadline0 := job_deadline)
+                                                                  (ts0 := ts); try (by done);
+            [ by apply bertogna_edf_tsk_other_in_ts
+            | by apply H_tasks_miss_no_deadlines
+            | by apply H_tasks_miss_no_deadlines | ].
+          by ins; apply H_all_previous_jobs_completed_on_time with (tsk_other := tsk_other). 
+        Qed.
+        
+      End LemmasAboutInterferingTasks.
+
+      (* Next we prove some lemmas that help to derive a contradiction.*)
+      Section DerivingContradiction.
+
+      (* 0) Since job j did not complete by its response time bound, it follows that
+            the total interference X exceeds  R - e_k + 1. *)
+      Lemma bertogna_edf_too_much_interference : X >= R - task_cost tsk + 1.
+      Proof.
+        rename H_completed_jobs_dont_execute into COMP,
+               H_valid_job_parameters into PARAMS, H_response_time_is_fixed_point into REC,
+               H_job_of_tsk into JOBtsk, H_j_not_completed into NOTCOMP.
+        unfold completed, valid_sporadic_job in *.
+        unfold X, total_interference; rewrite addn1.
+        rewrite -(ltn_add2r (task_cost tsk)).
+        rewrite subh1; last by rewrite [R](REC tsk) // leq_addr.
+        rewrite -addnBA // subnn addn0.
+        move: (NOTCOMP) => /negP NOTCOMP'.
+        rewrite neq_ltn in NOTCOMP.
+        move: NOTCOMP => /orP [LT | BUG]; last first.
+        {
+          exfalso; rewrite ltnNge in BUG; move: BUG => /negP BUG; apply BUG.
+          by apply cumulative_service_le_job_cost.
+        }
+        apply leq_ltn_trans with (n := (\sum_(job_arrival j <= t < job_arrival j + R)
+                                     backlogged job_cost sched j t) +
+                                   service sched j (job_arrival j + R)); last first.
+        {
+          rewrite -addn1 -addnA leq_add2l addn1.
+          apply leq_trans with (n := job_cost j); first by done.
+          by specialize (PARAMS j); des; rewrite -JOBtsk.
+        }
+        unfold service; rewrite service_before_arrival_eq_service_during //.
+        rewrite -big_split /=.
+        apply leq_trans with (n := \sum_(job_arrival j <= i < job_arrival j + R) 1);
+          first by rewrite big_const_nat iter_addn mul1n addn0 addKn.
+        rewrite big_nat_cond [\sum_(_ <= _ < _ | true) _]big_nat_cond.
+        apply leq_sum; move => i /andP [/andP [GEi LTi] _].
+        destruct (backlogged job_cost sched j i) eqn:BACK;
+          first by rewrite -addn1 addnC; apply leq_add.
+        apply negbT in BACK.
+        rewrite add0n lt0n -not_scheduled_no_service negbK.
+        rewrite /backlogged negb_and negbK in BACK.
+        move: BACK => /orP [/negP NOTPENDING | SCHED]; last by done.
+        exfalso; apply NOTPENDING; unfold pending; apply/andP; split; first by done.
+        apply/negP; red; intro BUG; apply NOTCOMP'.
+        by apply completion_monotonic with (t := i); try (by done); apply ltnW.
+      Qed.
+
+      (* 1) Next, we prove that during the scheduling window of j, any job that is
+            scheduled while j is backlogged comes from a different task.
+            This follows from the fact that j is the first job not to complete
+            by its response-time bound, so previous jobs of j's task must have
+            completed by their periods and cannot be pending. *)
+      Lemma bertogna_edf_interference_by_different_tasks :
+        forall t j_other,
+          job_arrival j <= t < job_arrival j + R ->
+          backlogged job_cost sched j t ->
+          scheduled sched j_other t ->
+          job_task j_other != tsk.
+      Proof.
+        rename H_all_jobs_from_taskset into FROMTS,
+               H_valid_task_parameters into PARAMS,
+               H_job_of_tsk into JOBtsk, H_sporadic_tasks into SPO,
+               H_work_conserving into WORK,
+               H_tsk_R_in_rt_bounds into INbounds,
+               H_all_previous_jobs_completed_on_time into BEFOREok,
+               H_tasks_miss_no_deadlines into NOMISS,
+               H_constrained_deadlines into RESTR.
+        move => t j_other /andP [LEt GEt] BACK SCHED.
+        apply/eqP; red; intro SAMEtsk.
+        move: SCHED => /existsP [cpu SCHED].
+        assert (SCHED': scheduled sched j_other t).
+          by apply/existsP; exists cpu.
+        clear SCHED; rename SCHED' into SCHED.
+        move: (SCHED) => PENDING.
+        apply scheduled_implies_pending with (job_cost0 := job_cost) in PENDING; try (by done).
+        destruct (ltnP (job_arrival j_other) (job_arrival j)) as [BEFOREother | BEFOREj].
+         {
+          move: (BEFOREother) => LT; rewrite -(ltn_add2r R) in LT.
+          specialize (BEFOREok j_other tsk R SAMEtsk INbounds LT).
+          move: PENDING => /andP [_ /negP NOTCOMP]; apply NOTCOMP.
+          apply completion_monotonic with (t0 := job_arrival j_other + R); try (by done).
+          apply leq_trans with (n := job_arrival j); last by done.
+          apply leq_trans with (n := job_arrival j_other + task_deadline tsk);
+            first by rewrite leq_add2l; apply NOMISS.
+          apply leq_trans with (n := job_arrival j_other + task_period tsk);
+            first by rewrite leq_add2l; apply RESTR; rewrite -JOBtsk FROMTS.
+          rewrite -SAMEtsk; apply SPO; [ | by rewrite JOBtsk | by apply ltnW].
+          by red; intro EQ; subst; rewrite ltnn in BEFOREother.
+        }
+        {
+          move: PENDING => /andP [ARRIVED _].
+          exploit (SPO j j_other); [ | by rewrite SAMEtsk | by done | ]; last first.
+          {
+            apply/negP; rewrite -ltnNge.
+            apply leq_ltn_trans with (n := t); first by done.
+            apply leq_trans with (n := job_arrival j + R); first by done.
+            by rewrite leq_add2l; apply leq_trans with (n := task_deadline tsk);
+              [by apply NOMISS | by rewrite JOBtsk RESTR // -JOBtsk FROMTS].
+          }
+          by red; intros EQtsk; subst; rewrite /backlogged SCHED andbF in BACK.
+        }
+      Qed.
+        
+      (* 2) In order to use the lemmas in constrained_deadlines.v, we show that
+            all jobs released before the end of the interval complete by their
+            periods. This follows trivially from the hypothesis that all jobs
+            before (job_arrival j + R) complete by their response-time bounds. 
+            With this lemma, we can conclude that during job j's scheduling
+            window there cannot be multiple pending jobs of each task.*)
+      Lemma bertogna_edf_all_previous_jobs_complete_by_their_period:
+        forall t (j0: JobIn arr_seq),
+          t < job_arrival j + R ->
+          job_arrival j0 + task_period (job_task j0) <= t ->
+          completed job_cost sched j0
+             (job_arrival j0 + task_period (job_task j0)).
+      Proof.
+        rename H_rt_bounds_contains_all_tasks into UNZIP,
+               H_constrained_deadlines into CONSTR,
+               H_tasks_miss_no_deadlines into NOMISS,
+               H_all_jobs_from_taskset into FROMTS,
+               H_all_previous_jobs_completed_on_time into BEFOREok.
+        intros t j0 LEt LE.
+        cut ((job_task j0) \in unzip1 rt_bounds = true); last by rewrite UNZIP FROMTS.
+        move => /mapP [p IN EQ]; destruct p as [tsk' R0]; simpl in *; subst tsk'.
+        apply completion_monotonic with (t0 := job_arrival j0 + R0); first by done.
+        {
+          rewrite leq_add2l; apply leq_trans with (n := task_deadline (job_task j0));
+            [by apply NOMISS | by apply CONSTR; rewrite FROMTS].
+        }
+        apply BEFOREok with (tsk_other := (job_task j0)); try by done.
+        apply leq_ltn_trans with (n := t); last by done.
+        apply leq_trans with (n := job_arrival j0 + task_period (job_task j0)); last by done.
+        by rewrite leq_add2l; apply leq_trans with (n := task_deadline (job_task j0));
+          [by apply NOMISS | apply CONSTR; rewrite FROMTS].
+      Qed.
+
+      (* 3) Next, we prove that the sum of the interference of each task is equal to the
+            total interference multiplied by the number of processors in tsk's *actual*
+            affinity. This holds because interference only occurs when all processors in
+            the affinity are busy.
+            With this lemma we can relate per-task interference with the total interference
+            incurred by j (backlogged time). *)
+      Lemma bertogna_edf_all_cpus_in_affinity_busy :
+        \sum_(tsk_k <- other_tasks_in alpha) x tsk_k = X * #|alpha tsk|.
+      Proof.
+        have DIFFTASK := bertogna_edf_interference_by_different_tasks.
+        rename H_all_jobs_from_taskset into FROMTS,
+               H_valid_task_parameters into PARAMS,
+               H_job_of_tsk into JOBtsk, H_sporadic_tasks into SPO,
+               H_work_conserving into WORK,
+               H_tsk_R_in_rt_bounds into INbounds,
+               H_all_previous_jobs_completed_on_time into BEFOREok,
+               H_tasks_miss_no_deadlines into NOMISS,
+               H_rt_bounds_contains_all_tasks into UNZIP,
+               H_constrained_deadlines into RESTR,
+               H_respects_affinity into APA.
+        unfold sporadic_task_model in *.
+        unfold x, X, total_interference, task_interference.
+        rewrite -big_mkcond -exchange_big big_distrl /= mul1n.
+        rewrite [\sum_(_ <= _ < _ | backlogged _ _ _ _) _]big_mkcond.
+        apply eq_big_nat; move => t /andP [GEt LTt].
+        destruct (backlogged job_cost sched j t) eqn:BACK; last first.
+        {
+          rewrite (eq_bigr (fun i => 0));
+            first by rewrite big_const_seq iter_addn mul0n addn0.
+          by intros i _; rewrite (eq_bigr (fun i => 0));
+            first by rewrite big_const_seq iter_addn mul0n addn0.
+        }
+        rewrite big_mkcond /=.
+        rewrite exchange_big /=.
+        apply eq_trans with (y := \sum_(cpu < num_cpus | cpu \in alpha tsk) 1);
+          last by rewrite sum1_card.
+        rewrite [\sum_(_ < _ | _) 1]big_mkcond /=.
+        apply eq_bigr; intros cpu _.
+        unfold can_execute_on in *.
+        destruct (cpu \in alpha (job_task j)) eqn:ALPHA; rewrite -?JOBtsk ALPHA;
+          last by rewrite big_filter (eq_bigr (fun x => 0));
+            [by simpl_sum_const | by ins].
+        move: (WORK j t BACK cpu ALPHA) => [j_other /eqP SCHED]; unfold scheduled_on in *.
+        rewrite (bigD1_seq (job_task j_other)) /=; last by rewrite filter_uniq; destruct ts.
+        {
+          rewrite (eq_bigr (fun i => 0));
+            last by intros i DIFF; rewrite /task_scheduled_on SCHED;apply/eqP;rewrite eqb0 eq_sym.
+          rewrite big_const_seq iter_addn mul0n 2!addn0; apply/eqP; rewrite eqb1.
+          by unfold task_scheduled_on; rewrite SCHED.
+        }
+        rewrite mem_filter; apply/andP; split; last by apply FROMTS.
+        unfold different_task_in, affinity_intersects.
+        apply/andP; split; last first.
+        {
+          apply/existsP; exists cpu; rewrite -JOBtsk ALPHA andTb.
+          by apply APA with (t := t); apply/eqP.
+        }
+        apply DIFFTASK with (t := t); [by auto | by done |].
+        by apply/existsP; exists cpu; apply/eqP.
+      Qed.
+
+      (* 4) Recall that the reduction-based analysis considers only the interfering tasks
+            within subaffinity (alpha' tsk), as opposed to (alpha tsk). Therefore, we must
+            reason about the task interference wihin (alpha' tsk).
+            We begin by showing that the cumulative interference within (alpha' tsk) is at
+            least the total interference multiplied by the number of processors in (alpha' tsk). *)
+      Lemma bertogna_edf_all_cpus_in_subaffinity_busy :
+        \sum_(tsk_k <- other_tasks_in alpha') x tsk_k >= X * #|alpha' tsk|.
+      Proof.
+        have DIFFTASK := bertogna_edf_interference_by_different_tasks.
+        rename H_all_jobs_from_taskset into FROMTS,
+               H_valid_task_parameters into PARAMS,
+               H_job_of_tsk into JOBtsk, H_sporadic_tasks into SPO,
+               H_work_conserving into WORK,
+               H_tsk_R_in_rt_bounds into INbounds,
+               H_all_previous_jobs_completed_on_time into BEFOREok,
+               H_tasks_miss_no_deadlines into NOMISS,
+               H_rt_bounds_contains_all_tasks into UNZIP,
+               H_constrained_deadlines into RESTR,
+               H_respects_affinity into APA, H_affinity_subset into SUB.
+        unfold sporadic_task_model in *.
+        unfold x, X, total_interference, task_interference.
+        rewrite -big_mkcond -exchange_big big_distrl /= mul1n.
+        rewrite [\sum_(_ <= _ < _ | backlogged _ _ _ _) _]big_mkcond /=.
+        apply leq_sum_nat; move => t /andP [GEt LTt] _.
+        destruct (backlogged job_cost sched j t) eqn:BACK; last first.
+        {
+          rewrite (eq_bigr (fun i => 0));
+            first by rewrite big_const_seq iter_addn mul0n addn0.
+          by intros i _; rewrite (eq_bigr (fun i => 0));
+            first by rewrite big_const_seq iter_addn mul0n addn0.
+        }
+        rewrite big_mkcond /=.
+        rewrite exchange_big /=.
+        apply leq_trans with (n := \sum_(cpu < num_cpus | cpu \in alpha' tsk) 1);
+          first by rewrite sum1_card.
+        rewrite [\sum_(_ < _ | _) 1]big_mkcond /=.
+        apply leq_sum; intros cpu _.
+        unfold can_execute_on in *.
+        destruct (cpu \in alpha' (job_task j)) eqn:ALPHA'; rewrite -?JOBtsk ALPHA';
+          last by done.
+        move: (SUB (job_task j) (FROMTS j) cpu ALPHA') => SUBj.
+        move: (WORK j t BACK cpu SUBj) => [j_other /eqP SCHED]; unfold scheduled_on in *.
+        rewrite (bigD1_seq (job_task j_other)) /=; last by apply filter_uniq; destruct ts.
+        {
+          by rewrite {1}/task_scheduled_on SUBj SCHED eq_refl andTb.
+        }
+        {
+          rewrite mem_filter; apply/andP; split; last by apply FROMTS.
+          apply/andP; split; last first.
+          {
+            apply/existsP; exists cpu; apply/andP; split; first by rewrite -JOBtsk.
+            by apply APA with (t := t); apply/eqP.
+          }
+          apply DIFFTASK with (t := t); [by auto | by done |].
+          by apply/existsP; exists cpu; apply/eqP.
+        }
+      Qed.
+      
+      (* Let's define a predicate for whether a task is scheduled on (alpha tsk). *)
+      Let scheduled_on_alpha_tsk := fun t tsk_k =>
+        task_scheduled_on_affinity job_task sched (alpha tsk) tsk_k t.
+      
+      (* 5) Now we prove that, at all times that j is backlogged, the number
+            of tasks whose affinity intersects (alpha' tsk) that are in fact
+            scheduled on (alpha' tsk) is at least the size of (alpha' tsk).
+            This is required to prove lemma (6). *)
+      Lemma bertogna_edf_alpha'_is_full:
+        forall t,
+          job_arrival j <= t < job_arrival j + R ->
+          backlogged job_cost sched j t ->
+          count (scheduled_on_alpha_tsk t) (other_tasks_in alpha') >= #|alpha' tsk|.
+      Proof.
+        have COMP := bertogna_edf_all_previous_jobs_complete_by_their_period.       
+        rename H_work_conserving into WORK, H_respects_affinity into APA,
+               H_affinity_subset into SUB, H_job_of_tsk into JOBtsk,
+               H_all_jobs_from_taskset into FROMTS,
+               H_valid_task_parameters into PARAMS,
+               H_sequential_jobs into SEQ.
+        move => t /andP [GEt LTt] BACK. 
+        move: WORK  => WORK.
+        specialize (WORK j t BACK).
+        rewrite -size_filter.
+        apply leq_trans with (n := size (alpha' tsk));
+          first by apply card_size.
+        apply leq_trans with (n := size (pmap (fun cpu => sched cpu t) (enum (alpha' tsk)))).
+        {
+          rewrite size_pmap -size_filter.
+          apply uniq_leq_size; first by destruct (alpha' tsk).
+          intros cpu IN.
+          rewrite mem_filter; apply/andP; split; last by rewrite mem_enum.
+          feed (WORK cpu); first by apply SUB; rewrite ?FROMTS ?JOBtsk.
+          by move: WORK => [j_other /eqP SCHED]; rewrite SCHED.
+        }
+        {
+          apply leq_trans with (n := size (map (fun j: JobIn arr_seq => job_task j) (pmap (fun cpu => sched cpu t) (enum (alpha' tsk)))));
+            first by rewrite size_map.
+          apply uniq_leq_size.
+          {
+            rewrite map_inj_in_uniq.
+            {
+              apply pmap_inj_in_uniq; last by apply enum_uniq.
+              intros cpu1 cpu2 IN1 IN2 SCHED2.
+              destruct (sched cpu1 t) eqn:SCHED1; symmetry in SCHED2;
+                first by apply SEQ with (j := j0) (t := t).
+              rewrite 2!mem_enum in IN1 IN2.
+              exploit (WORK cpu1); first by apply SUB; rewrite ?FROMTS ?JOBtsk.
+              by move => [j_other SCHED]; rewrite /scheduled_on SCHED1 in SCHED.  
+            }
+            {
+              intros j1 j2 SCHED1 SCHED2 SAMEtsk.
+              rewrite 2!mem_pmap in SCHED1 SCHED2.
+              move: SCHED1 SCHED2 => /mapP [cpu1 IN1 SCHED1] /mapP [cpu2 IN2 SCHED2].
+              assert (PENDING1: pending job_cost sched j1 t).
+              {
+                apply scheduled_implies_pending; try by done.
+                by apply/existsP; exists cpu1; rewrite /scheduled_on -SCHED1.
+              }
+              assert (SCHED2': pending job_cost sched j2 t).
+              {
+                apply scheduled_implies_pending; try by done.
+                by apply/existsP; exists cpu2; rewrite /scheduled_on -SCHED2. 
+              }
+              apply platform_at_most_one_pending_job_of_each_task with (task_cost0 := task_cost)
+              (task_period0 := task_period) (task_deadline0 := task_deadline) (tsk0 := tsk)
+              (job_cost0 := job_cost) (job_task0 := job_task) (sched0 := sched) (j0 := j) (t0 := t);
+              rewrite ?JOBtsk ?SAMEtsk //; first by apply PARAMS; rewrite -JOBtsk FROMTS.
+              intros j0 tsk0 TSK0 LE.
+              by apply (COMP t); rewrite ?TSK0.
+            }
+          }
+          {
+            move => tsk' /mapP [j' IN EQtsk'].
+            rewrite mem_pmap in IN.
+            move: IN => /mapP [cpu INcpu SCHED'].
+            rewrite mem_enum in INcpu.
+            rewrite mem_filter; apply/andP; split.
+            {
+              apply/existsP; exists cpu; apply/andP; split;
+                 first by apply SUB; rewrite -?JOBtsk ?FROMTS ?JOBtsk.
+              by rewrite /task_scheduled_on -SCHED' EQtsk'.
+            }
+            rewrite EQtsk' mem_filter; apply/andP; split; last by apply FROMTS.
+            apply/andP; split; last first.
+            {
+              apply/existsP; exists cpu; apply/andP; split; first by done.
+              by apply APA with (t := t); rewrite /scheduled_on SCHED'.
+            }
+            {
+              apply/eqP; intro SAMEtsk.
+              move: BACK => /andP [PENDING NOTSCHED].
+              assert (SCHEDULED': scheduled sched j' t).
+              {
+                by apply/existsP; exists cpu; rewrite /scheduled_on -SCHED'.
+              }
+              move: (SCHEDULED') => PENDING'.
+              apply scheduled_implies_pending with (job_cost0 := job_cost) in PENDING';
+                try by done.
+              assert (BUG: j = j').
+              {
+                apply platform_at_most_one_pending_job_of_each_task with (task_cost0 := task_cost)
+                (task_period0 := task_period) (task_deadline0 := task_deadline) (tsk0 := tsk)
+                (job_cost0 := job_cost) (job_task0 := job_task) (sched0 := sched) (j0 := j) (t0 := t);
+                rewrite ?JOBtsk ?SAMEtsk //; first by apply PARAMS; rewrite -JOBtsk FROMTS.
+                intros j0 tsk0 TSK0 LE.
+                by apply (COMP t); rewrite ?TSK0.
+              }
+              by rewrite BUG SCHEDULED' in NOTSCHED.
+            }
+          }
+        }
+      Qed.
+
+      (* Before stating the next lemma, let (num_tasks_exceeding delta) be the
+         number of interfering tasks that can execute on (alpha' tsk) whose
+         interference x is larger than delta. *)
+      Let num_tasks_exceeding delta := count (fun i => x i >= delta) (other_tasks_in alpha').
+
+      (* 6) Now we prove that, for any delta, if (num_task_exceeding delta > 0), then the
+            cumulative interference caused by the complementary set of interfering tasks fills
+            the remaining, not-completely-full (#|alpha' tsk| - num_tasks_exceeding delta)
+            processors. *)
+      Lemma bertogna_edf_interference_in_non_full_processors :
+        forall delta,
+          0 < num_tasks_exceeding delta < #|alpha' tsk| ->
+          \sum_(i <- other_tasks_in alpha' | x i < delta) x i >= delta * (#|alpha' tsk| - num_tasks_exceeding delta).
+      Proof.
+        have COMP := bertogna_edf_all_previous_jobs_complete_by_their_period.
+        have ATMOST := platform_at_most_one_pending_job_of_each_task.
+        have INV := bertogna_edf_alpha'_is_full.
+        rename H_all_jobs_from_taskset into FROMTS,
+               H_valid_task_parameters into PARAMS,
+               H_job_of_tsk into JOBtsk,
+               H_sporadic_tasks into SPO,
+               H_tsk_R_in_rt_bounds into INbounds,
+               H_all_previous_jobs_completed_on_time into BEFOREok,
+               H_tasks_miss_no_deadlines into NOMISS,
+               H_constrained_deadlines into RESTR,
+               H_sequential_jobs into SEQ.
+        unfold sporadic_task_model in *.
+        move => delta /andP [HAS LT]. 
+        rewrite -has_count in HAS.
+
+        set some_interference_A := fun t =>
+          has (fun tsk_k => backlogged job_cost sched j t &&
+                            (x tsk_k >= delta) &&
+                            scheduled_on_alpha_tsk t tsk_k)
+            (other_tasks_in alpha').
+        set total_interference_B := fun t =>
+            backlogged job_cost sched j t *
+            count (fun tsk_k => (x tsk_k < delta) &&
+                  scheduled_on_alpha_tsk t tsk_k) (other_tasks_in alpha').
+
+        apply leq_trans with ((\sum_(job_arrival j <= t < job_arrival j + R)
+                              some_interference_A t) * (#|alpha' tsk| - num_tasks_exceeding delta)).
+        {
+          rewrite leq_mul2r; apply/orP; right.
+          move: HAS => /hasP HAS; destruct HAS as [tsk_a INa LEa].
+          apply leq_trans with (n := x tsk_a); first by apply LEa.
+          unfold x, task_interference, some_interference_A.
+          apply leq_sum_nat; move => t /andP [GEt LTt] _.
+          destruct (backlogged job_cost sched j t) eqn:BACK;
+            last by rewrite (eq_bigr (fun x => 0)); [by simpl_sum_const | by ins].
+          destruct ([exists cpu, can_execute_on alpha (job_task j) cpu &&
+                              task_scheduled_on job_task sched tsk_a cpu t]) eqn:SCHED;
+            last first.
+          {
+            apply negbT in SCHED; rewrite negb_exists in SCHED; move: SCHED => /forallP ALL.
+            rewrite (eq_bigr (fun x => 0)); first by simpl_sum_const.
+            by intros cpu _; specialize (ALL cpu); apply negbTE in ALL; rewrite ALL.
+          }
+          move: SCHED => /existsP [cpu /andP [CAN SCHED]].
+          apply leq_trans with (n := 1); last first.
+          {
+            rewrite lt0b; apply/hasP; exists tsk_a; first by done.
+            rewrite LEa 2!andTb; apply/existsP; exists cpu.
+            by apply/andP; split; first by rewrite -JOBtsk.
+          }
+          rewrite (bigD1 cpu) /= // SCHED.
+          rewrite (eq_bigr (fun x => 0)); first by simpl_sum_const; rewrite leq_b1.
+          intros cpu' DIFF.
+          apply/eqP; rewrite eqb0; apply/negP.
+          move => /andP [ALPHA' SCHED'].
+          move: DIFF => /negP DIFF; apply DIFF; apply/eqP.
+          unfold task_scheduled_on in *.
+          destruct (sched cpu t) as [j1|] eqn:SCHED1; last by done.
+          destruct (sched cpu' t) as [j2|] eqn:SCHED2; last by done.
+          move: SCHED SCHED' => /eqP JOB /eqP JOB'.
+          subst tsk_a; symmetry in JOB'.
+          assert (BUG: j1 = j2).
+          {
+            assert (PENDING1: pending job_cost sched j1 t).
+            {
+              apply scheduled_implies_pending; try by done.
+              by apply/existsP; exists cpu; rewrite /scheduled_on -SCHED1.
+            }
+            assert (SCHED2': pending job_cost sched j2 t).
+            {
+              apply scheduled_implies_pending; try by done.
+              by apply/existsP; exists cpu'; rewrite /scheduled_on -SCHED2. 
+            }
+            apply platform_at_most_one_pending_job_of_each_task with (task_cost0 := task_cost)
+              (task_period0 := task_period) (task_deadline0 := task_deadline) (tsk0 := tsk)
+              (job_cost0 := job_cost) (job_task0 := job_task) (sched0 := sched) (j0 := j) (t0 := t);
+            rewrite ?JOBtsk ?SAMEtsk //; first by apply PARAMS; rewrite -JOBtsk FROMTS.
+            intros j0 tsk0 TSK0 LE.
+            by apply (COMP t); rewrite ?TSK0.
+          }
+          by subst j2; apply SEQ with (j := j1) (t := t).
+        }
+
+        apply leq_trans with (\sum_(job_arrival j <= t < job_arrival j + R)
+                                   total_interference_B t).
+        {
+          rewrite big_distrl /=.
+          apply leq_sum_nat; move => t LEt _.
+          unfold some_interference_A, total_interference_B. 
+          destruct (backlogged job_cost sched j t) eqn:BACK;
+            [rewrite mul1n /= | by rewrite has_pred0 //].
+          
+          destruct (has (fun tsk_k : sporadic_task => (delta <= x tsk_k) &&
+                     scheduled_on_alpha_tsk t tsk_k) (other_tasks_in alpha')) eqn:HAS';
+            last by done.
+          rewrite mul1n; move: HAS => /hasP [tsk_k INk LEk].
+          unfold num_tasks_exceeding.
+          apply leq_trans with (n := #|alpha' tsk| -
+                       count (fun i => (x i >= delta) &&
+                          scheduled_on_alpha_tsk t i) (other_tasks_in alpha')).
+          {
+            apply leq_sub2l.
+            rewrite -2!sum1_count big_mkcond /=.
+            rewrite [\sum_(_ <- _ | _ <= _)_]big_mkcond /=.
+            apply leq_sum; intros i _.
+            by destruct (scheduled_on_alpha_tsk t i);
+              [by rewrite andbT | by rewrite andbF].
+          }
+          rewrite -count_filter -[count _ (other_tasks_in _)]count_filter.
+          eapply leq_trans with (n := count (predC (fun tsk => delta <= x tsk)) _);
+            last by apply eq_leq, eq_in_count; red; ins; rewrite ltnNge.
+          rewrite leq_subLR count_predC size_filter.
+          move: INV => INV. by apply (INV t).
+        }
+        {
+          unfold x at 2, total_interference_B.
+          rewrite exchange_big /=; apply leq_sum; intros t _.
+          destruct (backlogged job_cost sched j t) eqn:BACK; last by ins.
+          rewrite mul1n -sum1_count.
+          rewrite big_mkcond [\sum_(i <- other_tasks_in alpha' | _ < _) _]big_mkcond /=.
+          apply leq_sum_seq; move => tsk_k IN _.
+          destruct (x tsk_k < delta); [rewrite andTb | by rewrite andFb].
+          destruct (scheduled_on_alpha_tsk t tsk_k) eqn:SCHED; last by done.
+          move: SCHED => /existsP [cpu /andP [ALPHA SCHED]].
+          rewrite (bigD1 cpu) /= // JOBtsk SCHED andbT.
+          by rewrite /can_execute_on ALPHA.
+        }
+      Qed.
+
+      (* 7) Based on lemma (6), we prove that, for any interval delta, if the sum of per-task
+            interference exceeds (delta * |alpha' tsk|), the same applies for the
+            sum of the minimum of the interference and delta. *)
+      Lemma bertogna_edf_minimum_exceeds_interference :
+        forall delta,
+          \sum_(tsk_k <- other_tasks_in alpha') x tsk_k >= delta * #|alpha' tsk| ->
+             \sum_(tsk_k <- other_tasks_in alpha') minn (x tsk_k) delta >=
+             delta * #|alpha' tsk|.
+      Proof.
+        intros delta SUMLESS.
+        set more_interf := fun tsk_k => x tsk_k >= delta.
+        rewrite [\sum_(_ <- _) minn _ _](bigID more_interf) /=.
+        unfold more_interf, minn.
+        rewrite [\sum_(_ <- _ | delta <= _)_](eq_bigr (fun i => delta));
+          last by intros i COND; rewrite leqNgt in COND; destruct (delta > x i).
+        rewrite [\sum_(_ <- _ | ~~_)_](eq_big (fun i => x i < delta)
+                                              (fun i => x i));
+          [| by red; ins; rewrite ltnNge
+           | by intros i COND; rewrite -ltnNge in COND; rewrite COND].
+
+        (* Case 1: num_tasks_exceeding = 0 *)
+        destruct (~~ has (fun i => delta <= x i) (other_tasks_in alpha')) eqn:HASa.
+        {
+          rewrite [\sum_(_ <- _ | _ <= _) _]big_hasC; last by apply HASa.
+          rewrite big_seq_cond; move: HASa => /hasPn HASa.
+          rewrite add0n (eq_bigl (fun i => (i \in other_tasks_in alpha') && true));
+            last by red; intros tsk_k; destruct (tsk_k \in other_tasks_in alpha') eqn:INk;
+              [by rewrite andTb ltnNge; apply HASa | by rewrite andFb].
+          by rewrite -big_seq_cond.
+        } apply negbFE in HASa.
+        
+        (* Case 2: num_tasks_exceeding >= #|alpha' tsk| *)
+        destruct (num_tasks_exceeding delta >= #|alpha' tsk|) eqn:CARD.
+        {
+          apply leq_trans with (delta * num_tasks_exceeding delta);
+            first by rewrite leq_mul2l; apply/orP; right.
+          unfold num_tasks_exceeding; rewrite -sum1_count big_distrr /=.
+          rewrite -[\sum_(_ <- _ | _) _]addn0.
+          by apply leq_add; [by apply leq_sum; ins; rewrite muln1|by ins].
+        } apply negbT in CARD; rewrite -ltnNge in CARD.
+
+        (* Case 3: num_tasks_exceeding < #|alpha' tsk| *)
+        rewrite big_const_seq iter_addn addn0; fold num_tasks_exceeding.
+        apply leq_trans with (n := delta * num_tasks_exceeding delta +
+                                   delta * (#|alpha' tsk| - num_tasks_exceeding delta));
+          first by rewrite -mulnDr subnKC //; apply ltnW.
+        rewrite leq_add2l bertogna_edf_interference_in_non_full_processors //.
+        by apply/andP; split; [by rewrite -has_count | by done].
+      Qed.
+
+      (* 8) Note that lemma (7) only refers to interference within subaffinity (alpha' tsk).
+            To reason about the interference incurred by job j in its complete affinity,
+            we prove that an exceeding interference on affinity (alpha tsk) also
+            implies an exceeding interference on the subaffinity (alpha' tsk). *)
+      Lemma bertogna_edf_interference_on_subaffinity :
+        forall delta,
+          \sum_(tsk_k <- other_tasks_in alpha) x tsk_k >= delta * #|alpha tsk| ->
+          \sum_(tsk_k <- other_tasks_in alpha') x tsk_k >= delta * #|alpha' tsk|.
+      Proof.
+        have ALL := bertogna_edf_all_cpus_in_affinity_busy.
+        have SUB := bertogna_edf_all_cpus_in_subaffinity_busy.
+        rename H_at_least_one_cpu into NONEMPTY',
+               H_job_of_tsk into JOBtsk, H_affinity_subset into SUBSET,
+               H_all_jobs_from_taskset into FROMTS.
+        intros delta LE.
+        rewrite ALL in LE.
+        apply leq_trans with (n := X * #|alpha' tsk|); last by apply SUB.
+        rewrite leq_mul2r in LE;  move: LE => /orP [/eqP EMPTY | LEx];
+          last by rewrite leq_mul2r; apply/orP; right.
+        assert (NONEMPTY: #|alpha tsk| > 0).
+        {
+          apply leq_trans with (n := #|alpha' tsk|);
+            last by apply leq_subaffinity, SUBSET; rewrite -JOBtsk FROMTS.
+          by apply NONEMPTY'; rewrite -JOBtsk FROMTS.
+        }
+        by rewrite EMPTY ltnn in NONEMPTY.
+      Qed.
+ 
+      (* 9) Next, using lemmas (0), (3), (7) and (8), we prove that the reduction-based
+            interference bound is not enough to cover the sum of the minima over all tasks
+            (artifact of the proof by contradiction). *)
+      Lemma bertogna_edf_sum_exceeds_total_interference:
+        \sum_((tsk_other, R_other) <- rt_bounds | other_task_in (alpha' tsk) tsk_other)
+          minn (x tsk_other) (R - task_cost tsk + 1) > I tsk R.
+      Proof.
+        have GE_COST := bertogna_edf_R_other_ge_cost.
+        have EXCEEDS := bertogna_edf_minimum_exceeds_interference.
+        have SUB := bertogna_edf_interference_on_subaffinity.
+        have ALLBUSY := bertogna_edf_all_cpus_in_affinity_busy.
+        have TOOMUCH := bertogna_edf_too_much_interference.
+        rename H_rt_bounds_contains_all_tasks into UNZIP,
+               H_job_of_tsk into JOBtsk,
+               H_all_jobs_from_taskset into FROMTS,
+               H_response_time_is_fixed_point into REC.
+        apply leq_trans with (n := \sum_(tsk_other <- other_tasks_in alpha')
+                                     minn (x tsk_other) (R - task_cost tsk + 1));
+          last first.
+        {
+          rewrite (eq_bigr (fun i => minn (x (fst i)) (R - task_cost tsk + 1)));
+            last by ins; destruct i.
+          assert (FILTER: filter (other_task_in (alpha' tsk)) (unzip1 rt_bounds) =
+                          filter (other_task_in (alpha' tsk)) ts).
+            by f_equal.
+          unfold other_tasks_in; rewrite -FILTER; clear FILTER.
+          rewrite -[\sum_(_ <- rt_bounds | _)_]big_filter.
+          assert (SUBST: [seq i <- rt_bounds
+                 | let '(tsk_other, _) := i in
+                   other_task_in (alpha' tsk) tsk_other] =
+                         [seq i <- rt_bounds
+                           | other_task_in (alpha' tsk) (fst i)]).
+          {
+            by apply eq_filter; red; intro i; destruct i.
+          } rewrite SUBST; clear SUBST.         
+          have MAP := big_map (fun x => fst x) (fun i => true)
+                              (fun i => minn (x i) (R - task_cost tsk + 1)).
+          by rewrite -MAP; apply eq_leq; f_equal; rewrite filter_map.
+        }
+        
+        apply ltn_div_trunc with (d := #|alpha' tsk|);
+          first by apply H_at_least_one_cpu; rewrite -JOBtsk FROMTS.
+        rewrite -(ltn_add2l (task_cost tsk)) -REC; last first. by done.
+        rewrite -addn1 -leq_subLR.
+        rewrite -[R + 1 - _]subh1; last by apply GE_COST.
+        rewrite leq_divRL;
+          last by apply H_at_least_one_cpu; rewrite -JOBtsk FROMTS.
+        apply EXCEEDS, SUB.
+        apply leq_trans with (n := X * #|alpha tsk|); last by rewrite ALLBUSY.
+        by rewrite leq_mul2r; apply/orP; right; apply TOOMUCH.
+      Qed.
+
+      (* 10) After concluding that the sum of the minima exceeds (R - e_i + 1),
+            we prove that there exists a tuple (tsk_k, R_k) that satisfies
+            min (x_k, R - e_i + 1) > min (W_k, I_edf, R - e_i + 1).
+            This implies that either x_k > W_k or x_k > I_edf, which is a contradiction,
+            since both W_k and I_edf are valid task interference bounds. *)
+      Lemma bertogna_edf_exists_task_that_exceeds_bound :
+        exists tsk_other R_other,
+          (tsk_other, R_other) \in rt_bounds /\
+          (minn (x tsk_other) (R - task_cost tsk + 1) > interference_bound tsk_other R_other).
+      Proof.
+        have SUM := bertogna_edf_sum_exceeds_total_interference.
+        have BOUND := bertogna_edf_workload_bounds_interference.
+        have EDFBOUND := bertogna_edf_specific_bound_holds.
+        rename H_rt_bounds_contains_all_tasks into UNZIP.
+        assert (HAS: has (fun tup : task_with_response_time =>
+                            let (tsk_other, R_other) := tup in
+                (tsk_other \in ts) && other_task_in (alpha' tsk) tsk_other &&
+                (minn (x tsk_other) (R - task_cost tsk + 1)  > interference_bound tsk_other R_other))
+                         rt_bounds).
+        {
+          apply/negP; unfold not; intro NOTHAS.
+          move: NOTHAS => /negP /hasPn ALL.
+          rewrite -[_ < _]negbK in SUM.
+          move: SUM => /negP SUM; apply SUM; rewrite -leqNgt.
+          unfold I, total_interference_bound_edf.
+          rewrite big_seq_cond [\sum_(_ <- _ | let _ := _ in _)_]big_seq_cond.
+          apply leq_sum; move => tsk_k /andP [INBOUNDSk INTERFk]; destruct tsk_k as [tsk_k R_k].
+          specialize (ALL (tsk_k, R_k) INBOUNDSk).
+          unfold interference_bound_edf; simpl in *.
+          rewrite leq_min; apply/andP; split.
+          {
+            unfold interference_bound; rewrite leq_min; apply/andP; split;
+              last by rewrite geq_minr.
+            by apply leq_trans with (n := x tsk_k);
+              [by rewrite geq_minl | by apply BOUND].
+          }
+          {
+            apply leq_trans with (n := x tsk_k);
+              [by rewrite geq_minl | by apply EDFBOUND].
+          }
+        }
+        move: HAS => /hasP HAS; destruct HAS as [[tsk_k R_k] HPk MIN].
+        move: MIN => /andP [/andP [INts INTERFk] MINk].
+        by exists tsk_k, R_k; repeat split.
+      Qed.
+
+      End DerivingContradiction.
+      
+    End Lemmas.
+
+    (* Using the lemmas above, we now prove that any response-time bound
+       obtained from the analysis is safe. *)
+    Section MainProof.
+      
+      (* Let (tsk, R) be any task to be analyzed, with its response-time bound R. *)
+      Variable tsk: sporadic_task.
+      Variable R: time.
+      Hypothesis H_tsk_R_in_rt_bounds: (tsk, R) \in rt_bounds.
+
+      (* Then, R bounds the response-time of tsk. *)
+      Theorem bertogna_cirinei_response_time_bound_edf :
+        response_time_bounded_by tsk R.
+      Proof.
+        intros j JOBtsk.
+       
+        (* First, rewrite the claim in terms of the *absolute* response-time bound (arrival + R) *)
+        remember (job_arrival j + R) as ctime.
+
+        revert H_tsk_R_in_rt_bounds.
+        generalize dependent R; generalize dependent tsk; generalize dependent j.
+      
+        (* Now, we apply strong induction on the absolute response-time bound. *)
+        induction ctime as [ctime IH] using strong_ind.
+
+        intros j tsk' JOBtsk R' EQc INbounds; subst ctime.
+
+        (* First, let's simplify the induction hypothesis. *)
+        assert (BEFOREok: forall (j0: JobIn arr_seq) tsk R0,
+                                 job_task j0 = tsk ->
+                               (tsk, R0) \in rt_bounds ->
+                               job_arrival j0 + R0 < job_arrival j + R' ->
+                               service sched j0 (job_arrival j0 + R0) == job_cost j0).
+        {
+            by ins; apply IH with (tsk := tsk0) (R := R0).
+        }
+        clear IH.
+        
+        (* The proof follows by contradiction. Assume that job j does not complete by its
+           response-time bound. By the induction hypothesis, all jobs with absolute
+           response-time bound t < (job_arrival j + R) have correct response-time bounds. *)
+        destruct (completed job_cost sched j (job_arrival j + R')) eqn:NOTCOMP;
+          first by done.
+        apply negbT in NOTCOMP; exfalso.
+        
+        (* Next, we derive a contradiction using the previous lemmas. *)
+        exploit (bertogna_edf_exists_task_that_exceeds_bound tsk' R' INbounds j JOBtsk NOTCOMP).
+        {
+          by ins; apply IH with (tsk := tsk_other) (R := R_other).
+        } 
+        intro EX; destruct EX as [tsk_other [R_other [HP LTmin]]].
+        unfold interference_bound_edf, interference_bound_generic in LTmin.
+        rewrite minnAC in LTmin; apply min_lt_same in LTmin.
+        have BASICBOUND := bertogna_edf_workload_bounds_interference R' j BEFOREok tsk_other R_other HP.
+        have EDFBOUND := (bertogna_edf_specific_bound_holds tsk' R' INbounds j JOBtsk BEFOREok tsk_other R_other HP).
+        unfold minn in LTmin; clear -LTmin HP BASICBOUND EDFBOUND tsk; desf.
+        {
+          by apply (leq_ltn_trans BASICBOUND) in LTmin; rewrite ltnn in LTmin. 
+        }
+        {
+          by apply (leq_ltn_trans EDFBOUND) in LTmin; rewrite ltnn in LTmin.
+        }
+      Qed.
+
+    End MainProof.
+    
+  End ResponseTimeBound.
+
+End ResponseTimeAnalysisEDF.
\ No newline at end of file
diff --git a/analysis/apa/bertogna_fp_comp.v b/analysis/apa/bertogna_fp_comp.v
new file mode 100644
index 0000000000000000000000000000000000000000..b8a18df38d37f5529c442a9ba2e4eb50d6768264
--- /dev/null
+++ b/analysis/apa/bertogna_fp_comp.v
@@ -0,0 +1,731 @@
+Require Import rt.util.all rt.util.divround.
+Require Import rt.analysis.apa.bertogna_fp_theory.
+From mathcomp Require Import ssreflect ssrbool ssrfun eqtype ssrnat seq fintype bigop div path.
+
+Module ResponseTimeIterationFP.
+
+  Import ResponseTimeAnalysisFP.
+
+  (* In this section, we define the algorithm corresponding to the APA-reduction
+     of Bertogna and Cirinei's RTA for FP scheduling. *)
+  Section Analysis.
+    
+    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_cost: Job -> time.
+    Variable job_deadline: Job -> time.
+    Variable job_task: Job -> sporadic_task.
+
+    (* As input for each iteration of the algorithm, we consider pairs
+       of tasks and computed response-time bounds. *)
+    Let task_with_response_time := (sporadic_task * time)%type.
+    
+    (* Consider a platform with num_cpus processors, ... *)
+    Variable num_cpus: nat.
+
+    (* ..., and priorities based on an FP policy. *)
+    Variable higher_priority: FP_policy sporadic_task.
+
+    (* Assume that every task has a processor affinity alpha. *)
+    Variable alpha: task_affinity sporadic_task num_cpus.
+
+    (* For the reduction to subproblems, consider a subaffinity alpha' for each task. *)
+    Variable alpha': task_affinity sporadic_task num_cpus.
+    
+    (* Next we define the fixed-point iteration for the APA-reduction of
+       Bertogna and Cirineis's response-time analysis. *)
+    
+    (* First, given a sequence of pairs R_prev = <..., (tsk_hp, R_hp)> of
+       response-time bounds for the higher-priority tasks, we define an
+       iteration that computes the response-time bound of the current task:
+
+           R_tsk (0) = task_cost tsk
+           R_tsk (step + 1) =  f (R step),
+
+       where f is the response-time recurrence, step is the number of iterations,
+       and R_tsk (0) is the initial state. *)
+    Definition per_task_rta (tsk: sporadic_task)
+                            (R_prev: seq task_with_response_time) (step: nat) :=
+      iter step
+        (fun t => task_cost tsk +
+                  div_floor
+                    (total_interference_bound_fp task_cost task_period alpha tsk
+                                                 (alpha' tsk) R_prev t higher_priority)
+                    #|alpha' tsk|)
+        (task_cost tsk).
+
+    (* To ensure that the iteration converges, we will apply per_task_rta
+       a "sufficient" number of times: task_deadline tsk - task_cost tsk + 1.
+       This corresponds to the time complexity of the iteration. *)
+    Definition max_steps (tsk: sporadic_task) := task_deadline tsk - task_cost tsk + 1.
+    
+    (* Next we compute the response-time bounds for the entire task set.
+       Since high-priority tasks may not be schedulable, we allow the
+       computation to fail.
+       Thus, given the response-time bound of previous tasks, we either
+       (a) append the computed response-time bound (tsk, R) of the current task
+           to the list of pairs, or,
+       (b) return None if the response-time analysis failed. *)
+    Definition fp_bound_of_task hp_pairs tsk :=
+      if hp_pairs is Some rt_bounds then
+        let R := per_task_rta tsk rt_bounds (max_steps tsk) in
+          if R <= task_deadline tsk then
+            Some (rcons rt_bounds (tsk, R))
+          else None
+      else None.
+
+    (* The response-time analysis for a given task set is defined
+       as a left-fold (reduce) based on the function above.
+       This either returns a list of task and response-time bounds, or None. *)
+    Definition fp_claimed_bounds (ts: seq sporadic_task) :=
+      foldl fp_bound_of_task (Some [::]) ts.
+
+    (* The schedulability test simply checks if we got a list of
+       response-time bounds (i.e., if the computation did not fail). *)
+    Definition fp_schedulable (ts: seq sporadic_task) :=
+      fp_claimed_bounds ts != None.
+    
+    (* In the following section, we prove several helper lemmas about the
+       list of response-time bounds. The results seem trivial, but must be proven
+       nonetheless since the list of response-time bounds is computed with
+       a specific algorithm and there are no lemmas in the library for that. *)
+    Section SimpleLemmas.
+
+      (* First, we show that the first component of the computed list is the set of tasks. *)
+      Lemma fp_claimed_bounds_unzip :
+        forall ts hp_bounds, 
+          fp_claimed_bounds ts = Some hp_bounds ->
+          unzip1 hp_bounds = ts.
+      Proof.
+        unfold fp_claimed_bounds in *; intros ts.
+        induction ts using last_ind; first by destruct hp_bounds.
+        {
+          intros hp_bounds SOME.
+          destruct (lastP hp_bounds) as [| hp_bounds'].
+          {
+            rewrite -cats1 foldl_cat /= in SOME.
+            unfold fp_bound_of_task at 1 in SOME; simpl in *; desf.
+            by destruct l.
+          }
+          rewrite -cats1 foldl_cat /= in SOME.
+          unfold fp_bound_of_task at 1 in SOME; simpl in *; desf.
+          move: H0 => /eqP EQSEQ.
+          rewrite eqseq_rcons in EQSEQ.
+          move: EQSEQ => /andP [/eqP SUBST /eqP EQSEQ]; subst.
+          unfold unzip1; rewrite map_rcons; f_equal.
+          by apply IHts.
+        }
+      Qed.
+      
+      (* Next, we show that some properties of the analysis are preserved for the
+         prefixes of the list: (a) the tasks do not change, (b) R <= deadline,
+         (c) R is computed using the response-time equation, ... *) 
+      Lemma fp_claimed_bounds_rcons :
+        forall ts' hp_bounds tsk1 tsk2 R,
+          (fp_claimed_bounds (rcons ts' tsk1) = Some (rcons hp_bounds (tsk2, R)) ->
+           (fp_claimed_bounds ts' = Some hp_bounds /\
+            tsk1 = tsk2 /\
+            R = per_task_rta tsk1 hp_bounds (max_steps tsk1) /\
+            R <= task_deadline tsk1)).
+      Proof.
+        intros ts hp_bounds tsk tsk' R.
+        rewrite -cats1.
+        unfold fp_claimed_bounds in *.
+        rewrite foldl_cat /=.
+        unfold fp_bound_of_task at 1; simpl; desf.
+        intros EQ; inversion EQ; move: EQ H0 => _ /eqP EQ.
+        rewrite eqseq_rcons in EQ.
+        move: EQ => /andP [/eqP EQ /eqP RESP].
+        by inversion RESP; repeat split; subst.
+      Qed.
+
+      (* ..., which implies that any prefix of the computation is the computation
+         of the prefix. *)
+      Lemma fp_claimed_bounds_take :
+        forall ts hp_bounds i,
+          fp_claimed_bounds ts = Some hp_bounds ->
+          i <= size hp_bounds ->
+          fp_claimed_bounds (take i ts) = Some (take i hp_bounds).
+      Proof.                                                        
+        intros ts hp_bounds i SOME LTi.
+        have UNZIP := fp_claimed_bounds_unzip ts hp_bounds SOME.
+        rewrite <- UNZIP in *.
+        rewrite -[hp_bounds]take_size /unzip1 map_take in SOME.
+        fold (unzip1 hp_bounds) in *; clear UNZIP.
+        rewrite leq_eqVlt in LTi.
+        move: LTi => /orP [/eqP EQ | LTi]; first by subst.
+        remember (size hp_bounds) as len; apply eq_leq in Heqlen.
+        induction len; first by rewrite ltn0 in LTi.
+        {
+          assert (TAKElen: fp_claimed_bounds (take len (unzip1 (hp_bounds))) =
+                             Some (take len (hp_bounds))).
+          {
+            assert (exists p, p \in hp_bounds).
+            {
+              destruct hp_bounds; first by rewrite ltn0 in Heqlen.
+              by exists t; rewrite in_cons eq_refl orTb.
+            } destruct H as [[tsk R] _].
+             rewrite (take_nth tsk) in SOME; last by rewrite size_map.
+            rewrite (take_nth (tsk,R)) in SOME; last by done.
+            destruct (nth (tsk, R) hp_bounds len) as [tsk_len R_len].
+            by apply fp_claimed_bounds_rcons in SOME; des.
+          }
+          rewrite ltnS leq_eqVlt in LTi.
+          move: LTi => /orP [/eqP EQ | LESS]; first by subst.
+          apply ltnW in Heqlen.
+          by specialize (IHlen Heqlen TAKElen LESS).
+        }
+      Qed.
+      
+      (* If the analysis suceeds, the computed response-time bounds are no larger
+         than the deadlines ... *)
+      Lemma fp_claimed_bounds_le_deadline :
+        forall ts' rt_bounds tsk R,
+          fp_claimed_bounds ts' = Some rt_bounds ->
+          (tsk, R) \in rt_bounds ->
+          R <= task_deadline tsk.
+      Proof.
+        intros ts; induction ts as [| ts' tsk_lst] using last_ind.
+        {
+          intros rt_bounds tsk R SOME IN.
+          by inversion SOME; subst; rewrite in_nil in IN.
+        }
+        {
+          intros rt_bounds tsk_i R SOME IN.
+          destruct (lastP rt_bounds) as [|rt_bounds (tsk_lst', R_lst)];
+            first by rewrite in_nil in IN.
+          rewrite mem_rcons in_cons in IN; move: IN => /orP IN.
+          destruct IN as [LAST | FRONT].
+          {
+            move: LAST => /eqP LAST.
+            rewrite -cats1 in SOME.
+            unfold fp_claimed_bounds in *.
+            rewrite foldl_cat /= in SOME.
+            unfold fp_bound_of_task in SOME.
+            desf; rename H0 into EQ.
+            move: EQ => /eqP EQ.
+            rewrite eqseq_rcons in EQ.
+            move: EQ => /andP [_ /eqP EQ].
+            inversion EQ; subst.
+            by apply Heq0.
+          }
+          {
+            apply IHts with (rt_bounds := rt_bounds); last by ins.
+            by apply fp_claimed_bounds_rcons in SOME; des.
+          }
+        }
+      Qed.
+      
+      (* ... and no smaller than the task costs. *)
+      Lemma fp_claimed_bounds_ge_cost :
+        forall ts' rt_bounds tsk R,
+          fp_claimed_bounds ts' = Some rt_bounds ->
+          (tsk, R) \in rt_bounds ->
+          R >= task_cost tsk.
+      Proof.
+        intros ts; induction ts as [| ts' tsk_lst] using last_ind.
+        {
+          intros rt_bounds tsk R SOME IN.
+          by inversion SOME; subst; rewrite in_nil in IN.
+        }
+        {
+          intros rt_bounds tsk_i R SOME IN.
+          destruct (lastP rt_bounds) as [|rt_bounds (tsk_lst', R_lst)];
+            first by rewrite in_nil in IN.
+          rewrite mem_rcons in_cons in IN; move: IN => /orP IN.
+          destruct IN as [LAST | FRONT].
+          {
+            move: LAST => /eqP LAST.
+            rewrite -cats1 in SOME.
+            unfold fp_claimed_bounds in *.
+            rewrite foldl_cat /= in SOME.
+            unfold fp_bound_of_task in SOME.
+            desf; rename H0 into EQ.
+            move: EQ => /eqP EQ.
+            rewrite eqseq_rcons in EQ.
+            move: EQ => /andP [_ /eqP EQ].
+            inversion EQ; subst.
+            by destruct (max_steps tsk_lst');
+              [by apply leqnn | by apply leq_addr].
+          }
+          {
+            apply IHts with (rt_bounds := rt_bounds); last by ins.
+            by apply fp_claimed_bounds_rcons in SOME; des.
+          }
+        }
+      Qed.
+
+      (* Short lemma about unfolding the iteration one step. *)
+      Lemma per_task_rta_fold :
+        forall tsk rt_bounds,
+          task_cost tsk +
+           div_floor (total_interference_bound_fp task_cost task_period alpha tsk (alpha' tsk) rt_bounds
+                     (per_task_rta tsk rt_bounds (max_steps tsk)) higher_priority) #|alpha' tsk|
+          = per_task_rta tsk rt_bounds (max_steps tsk).+1.
+      Proof.
+          by done.
+      Qed.
+
+    End SimpleLemmas.
+
+    (* In this section, we prove that if the task set is sorted by priority,
+       the tasks in fp_claimed_bounds are interfering tasks.  *)
+    Section HighPriorityTasks.
+
+      (* Consider a list of previous tasks and a task tsk to be analyzed. *)
+      Variable ts: taskset_of sporadic_task.
+
+      (* Assume that the task set is sorted by unique priorities, ... *)
+      Hypothesis H_task_set_is_sorted: sorted higher_priority ts.
+      Hypothesis H_task_set_has_unique_priorities:
+        FP_is_antisymmetric_over_task_set higher_priority ts.
+
+      (* ...the priority order is transitive, ...*)
+      Hypothesis H_priority_transitive: FP_is_transitive higher_priority.
+      
+      (* ... and that the response-time analysis succeeds. *)
+      Variable hp_bounds: seq task_with_response_time.
+      Variable R: time.
+      Hypothesis H_analysis_succeeds: fp_claimed_bounds ts = Some hp_bounds.
+
+      (* Let's refer to tasks by index. *)
+      Variable elem: sporadic_task.
+      Let TASK := nth elem ts.
+
+      (* We prove that higher-priority tasks have smaller index. *)
+      Lemma fp_claimed_bounds_hp_tasks_have_smaller_index :
+        forall hp_idx idx,
+          hp_idx < size ts ->
+          idx < size ts ->
+          hp_idx != idx ->
+          higher_priority (TASK hp_idx) (TASK idx) ->
+          hp_idx < idx.
+      Proof.
+        unfold TASK; clear TASK.
+        rename ts into ts'; destruct ts' as [ts UNIQ]; simpl in *.
+        intros hp_idx idx LThp LT NEQ HP.
+        rewrite ltn_neqAle; apply/andP; split; first by done.
+        by apply sorted_rel_implies_le_idx with (leT := higher_priority) (s := ts) (x0 := elem).
+      Qed.
+      
+    End HighPriorityTasks.
+
+    (* In this section, we show that the fixed-point iteration converges. *)
+    Section Convergence.
+
+      (* Consider any sequence with higher-priority tasks. *)
+      Variable ts_hp: seq sporadic_task.
+
+      (* Assume that the response-time analysis succeeds for the higher-priority tasks. *)
+      Variable rt_bounds: seq task_with_response_time.
+      Hypothesis H_test_succeeds: fp_claimed_bounds ts_hp = Some rt_bounds.
+
+      (* Consider any task tsk to be analyzed, ... *)
+      Variable tsk: sporadic_task.
+
+      (* ... and assume all tasks have valid parameters. *)
+      Hypothesis H_valid_task_parameters:
+        valid_sporadic_taskset task_cost task_period task_deadline (rcons ts_hp tsk).
+
+      (* To simplify, let f denote the fixed-point iteration. *)
+      Let f := per_task_rta tsk rt_bounds.
+
+      (* Assume that f (max_steps tsk) is no larger than the deadline. *)
+      Hypothesis H_no_larger_than_deadline: f (max_steps tsk) <= task_deadline tsk.
+
+      (* First, we show that f is monotonically increasing. *)
+      Lemma bertogna_fp_comp_f_monotonic :
+        forall x1 x2, x1 <= x2 -> f x1 <= f x2.
+      Proof.
+        unfold valid_sporadic_taskset, is_valid_sporadic_task in *.
+        rename H_test_succeeds into SOME,
+               H_valid_task_parameters into VALID.
+        intros x1 x2 LEx; unfold f, per_task_rta.
+        apply fun_mon_iter_mon; [by ins | by ins; apply leq_addr |].
+        clear LEx x1 x2; intros x1 x2 LEx.
+        unfold div_floor, total_interference_bound_fp.
+        rewrite big_seq_cond [\sum_(i <- _ | let '(tsk_other, _) := i in
+                          higher_priority_task_in _ _ _ _ _)_]big_seq_cond.
+        rewrite leq_add2l leq_div2r //.
+        apply leq_sum; move => i /andP [IN _].
+        destruct i as [i R].
+        have GE_COST := fp_claimed_bounds_ge_cost ts_hp rt_bounds i R SOME IN.
+        have UNZIP := fp_claimed_bounds_unzip ts_hp rt_bounds SOME.
+        unfold interference_bound_generic; simpl.
+        assert (IN': i \in ts_hp).
+        {
+          by rewrite -UNZIP; apply/mapP; exists (i,R).
+        }
+        unfold interference_bound_generic; simpl.
+        rewrite leq_min; apply/andP; split.
+        {
+          apply leq_trans with (n := W task_cost task_period i R x1); first by apply geq_minl.
+          exploit (VALID i); [by rewrite mem_rcons in_cons IN' orbT | ins; des].
+          by apply W_monotonic; try (by done); apply GE_COST.
+        }
+        {
+          apply leq_trans with (n := x1 - task_cost tsk + 1); first by apply geq_minr.
+          by rewrite leq_add2r leq_sub2r //.
+        }
+      Qed.
+
+      (* If the iteration converged at an earlier step, it remains as a fixed point. *)
+      Lemma bertogna_fp_comp_f_converges_early :
+        (exists k, k <= max_steps tsk /\ f k = f k.+1) ->
+        f (max_steps tsk) = f (max_steps tsk).+1.
+      Proof.
+        by intros EX; des; apply iter_fix with (k := k).
+      Qed.
+
+      (* Else, we derive a contradiction. *)
+      Section DerivingContradiction.
+
+        (* Assume instead that the iteration continued to diverge. *)
+        Hypothesis H_keeps_diverging:
+          forall k,
+            k <= max_steps tsk -> f k != f k.+1.
+
+        (* By monotonicity, it follows that the value always increases. *)
+        Lemma bertogna_fp_comp_f_increases :
+          forall k,
+            k <= max_steps tsk ->
+            f k < f k.+1.
+        Proof.
+          intros k LT.
+          rewrite ltn_neqAle; apply/andP; split.
+            by apply H_keeps_diverging.
+            by apply bertogna_fp_comp_f_monotonic, leqnSn.
+        Qed.
+
+        (* In the end, the response-time bound must exceed the deadline. Contradiction! *)
+        Lemma bertogna_fp_comp_rt_grows_too_much :
+          forall k,
+            k <= max_steps tsk ->
+            f k > k + task_cost tsk - 1.
+        Proof.
+          have INC := bertogna_fp_comp_f_increases.
+          rename H_valid_task_parameters into TASK_PARAMS.
+          unfold valid_sporadic_taskset, is_valid_sporadic_task in *; des.
+          exploit (TASK_PARAMS tsk);
+            [by rewrite mem_rcons in_cons eq_refl orTb | intro PARAMS; des].
+          induction k.
+          {
+            intros _; rewrite add0n -addn1 subh1;
+              first by rewrite -addnBA // subnn addn0 /= leqnn.
+            by apply PARAMS.
+          }
+          {
+            intros LT.
+            specialize (IHk (ltnW LT)).
+            apply leq_ltn_trans with (n := f k);last by apply INC, ltnW.
+            rewrite -addn1 -addnA [1 + _]addnC addnA -addnBA // subnn addn0.
+            rewrite -(ltn_add2r 1) in IHk.
+            rewrite subh1 in IHk;
+              last by apply leq_trans with (n := task_cost tsk);
+                [by apply PARAMS | by apply leq_addl].
+            by rewrite -addnBA // subnn addn0 addn1 ltnS in IHk.
+          }
+        Qed.
+
+      End DerivingContradiction.
+      
+      (* Using the lemmas above, we prove the convergence of the iteration after max_steps. *)
+      Lemma per_task_rta_converges:
+        f (max_steps tsk) = f (max_steps tsk).+1.
+      Proof.
+        have TOOMUCH := bertogna_fp_comp_rt_grows_too_much.
+        have INC := bertogna_fp_comp_f_increases.
+        rename H_no_larger_than_deadline into LE,
+               H_valid_task_parameters into TASK_PARAMS.
+        unfold valid_sporadic_taskset, is_valid_sporadic_task in *; des.
+       
+        (* Either f converges by the deadline or not. *)
+        destruct ([exists k in 'I_(max_steps tsk).+1, f k == f k.+1]) eqn:EX.
+        {
+          move: EX => /exists_inP EX; destruct EX as [k _ ITERk].
+          apply bertogna_fp_comp_f_converges_early.
+          by exists k; split; [by rewrite -ltnS; apply ltn_ord | by apply/eqP].
+        }
+
+        (* If not, then we reach a contradiction *)
+        apply negbT in EX; rewrite negb_exists_in in EX.
+        move: EX => /forall_inP EX.
+        rewrite leqNgt in LE; move: LE => /negP LE.
+        exfalso; apply LE.
+
+        assert (DIFF: forall k : nat, k <= max_steps tsk -> f k != f k.+1).
+        {
+          intros k LEk; rewrite -ltnS in LEk.
+          by exploit (EX (Ordinal LEk)); [by done | intro DIFF; apply DIFF].
+        }          
+        exploit TOOMUCH; [by apply DIFF | by apply leq_addr |].
+        exploit (TASK_PARAMS tsk);
+          [by rewrite mem_rcons in_cons eq_refl orTb | intro PARAMS; des].
+        rewrite subh1; last by apply PARAMS2.
+        rewrite -addnBA // subnn addn0 subn1 prednK //.
+        intros LT; apply (leq_ltn_trans LT).
+        by rewrite /max_steps [_ - _ + 1]addn1; apply INC, leq_addr.
+      Qed.
+      
+    End Convergence.
+    
+    (* Now we prove the correctness of the response-time bounds. *)
+    Section MainProof.
+
+      (* Consider a task set ts. *)
+      Variable ts: taskset_of sporadic_task.
+      
+      (* Assume that all tasks have valid parameters ... *)
+      Hypothesis H_valid_task_parameters:
+        valid_sporadic_taskset task_cost task_period task_deadline ts.
+
+      (* ...and constrained deadlines. *)
+      Hypothesis H_constrained_deadlines:
+        forall tsk, tsk \in ts -> task_deadline tsk <= task_period tsk.
+
+      (* Assume that alpha' is a non-empty subaffinity of alpha. *)
+      Hypothesis H_non_empty_affinity:
+        forall tsk, tsk \in ts -> #|alpha' tsk| > 0.
+      Hypothesis H_subaffinity:
+        forall tsk, tsk \in ts -> is_subaffinity (alpha' tsk) (alpha tsk).
+      
+      (* Assume that the task set is totally ordered by unique decreasing
+         priorities and that the priority order is transitive. *)
+      Hypothesis H_task_set_is_sorted: sorted higher_priority ts.
+      Hypothesis H_task_set_has_unique_priorities:
+        FP_is_antisymmetric_over_task_set higher_priority ts.
+      Hypothesis H_priority_is_total:
+        FP_is_total_over_task_set higher_priority ts.
+      Hypothesis H_priority_transitive: FP_is_transitive higher_priority.
+
+      (* Next, consider any arrival sequence such that...*)
+      Context {arr_seq: arrival_sequence Job}.
+
+     (* ...all jobs come from task set ts, ...*)
+      Hypothesis H_all_jobs_from_taskset:
+        forall (j: JobIn arr_seq), job_task j \in ts.
+      
+      (* ...jobs have valid parameters,...*)
+      Hypothesis H_valid_job_parameters:
+        forall (j: JobIn arr_seq),
+          valid_sporadic_job task_cost task_deadline job_cost job_deadline job_task j.
+      
+      (* ... and satisfy the sporadic task model.*)
+      Hypothesis H_sporadic_tasks:
+        sporadic_task_model task_period arr_seq job_task.
+      
+      (* Then, consider any schedule such that...*)
+      Variable sched: schedule num_cpus arr_seq.
+
+      (* ...jobs only execute after they arrived and no longer
+         than their execution costs,... *)
+      Hypothesis H_jobs_must_arrive_to_execute:
+        jobs_must_arrive_to_execute sched.
+      Hypothesis H_completed_jobs_dont_execute:
+        completed_jobs_dont_execute job_cost sched.
+
+      (* ...and jobs are sequential. *)
+      Hypothesis H_sequential_jobs: sequential_jobs sched.
+
+      (* Assume a work-conserving APA scheduler that enforces the FP policy. *)
+      Hypothesis H_respects_affinity: respects_affinity job_task sched alpha.
+      Hypothesis H_work_conserving: apa_work_conserving job_cost job_task sched alpha.
+      Hypothesis H_enforces_FP_policy:
+        enforces_FP_policy_under_weak_APA job_cost job_task sched alpha higher_priority.
+
+      (* To avoid a long list of parameters, we provide some local definitions. *)
+      Let no_deadline_missed_by_task (tsk: sporadic_task) :=
+        task_misses_no_deadline job_cost job_deadline job_task sched tsk.
+      Let no_deadline_missed_by_job :=
+        job_misses_no_deadline job_cost job_deadline sched.
+      Let response_time_bounded_by (tsk: sporadic_task) :=
+        is_response_time_bound_of_task job_cost job_task tsk sched.
+          
+      (* In the following theorem, we prove that any response-time bound contained
+         in fp_claimed_bounds is safe. The proof follows by induction on the task set:
+
+           Induction hypothesis: assume all higher-priority tasks have safe response-time bounds.
+           Inductive step: we prove that the response-time bound of the current task is safe.
+
+         Note that the inductive step is a direct application of the main Theorem from
+         bertogna_fp_theory.v. *)
+      Theorem fp_analysis_yields_response_time_bounds :
+        forall tsk R,
+          (tsk, R) \In fp_claimed_bounds ts ->
+          response_time_bounded_by tsk R.
+      Proof.
+        rename H_valid_job_parameters into JOBPARAMS, H_valid_task_parameters into TASKPARAMS.
+        unfold valid_sporadic_taskset in *.
+        intros tsk R MATCH.
+        assert (SOME: exists hp_bounds, fp_claimed_bounds ts = Some hp_bounds /\
+                                        (tsk, R) \in hp_bounds).
+        {
+          destruct (fp_claimed_bounds ts); last by done.
+          by exists l; split.
+        } clear MATCH; des; rename SOME0 into IN.
+
+        have UNZIP := fp_claimed_bounds_unzip ts hp_bounds SOME.
+        
+        set elem := (tsk,R).
+        move: IN => /(nthP elem) [idx LTidx EQ].
+        set NTH := fun k => nth elem hp_bounds k.
+        set TASK := fun k => (NTH k).1.
+        set RESP := fun k => (NTH k).2.
+        cut (response_time_bounded_by (TASK idx) (RESP idx));
+          first by unfold TASK, RESP, NTH; rewrite EQ.
+        clear EQ.
+
+        assert (PAIR: forall idx, (TASK idx, RESP idx) = NTH idx).
+        {
+          by intros i; unfold TASK, RESP; destruct (NTH i).
+        }
+
+        assert (SUBST: forall i, i < size hp_bounds -> TASK i = nth tsk ts i).
+        {
+
+          by intros i LTi; rewrite /TASK /NTH -UNZIP (nth_map elem) //.
+        }
+
+        assert (SIZE: size hp_bounds = size ts).
+        {
+          by rewrite -UNZIP size_map.
+        }
+
+        induction idx as [idx IH'] using strong_ind.
+
+        assert (IH: forall tsk_hp R_hp, (tsk_hp, R_hp) \in take idx hp_bounds -> response_time_bounded_by tsk_hp R_hp).
+        {
+          intros tsk_hp R_hp INhp.
+          move: INhp => /(nthP elem) [k LTk EQ].
+          rewrite size_take LTidx in LTk.
+          rewrite nth_take in EQ; last by done.
+          cut (response_time_bounded_by (TASK k) (RESP k));
+            first by unfold TASK, RESP, NTH; rewrite EQ.
+          by apply IH'; try (by done); apply (ltn_trans LTk).
+        } clear IH'.
+
+        unfold response_time_bounded_by in *.
+
+        exploit (fp_claimed_bounds_rcons (take idx ts) (take idx hp_bounds) (TASK idx) (TASK idx) (RESP idx)).
+        {
+          by rewrite PAIR SUBST // -2?take_nth -?SIZE // (fp_claimed_bounds_take _ hp_bounds).
+        }
+        intros [_ [_ [REC DL]]].
+
+        apply bertogna_cirinei_response_time_bound_fp with
+              (alpha0 := alpha) (alpha'0 := alpha')
+              (task_cost0 := task_cost) (task_period0 := task_period)
+              (task_deadline0 := task_deadline) (job_deadline0 := job_deadline) (tsk0 := (TASK idx))
+              (job_task0 := job_task) (ts0 := ts) (hp_bounds0 := take idx hp_bounds)
+              (higher_eq_priority := higher_priority); try (by ins).
+        {
+          cut (NTH idx \in hp_bounds = true); [intros IN | by apply mem_nth].
+          by rewrite set_mem -UNZIP; apply/mapP; exists (TASK idx, RESP idx); rewrite PAIR.
+        }
+        {
+          intros hp_tsk IN INTERF.
+          exists (RESP (index hp_tsk ts)).
+          move: (IN) => INDEX; apply nth_index with (x0 := tsk) in INDEX.
+          rewrite -{1}[hp_tsk]INDEX -SUBST; last by rewrite SIZE index_mem.
+          assert (UNIQ: uniq hp_bounds).
+          {
+            apply map_uniq with (f := fst); unfold unzip1 in *; rewrite UNZIP.
+            by destruct ts.
+          }
+          rewrite -filter_idx_lt_take //.
+          {
+            rewrite PAIR mem_filter; apply/andP; split;
+              last by apply mem_nth; rewrite SIZE index_mem.
+            {
+              rewrite /NTH index_uniq; [| by rewrite SIZE index_mem | by done ].
+              {
+                move: INTERF => /andP [/andP [HP NEQ] _].
+                apply fp_claimed_bounds_hp_tasks_have_smaller_index with
+                  (ts := ts) (elem := tsk) (hp_bounds := hp_bounds);
+                  try (by done);
+                  [by rewrite index_mem | by rewrite -SIZE | | by rewrite INDEX -SUBST].
+                apply/eqP; intro BUG; subst idx.
+                rewrite SUBST -{1}INDEX in NEQ;
+                  first by rewrite eq_refl in NEQ.
+                by rewrite SIZE index_mem INDEX.
+              }
+            }
+          }
+        }
+        {
+          intros hp_tsk R_hp IN; apply mem_take in IN.
+          by apply fp_claimed_bounds_ge_cost with (ts' := ts) (rt_bounds := hp_bounds).
+        }
+        {
+          intros hp_tsk R_hp IN; apply mem_take in IN.
+          by apply fp_claimed_bounds_le_deadline with (ts' := ts) (rt_bounds := hp_bounds).
+        }
+        {
+          rewrite REC.
+          rewrite per_task_rta_fold.
+          apply per_task_rta_converges with (ts_hp := take idx ts);
+            [by apply fp_claimed_bounds_take; try (by apply ltnW) | | by rewrite -REC ].
+          rewrite SUBST // -take_nth -?SIZE //.
+          by intros i IN; eapply TASKPARAMS, mem_take, IN.
+        }
+      Qed.
+      
+      (* Therefore, if the schedulability test suceeds, ...*)
+      Hypothesis H_test_succeeds: fp_schedulable ts.
+      
+      (*..., no task misses its deadline. *)
+      Theorem taskset_schedulable_by_fp_rta :
+        forall tsk, tsk \in ts -> no_deadline_missed_by_task tsk.
+      Proof.
+        have RLIST := (fp_analysis_yields_response_time_bounds).
+        have UNZIP := (fp_claimed_bounds_unzip ts).
+        have DL := (fp_claimed_bounds_le_deadline ts).
+        unfold no_deadline_missed_by_task, task_misses_no_deadline,
+               job_misses_no_deadline, completed,
+               fp_schedulable, valid_sporadic_job in *.
+        rename H_valid_job_parameters into JOBPARAMS.
+        move => tsk INtsk j JOBtsk.
+
+        destruct (fp_claimed_bounds ts) as [rt_bounds |]; last by ins.
+        feed (UNZIP rt_bounds); first by done.
+        assert (EX: exists R, (tsk, R) \in rt_bounds).
+        {
+          rewrite set_mem -UNZIP in INtsk; move: INtsk => /mapP EX.
+          by destruct EX as [p]; destruct p as [tsk' R]; simpl in *; subst tsk'; exists R.
+        } des.
+        exploit (RLIST tsk R); [by ins | by apply JOBtsk | intro COMPLETED].
+        exploit (DL rt_bounds tsk R); [by ins | by ins | clear DL; intro DL].
+        rewrite eqn_leq; apply/andP; split; first by apply cumulative_service_le_job_cost.
+        apply leq_trans with (n := service sched j (job_arrival j + R)); last first.
+        {
+          unfold valid_sporadic_taskset, is_valid_sporadic_task in *.
+          apply extend_sum; rewrite // leq_add2l.
+          specialize (JOBPARAMS j); des; rewrite JOBPARAMS1.
+          by rewrite JOBtsk.
+        }
+        rewrite leq_eqVlt; apply/orP; left; rewrite eq_sym.
+        by apply COMPLETED.
+      Qed.
+
+      (* For completeness, since all jobs of the arrival sequence
+         are spawned by the task set, we also conclude that no job in
+         the schedule misses its deadline. *)
+      Theorem jobs_schedulable_by_fp_rta :
+        forall (j: JobIn arr_seq), no_deadline_missed_by_job j.
+      Proof.
+        intros j.
+        have SCHED := taskset_schedulable_by_fp_rta.
+        unfold no_deadline_missed_by_task, task_misses_no_deadline in *.
+        apply SCHED with (tsk := job_task j); last by done.
+        by apply H_all_jobs_from_taskset.
+      Qed.
+      
+    End MainProof.
+
+  End Analysis.
+
+End ResponseTimeIterationFP.
\ No newline at end of file
diff --git a/analysis/apa/bertogna_fp_theory.v b/analysis/apa/bertogna_fp_theory.v
new file mode 100644
index 0000000000000000000000000000000000000000..0dab3f0ffe88f737c08404369e8a1d6a0ddb51b5
--- /dev/null
+++ b/analysis/apa/bertogna_fp_theory.v
@@ -0,0 +1,1029 @@
+Require Import rt.util.all rt.util.divround.
+Require Import rt.model.apa.task rt.model.apa.job rt.model.apa.task_arrival
+               rt.model.apa.schedule rt.model.apa.platform rt.model.apa.platform_fp
+               rt.model.apa.workload rt.model.apa.schedulability rt.model.apa.priority
+               rt.model.apa.response_time rt.model.apa.interference
+               rt.model.apa.affinity rt.model.apa.constrained_deadlines.
+Require Import rt.analysis.apa.workload_bound rt.analysis.apa.interference_bound_fp.
+From mathcomp Require Import ssreflect ssrbool eqtype ssrnat seq fintype bigop div path.
+
+Module ResponseTimeAnalysisFP.
+
+  Export Job SporadicTaskset ScheduleOfSporadicTask Workload Interference InterferenceBoundFP
+         Platform PlatformFP Schedulability ResponseTime Priority
+         SporadicTaskArrival WorkloadBound Affinity ConstrainedDeadlines.
+    
+
+  (* In this section, we prove that any fixed point in the APA-reduction of Bertogna
+     and Cirinei's RTA for FP scheduling with slack updates is a safe response-time
+     bound. This result corresponds to Lemma 9 in the revised version of the APA paper:
+     http://www.mpi-sws.org/~bbb/papers/pdf/ecrts13a-rev1.pdf *)
+  Section ResponseTimeBound.
+
+    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_cost: Job -> time.
+    Variable job_deadline: Job -> time.
+    Variable job_task: Job -> sporadic_task.
+    
+    (* Assume any job arrival sequence... *)
+    Context {arr_seq: arrival_sequence Job}.
+
+    (* ... in which jobs arrive sporadically and have valid parameters. *)
+    Hypothesis H_sporadic_tasks:
+      sporadic_task_model task_period arr_seq job_task.
+    Hypothesis H_valid_job_parameters:
+      forall (j: JobIn arr_seq),
+        valid_sporadic_job task_cost task_deadline job_cost job_deadline job_task j.
+
+    (* Assume that we have a task set where all tasks have valid
+       parameters and constrained deadlines, ... *)
+    Variable ts: taskset_of sporadic_task.
+    Hypothesis H_valid_task_parameters:
+      valid_sporadic_taskset task_cost task_period task_deadline ts.
+    Hypothesis H_constrained_deadlines:
+      forall tsk, tsk \in ts -> task_deadline tsk <= task_period tsk.
+
+    (* ... and that all jobs in the arrival sequence come from the task set. *)
+    Hypothesis H_all_jobs_from_taskset:
+      forall (j: JobIn arr_seq), job_task j \in ts.
+
+    (* Also assume that every task has a non-empty processor affinity alpha. *)
+    Context {num_cpus: nat}.
+    Variable alpha: task_affinity sporadic_task num_cpus.
+
+    (* Next, consider any schedule such that...*)
+    Variable sched: schedule num_cpus arr_seq.
+
+    (* ...jobs are sequential and do not execute before their
+       arrival times nor longer than their execution costs. *)
+    Hypothesis H_sequential_jobs: sequential_jobs sched.
+    Hypothesis H_jobs_must_arrive_to_execute:
+      jobs_must_arrive_to_execute sched.
+    Hypothesis H_completed_jobs_dont_execute:
+      completed_jobs_dont_execute job_cost sched.
+
+    (* Consider a given FP policy, ... *)
+    Variable higher_eq_priority: FP_policy sporadic_task.
+
+    (* ... and assume that the schedule is an APA work-conserving
+       schedule that enforces this policy. *)
+    Hypothesis H_respects_affinity: respects_affinity job_task sched alpha.
+    Hypothesis H_work_conserving: apa_work_conserving job_cost job_task sched alpha.
+    Hypothesis H_enforces_FP_policy:
+      enforces_FP_policy_under_weak_APA job_cost job_task sched alpha higher_eq_priority.
+
+    (* Let's define some local names to avoid passing many parameters. *)
+    Let no_deadline_is_missed_by_tsk (tsk: sporadic_task) :=
+      task_misses_no_deadline job_cost job_deadline job_task sched tsk.
+    Let response_time_bounded_by (tsk: sporadic_task) :=
+      is_response_time_bound_of_task job_cost job_task tsk sched.
+
+    (* Next, we consider the response-time recurrence.
+       Let tsk be a task in ts that is to be analyzed. *)
+    Variable tsk: sporadic_task.
+    Hypothesis task_in_ts: tsk \in ts.
+
+    (* When computing the response-time bounds, we assume that each task under
+       analysis has a non-empty subaffinity alpha'. *)
+    Variable alpha': task_affinity sporadic_task num_cpus.
+    Hypothesis H_affinity_subset: forall tsk, tsk \in ts -> is_subaffinity (alpha' tsk) (alpha tsk).
+    Hypothesis H_at_least_one_cpu : forall tsk, tsk \in ts -> #|alpha' tsk| > 0.
+    
+    (* Let (hp_task_in alpha') denote whether a task is a higher-priority task
+       (with respect to tsk) that can execute on processors in (alpha' tsk). *)
+    Let hp_task_in alpha' := higher_priority_task_in alpha higher_eq_priority tsk alpha'.
+
+    (* Assume a response-time bound is known... *)
+    Let task_with_response_time := (sporadic_task * time)%type.
+    Variable hp_bounds: seq task_with_response_time.
+    Hypothesis H_response_time_of_interfering_tasks_is_known:
+      forall hp_tsk R,
+        (hp_tsk, R) \in hp_bounds ->
+        is_response_time_bound_of_task job_cost job_task hp_tsk sched R.
+    
+    (* ... for every higher-priority task in (alpha tsk). *)
+    Hypothesis H_hp_bounds_has_interfering_tasks:
+      forall hp_tsk,
+        hp_tsk \in ts ->
+        hp_task_in (alpha tsk) hp_tsk ->
+        exists R,
+          (hp_tsk, R) \in hp_bounds.
+
+    (* Assume that the response-time bounds are larger than task costs. *)
+    Hypothesis H_response_time_bounds_ge_cost:
+      forall hp_tsk R,
+        (hp_tsk, R) \in hp_bounds -> R >= task_cost hp_tsk.
+    
+    (* Assume that no deadline is missed by any higher-priority task. *)
+    Hypothesis H_interfering_tasks_miss_no_deadlines:
+      forall hp_tsk R,
+        (hp_tsk, R) \in hp_bounds -> R <= task_deadline hp_tsk.
+    
+    (* Let R be the fixed point of the APA-reduction of Bertogna and
+       Cirinei's response-time recurrence (using subaffinity alpha'), ...*)
+    Variable R: time.
+    Hypothesis H_response_time_recurrence_holds :
+      R = task_cost tsk +
+          div_floor
+            (total_interference_bound_fp task_cost task_period alpha tsk
+                            (alpha' tsk) hp_bounds R higher_eq_priority)
+            #|alpha' tsk|.
+
+    (* ... and assume that R is no larger than the deadline of tsk.*)
+    Hypothesis H_response_time_no_larger_than_deadline:
+      R <= task_deadline tsk.
+
+    (* In order to prove that R is a response-time bound, we first provide some lemmas. *)
+    Section Lemmas.
+
+      (* Consider any job j of tsk. *)
+      Variable j: JobIn arr_seq.
+      Hypothesis H_job_of_tsk: job_task j = tsk.
+
+      (* Assume that job j is the first job of tsk not to complete by the response time bound. *)
+      Hypothesis H_j_not_completed: ~~ completed job_cost sched j (job_arrival j + R).
+      Hypothesis H_previous_jobs_of_tsk_completed :
+        forall (j0: JobIn arr_seq),
+          job_task j0 = tsk ->
+          job_arrival j0 < job_arrival j ->
+          completed job_cost sched j0 (job_arrival j0 + R).
+
+      (* Let's call x the interference incurred by job j due to tsk_other, ...*)
+      Let x (tsk_other: sporadic_task) :=
+        task_interference job_cost job_task sched alpha j tsk_other
+                          (job_arrival j) (job_arrival j + R).
+
+      (* and X the total interference incurred by job j due to any task. *)
+      Let X := total_interference job_cost sched j (job_arrival j) (job_arrival j + R).
+
+      (* Recall Bertogna and Cirinei's workload bound. *)
+      Let workload_bound (tsk_other: sporadic_task) (R_other: time) :=
+        W task_cost task_period tsk_other R_other R.
+
+      (* Let (hp_tasks_in alpha') denote the set of higher-priority tasks
+         that can be scheduled on subaffinity alpha'. *)
+      Let hp_tasks_in alpha' :=
+        [seq tsk_other <- ts | hp_task_in (alpha' tsk) tsk_other].
+
+      (* Now we establish results the higher-priority tasks. *)
+      Section LemmasAboutHPTasks.
+        
+        (* Let (tsk_other, R_other) be any pair of higher-priority task
+           and response-time bound computed in previous iterations. *)
+        Variable tsk_other: sporadic_task.
+        Variable R_other: time.
+        Hypothesis H_tsk_other_already_processed: (tsk_other, R_other) \in hp_bounds.
+        Hypothesis H_tsk_other_has_higher_priority: hp_task_in (alpha tsk) tsk_other.
+
+        (* Since tsk_other cannot interfere more than it executes, we show that
+           the interference caused by tsk_other is no larger than workload bound W. *)
+        Lemma bertogna_fp_workload_bounds_interference :
+          x tsk_other <= workload_bound tsk_other R_other.
+        Proof.
+          unfold completed, completed_jobs_dont_execute, valid_sporadic_job in *.
+          rename H_valid_job_parameters into PARAMS,
+                 H_valid_task_parameters into TASK_PARAMS,
+                 H_constrained_deadlines into RESTR,
+                 H_all_jobs_from_taskset into FROMTS,
+                 H_response_time_of_interfering_tasks_is_known into RESP,
+                 H_interfering_tasks_miss_no_deadlines into NOMISS,
+                 H_response_time_bounds_ge_cost into GE_COST.
+          unfold x, workload_bound.
+          destruct ([exists t: 'I_(job_arrival j + R),
+                       task_is_scheduled job_task sched tsk_other t]) eqn: SCHED;
+            last first.
+          {
+            apply negbT in SCHED; rewrite negb_exists in SCHED.
+            move: SCHED => /forallP SCHED.
+            apply leq_trans with (n := 0); last by done.
+            apply leq_trans with (n := \sum_(job_arrival j <= t < job_arrival j + R) 0);
+              last by rewrite big1.
+            apply leq_sum_nat; move => i /andP [_ LTi] _.
+            specialize (SCHED (Ordinal LTi)).
+            rewrite negb_exists in SCHED; move: SCHED => /forallP SCHED.
+            rewrite big1 //; intros cpu _.
+            specialize (SCHED cpu); apply negbTE in SCHED.
+            by rewrite SCHED andbF.
+          }
+          move: SCHED => /existsP [t /existsP [cpu SCHED]].
+          unfold task_scheduled_on in SCHED.
+          destruct (sched cpu t) as [j0 |]; last by done.
+          assert (INts: tsk_other \in ts).
+            by move: SCHED => /eqP <-; rewrite FROMTS.
+          apply leq_trans with (n := workload job_task sched tsk_other
+                                              (job_arrival j) (job_arrival j + R));
+            first by apply task_interference_le_workload.
+          by apply workload_bounded_by_W with (task_deadline0 := task_deadline)
+                    (job_cost0 := job_cost) (job_deadline0 := job_deadline);
+            try (by ins); last 2 first;
+              [ by ins; apply GE_COST 
+              | by ins; apply NOMISS
+              | by ins; apply TASK_PARAMS
+              | by ins; apply RESTR
+              | by ins; apply RESP with (hp_tsk := tsk_other)].
+        Qed.
+
+      End LemmasAboutHPTasks.
+
+      (* Next we prove some lemmas that help to derive a contradiction.*)
+      Section DerivingContradiction.
+
+        (* 0) Since job j did not complete by its response time bound, it follows that
+              the total interference X >= R - e_k + 1. *)
+        Lemma bertogna_fp_too_much_interference : X >= R - task_cost tsk + 1.
+        Proof.
+          rename H_completed_jobs_dont_execute into COMP,
+                 H_valid_job_parameters into PARAMS,
+                 H_response_time_recurrence_holds into REC,
+                 H_job_of_tsk into JOBtsk, H_j_not_completed into NOTCOMP.
+          unfold completed, valid_sporadic_job in *.
+          unfold X, total_interference; rewrite addn1.
+          rewrite -(ltn_add2r (task_cost tsk)).
+          rewrite subh1; last by rewrite [R]REC // leq_addr.
+          rewrite -addnBA // subnn addn0.
+          move: (NOTCOMP) => /negP NOTCOMP'.
+          rewrite neq_ltn in NOTCOMP.
+          move: NOTCOMP => /orP [LT | BUG]; last first.
+          {
+            exfalso; rewrite ltnNge in BUG; move: BUG => /negP BUG; apply BUG.
+            by apply cumulative_service_le_job_cost.
+          }
+          apply leq_ltn_trans with (n := (\sum_(job_arrival j <= t < job_arrival j + R)
+                                       backlogged job_cost sched j t) +
+                                     service sched j (job_arrival j + R)); last first.
+          {
+            rewrite -addn1 -addnA leq_add2l addn1.
+            apply leq_trans with (n := job_cost j); first by done.
+            by specialize (PARAMS j); des; rewrite -JOBtsk.
+          }
+          unfold service; rewrite service_before_arrival_eq_service_during //.
+          rewrite -big_split /=.
+          apply leq_trans with (n := \sum_(job_arrival j <= i < job_arrival j + R) 1);
+            first by rewrite big_const_nat iter_addn mul1n addn0 addKn.
+          rewrite big_nat_cond [\sum_(_ <= _ < _ | true) _]big_nat_cond.
+          apply leq_sum; move => i /andP [/andP [GEi LTi] _].
+          destruct (backlogged job_cost sched j i) eqn:BACK;
+            first by rewrite -addn1 addnC; apply leq_add.
+          apply negbT in BACK.
+          rewrite add0n lt0n -not_scheduled_no_service negbK.
+          rewrite /backlogged negb_and negbK in BACK.
+          move: BACK => /orP [/negP NOTPENDING | SCHED]; last by done.
+          exfalso; apply NOTPENDING; unfold pending; apply/andP; split; first by done.
+          apply/negP; red; intro BUG; apply NOTCOMP'.
+          by apply completion_monotonic with (t := i); try (by done); apply ltnW.
+        Qed.
+
+        (* 1) Next, we prove that during the scheduling window of j, any job that is
+              scheduled while j is backlogged comes from a different task.
+              This follows from the fact that j is the first job not to complete
+              by its response-time bound, so previous jobs of j's task must have
+              completed by their periods and cannot be pending. *)
+        Lemma bertogna_fp_interference_by_different_tasks :
+          forall t j_other,
+            job_arrival j <= t < job_arrival j + R ->
+            backlogged job_cost sched j t ->
+            scheduled sched j_other t ->
+            job_task j_other != tsk.
+        Proof.
+          rename H_all_jobs_from_taskset into FROMTS,
+                 H_valid_task_parameters into PARAMS,
+                 H_job_of_tsk into JOBtsk, H_sporadic_tasks into SPO,
+                 H_work_conserving into WORK,
+                 H_previous_jobs_of_tsk_completed into BEFOREok,
+                 H_response_time_no_larger_than_deadline into NOMISS,
+                 H_constrained_deadlines into RESTR.
+          move => t j_other /andP [LEt GEt] BACK SCHED.
+          apply/eqP; red; intro SAMEtsk.
+          move: SCHED => /existsP [cpu SCHED].
+          assert (SCHED': scheduled sched j_other t).
+            by apply/existsP; exists cpu.
+          clear SCHED; rename SCHED' into SCHED.
+          move: (SCHED) => PENDING.
+          apply scheduled_implies_pending with (job_cost0 := job_cost) in PENDING; try (by done).
+          destruct (ltnP (job_arrival j_other) (job_arrival j)) as [BEFOREother | BEFOREj].
+           {
+            move: (BEFOREother) => LT; rewrite -(ltn_add2r R) in LT.
+            specialize (BEFOREok j_other SAMEtsk BEFOREother).
+            move: PENDING => /andP [_ /negP NOTCOMP]; apply NOTCOMP.
+            apply completion_monotonic with (t0 := job_arrival j_other + R); try (by done).
+            apply leq_trans with (n := job_arrival j); last by done.
+            apply leq_trans with (n := job_arrival j_other + task_deadline tsk);
+              first by rewrite leq_add2l; apply NOMISS.
+            apply leq_trans with (n := job_arrival j_other + task_period tsk);
+              first by rewrite leq_add2l; apply RESTR; rewrite -JOBtsk FROMTS.
+            rewrite -SAMEtsk; apply SPO; [ | by rewrite JOBtsk | by apply ltnW].
+            by red; intro EQ; subst j_other; rewrite ltnn in BEFOREother.
+          }
+          {
+            move: PENDING => /andP [ARRIVED _].
+            exploit (SPO j j_other); [ | by rewrite SAMEtsk | by done | ]; last first.
+            {
+              apply/negP; rewrite -ltnNge.
+              apply leq_ltn_trans with (n := t); first by done.
+              apply leq_trans with (n := job_arrival j + R); first by done.
+              by rewrite leq_add2l; apply leq_trans with (n := task_deadline tsk);
+                [by apply NOMISS | by rewrite JOBtsk RESTR // -JOBtsk FROMTS].
+            }
+            by red; intros EQtsk; subst j_other; rewrite /backlogged SCHED andbF in BACK.
+          }
+        Qed.
+
+        (* 2) In order to use the lemmas in constrained_deadlines.v, we show that
+              all jobs of higher-priority tasks released before the end of the
+              interval complete by their periods. This follows trivially from
+              the fact that all these tasks are known to be schedulable. 
+              With this lemma, we can conclude that during job j's scheduling
+              window there cannot be multiple pending jobs of higher-priority tasks.*)
+        Lemma bertogna_fp_previous_interfering_jobs_complete_by_their_period:
+          forall  (j0: JobIn arr_seq),
+            hp_task_in (alpha tsk) (job_task j0) ->
+            completed job_cost sched j0
+               (job_arrival j0 + task_period (job_task j0)).
+        Proof.
+          rename H_hp_bounds_has_interfering_tasks into HAS,
+                 H_constrained_deadlines into CONSTR,
+                 H_response_time_no_larger_than_deadline into NOMISS,
+                 H_all_jobs_from_taskset into FROMTS,
+                 H_interfering_tasks_miss_no_deadlines into NOMISS',
+                 H_previous_jobs_of_tsk_completed into BEFOREok,
+                 H_response_time_of_interfering_tasks_is_known into RESP.
+          intros j0 INTERF.
+          exploit (HAS (job_task j0));
+            [by rewrite FROMTS | by done | move => [R0 INbounds]].
+          apply completion_monotonic with (t := job_arrival j0 + R0); first by done.
+          {
+            rewrite leq_add2l; apply leq_trans with (n := task_deadline (job_task j0));
+              [by apply NOMISS' | by apply CONSTR; rewrite FROMTS].
+          }
+          {
+            by apply (RESP (job_task j0)).
+          }
+        Qed.
+
+        (* 3) Next, we prove that the sum of the interference of each task is equal to the
+              total interference multiplied by the number of processors in tsk's *actual*
+              affinity. This holds because interference only occurs when all processors in
+              the affinity are busy.
+              With this lemma we can relate per-task interference with the total interference
+              incurred by j (backlogged time). *)
+        Lemma bertogna_fp_all_cpus_in_affinity_busy :
+          \sum_(tsk_k <- hp_tasks_in alpha) x tsk_k = X * #|alpha tsk|.
+        Proof.
+          have DIFFTASK := bertogna_fp_interference_by_different_tasks.
+          rename H_all_jobs_from_taskset into FROMTS,
+                 H_valid_task_parameters into PARAMS,
+                 H_job_of_tsk into JOBtsk, H_sporadic_tasks into SPO,
+                 H_work_conserving into WORK,
+                 H_previous_jobs_of_tsk_completed into BEFOREok,
+                 H_response_time_no_larger_than_deadline into NOMISS,
+                 H_constrained_deadlines into RESTR,
+                 H_respects_affinity into APA, H_enforces_FP_policy into FP.
+          unfold sporadic_task_model in *.
+          unfold x, X, total_interference, task_interference.
+          rewrite -big_mkcond -exchange_big big_distrl /= mul1n.
+          rewrite [\sum_(_ <= _ < _ | backlogged _ _ _ _) _]big_mkcond.
+          apply eq_big_nat; move => t /andP [GEt LTt].
+          destruct (backlogged job_cost sched j t) eqn:BACK; last first.
+          {
+            rewrite (eq_bigr (fun i => 0));
+              first by rewrite big_const_seq iter_addn mul0n addn0.
+            by intros i _; rewrite (eq_bigr (fun i => 0));
+              first by rewrite big_const_seq iter_addn mul0n addn0.
+          }
+          rewrite big_mkcond /=.
+          rewrite exchange_big /=.
+          apply eq_trans with (y := \sum_(cpu < num_cpus | cpu \in alpha tsk) 1);
+            last by rewrite sum1_card.
+          rewrite [\sum_(_ < _ | _) 1]big_mkcond /=.
+          apply eq_bigr; intros cpu _.
+          unfold can_execute_on in *.
+          destruct (cpu \in alpha (job_task j)) eqn:ALPHA; rewrite -?JOBtsk ALPHA;
+            last by rewrite big_filter (eq_bigr (fun x => 0));
+              [by simpl_sum_const | by ins].
+          move: (WORK j t BACK cpu ALPHA) => [j_other /eqP SCHED]; unfold scheduled_on in *.
+          rewrite (bigD1_seq (job_task j_other)) /=; last by rewrite filter_uniq; destruct ts.
+          {
+            rewrite (eq_bigr (fun i => 0));
+              last by intros i DIFF; rewrite /task_scheduled_on SCHED;apply/eqP;rewrite eqb0 eq_sym.
+            rewrite big_const_seq iter_addn mul0n 2!addn0; apply/eqP; rewrite eqb1.
+            by unfold task_scheduled_on; rewrite SCHED.
+          }
+          rewrite mem_filter; apply/andP; split; last by apply FROMTS.
+          unfold higher_priority_task_in, affinity_intersects.
+          apply/andP; split; last first.
+          {
+            apply/existsP; exists cpu; rewrite -JOBtsk ALPHA andTb.
+            by apply APA with (t := t); apply/eqP.
+          }
+          apply/andP; split; last first.
+          {
+            apply DIFFTASK with (t := t); [by auto | by done |].
+            by apply/existsP; exists cpu; apply/eqP.
+          }
+          by rewrite -JOBtsk; apply FP with (cpu := cpu) (t := t); try (by apply/eqP).
+        Qed.
+
+        (* 4) Recall that the reduction-based analysis considers only the interfering tasks
+              within subaffinity (alpha' tsk), as opposed to (alpha tsk). Therefore, we must
+              reason about the task interference wihin (alpha' tsk).
+              We begin by showing that the cumulative interference within (alpha' tsk) is at
+              least the total interference multiplied by the number of processors in (alpha' tsk). *)
+        Lemma bertogna_fp_all_cpus_in_subaffinity_busy :
+          \sum_(tsk_k <- hp_tasks_in alpha') x tsk_k >= X * #|alpha' tsk|.
+        Proof.
+          have DIFFTASK := bertogna_fp_interference_by_different_tasks.
+          rename H_all_jobs_from_taskset into FROMTS,
+                 H_valid_task_parameters into PARAMS,
+                 H_job_of_tsk into JOBtsk, H_sporadic_tasks into SPO,
+                 H_work_conserving into WORK,
+                 H_response_time_no_larger_than_deadline into NOMISS,
+                 H_constrained_deadlines into RESTR,
+                 H_enforces_FP_policy into FP,
+                 H_respects_affinity into APA, H_affinity_subset into SUB.
+          unfold sporadic_task_model in *.
+          unfold x, X, total_interference, task_interference.
+          rewrite -big_mkcond -exchange_big big_distrl /= mul1n.
+          rewrite [\sum_(_ <= _ < _ | backlogged _ _ _ _) _]big_mkcond /=.
+          apply leq_sum_nat; move => t /andP [GEt LTt] _.
+          destruct (backlogged job_cost sched j t) eqn:BACK; last first.
+          {
+            rewrite (eq_bigr (fun i => 0));
+              first by rewrite big_const_seq iter_addn mul0n addn0.
+            by intros i _; rewrite (eq_bigr (fun i => 0));
+              first by rewrite big_const_seq iter_addn mul0n addn0.
+          }
+          rewrite big_mkcond /=.
+          rewrite exchange_big /=.
+          apply leq_trans with (n := \sum_(cpu < num_cpus | cpu \in alpha' tsk) 1);
+            first by rewrite sum1_card.
+          rewrite [\sum_(_ < _ | _) 1]big_mkcond /=.
+          apply leq_sum; intros cpu _.
+          unfold can_execute_on in *.
+          destruct (cpu \in alpha' (job_task j)) eqn:ALPHA'; rewrite -?JOBtsk ALPHA';
+            last by done.
+          move: (SUB (job_task j) (FROMTS j) cpu ALPHA') => SUBj.
+          move: (WORK j t BACK cpu SUBj) => [j_other /eqP SCHED]; unfold scheduled_on in *.
+          rewrite (bigD1_seq (job_task j_other)) /=; last by apply filter_uniq; destruct ts.
+          {
+            by rewrite {1}/task_scheduled_on SUBj SCHED eq_refl andTb.
+          }
+          {
+            rewrite mem_filter; apply/andP; split; last by apply FROMTS.
+            apply/andP; split; last first.
+            {
+              apply/existsP; exists cpu; apply/andP; split; first by rewrite -JOBtsk.
+              by apply APA with (t := t); apply/eqP.
+            }
+            apply/andP; split; last first.
+            {
+              apply DIFFTASK with (t := t); [by auto | by done |].
+              by apply/existsP; exists cpu; apply/eqP.
+            }
+            by rewrite -JOBtsk; apply FP with (cpu := cpu) (t := t); try (by apply/eqP).
+          }
+        Qed.
+
+        (* Let's define a predicate for whether a task is scheduled on (alpha tsk). *)
+        Let scheduled_on_alpha_tsk := fun t tsk_k =>
+          task_scheduled_on_affinity job_task sched (alpha tsk) tsk_k t.
+
+
+        (* 5) Now we prove that, at all times that j is backlogged, the number
+           of tasks whose affinity intersects (alpha' tsk) that are in fact
+           scheduled on (alpha' tsk) is at least the size of (alpha' tsk).
+           This is required to prove lemma (6). *)
+        Lemma bertogna_fp_alpha'_is_full:
+          forall t,
+            job_arrival j <= t < job_arrival j + R ->
+            backlogged job_cost sched j t ->
+            count (scheduled_on_alpha_tsk t) (hp_tasks_in alpha') >= #|alpha' tsk|.
+        Proof.
+          have DIFFTASK := bertogna_fp_interference_by_different_tasks.
+          have COMP := bertogna_fp_previous_interfering_jobs_complete_by_their_period.
+          rename H_work_conserving into WORK, H_respects_affinity into APA,
+                 H_affinity_subset into SUB, H_job_of_tsk into JOBtsk,
+                 H_all_jobs_from_taskset into FROMTS,
+                 H_valid_task_parameters into PARAMS,
+                 H_previous_jobs_of_tsk_completed into BEFOREok,
+                 H_sequential_jobs into SEQ, H_constrained_deadlines into CONSTR,
+                 H_enforces_FP_policy into FP.
+          move => t /andP [GEt LTt] BACK. 
+          move: WORK  => WORK.
+          specialize (WORK j t BACK).
+          rewrite -size_filter.
+          apply leq_trans with (n := size (alpha' tsk));
+            first by apply card_size.
+          apply leq_trans with (n := size (pmap (fun cpu => sched cpu t) (enum (alpha' tsk)))).
+          {
+            rewrite size_pmap -size_filter.
+            apply uniq_leq_size; first by destruct (alpha' tsk).
+            intros cpu IN.
+            rewrite mem_filter; apply/andP; split; last by rewrite mem_enum.
+            feed (WORK cpu); first by apply SUB; rewrite JOBtsk.
+            by move: WORK => [j_other /eqP SCHED]; rewrite SCHED.
+          }
+          {
+            apply leq_trans with (n := size (map (fun j: JobIn arr_seq => job_task j) (pmap (fun cpu => sched cpu t) (enum (alpha' tsk)))));
+              first by rewrite size_map.
+            apply uniq_leq_size.
+            {
+              rewrite map_inj_in_uniq.
+              {
+                apply pmap_inj_in_uniq; last by apply enum_uniq.
+                intros cpu1 cpu2 IN1 IN2 SCHED2.
+                destruct (sched cpu1 t) eqn:SCHED1; symmetry in SCHED2;
+                  first by apply SEQ with (j := j0) (t := t).
+                rewrite 2!mem_enum in IN1 IN2.
+                exploit (WORK cpu1); first by apply SUB; rewrite JOBtsk.
+                by move => [j_other SCHED]; rewrite /scheduled_on SCHED1 in SCHED.  
+              }
+              {
+                intros j1 j2 SCHED1 SCHED2 SAMEtsk.
+                rewrite 2!mem_pmap in SCHED1 SCHED2.
+                move: SCHED1 SCHED2 => /mapP [cpu1 IN1 SCHED1] /mapP [cpu2 IN2 SCHED2].
+                assert (PENDING1: pending job_cost sched j1 t).
+                {
+                  apply scheduled_implies_pending; try by done.
+                  by apply/existsP; exists cpu1; rewrite /scheduled_on -SCHED1.
+                }
+                assert (SCHED2': pending job_cost sched j2 t).
+                {
+                  apply scheduled_implies_pending; try by done.
+                  by apply/existsP; exists cpu2; rewrite /scheduled_on -SCHED2. 
+                }
+
+                apply platform_fp_no_multiple_jobs_of_interfering_tasks with
+                  (task_period0 := task_period) (tsk0 := tsk) (alpha0 := alpha)
+                  (higher_eq_priority0 := higher_eq_priority) (t0 := t)
+                (job_cost0 := job_cost) (job_task0 := job_task) (sched0 := sched);
+                  rewrite ?JOBtsk ?SAMEtsk //.
+                {
+                  by intros j0 tsk0 TSK0 LE; subst tsk0; apply COMP.
+                }
+                {
+                  apply/andP; split; last first.
+                  {
+                    apply/existsP; exists cpu2; apply/andP; split.
+                    by apply SUB; [rewrite -JOBtsk FROMTS | rewrite -mem_enum].
+                    by apply APA with (t := t); apply/eqP; rewrite SCHED2.
+                  }
+                  apply/andP; split.
+                  {
+                    rewrite -JOBtsk; apply FP with (cpu := cpu2) (t := t);
+                      [by done | by apply/eqP; rewrite SCHED2 |].
+                    by apply SUB; rewrite ?FROMTS // -mem_enum JOBtsk.
+                  }
+                  {
+                    apply DIFFTASK with (t := t); [auto | by done |].
+                    by apply/existsP; exists cpu2; apply/eqP; rewrite SCHED2.
+                  }
+                }
+              }
+            }
+            {
+              move => tsk' /mapP [j' IN EQtsk'].
+              rewrite mem_pmap in IN.
+              move: IN => /mapP [cpu INcpu SCHED'].
+              rewrite mem_enum in INcpu.
+              rewrite mem_filter; apply/andP; split.
+              {
+                apply/existsP; exists cpu; apply/andP; split; first by apply SUB.
+                by rewrite /task_scheduled_on -SCHED' EQtsk'.
+              }
+              rewrite EQtsk' mem_filter; apply/andP; split; last by apply FROMTS.
+              apply/andP; split; last first.
+              {
+                apply/existsP; exists cpu; apply/andP; split; first by done.
+                by apply APA with (t := t); rewrite /scheduled_on SCHED'.
+              }
+              apply/andP; split.
+              {
+                by rewrite -JOBtsk; apply FP with (cpu := cpu) (t := t);
+                  [| by apply/eqP; rewrite SCHED' | apply SUB; rewrite JOBtsk].
+              }
+              {
+                apply/eqP; intro SAMEtsk.
+                move: BACK => /andP [PENDING NOTSCHED].
+                assert (SCHEDULED': scheduled sched j' t).
+                {
+                  by apply/existsP; exists cpu; rewrite /scheduled_on -SCHED'.
+                }
+                move: (SCHEDULED') => PENDING'.
+                apply scheduled_implies_pending with (job_cost0 := job_cost) in PENDING';
+                  try by done.
+                assert (BUG: j' = j).
+                {
+                  apply platform_fp_no_multiple_jobs_of_tsk with (task_cost0 := task_cost)
+                    (task_period0 := task_period) (task_deadline0 := task_deadline)
+                    (job_cost0 := job_cost) (job_task0 := job_task)
+                    (sched0 := sched) (tsk0 := tsk) (t0 := t);
+                    try (by done);
+                    [by apply PARAMS | by apply/andP; split | |].
+                  {
+                    apply leq_trans with (n := job_arrival j + R); first by done.
+                    by rewrite leq_add2l; apply leq_trans with (n := task_deadline tsk);
+                      last by apply CONSTR; rewrite -JOBtsk FROMTS.
+                  }
+                  {
+                    intros j0 JOB0 LT0.
+                    apply completion_monotonic with (t0 := job_arrival j0 + R);
+                      [by done | | by apply BEFOREok].
+                    by rewrite leq_add2l; apply leq_trans with (n := task_deadline tsk);
+                      last by apply CONSTR; rewrite -JOBtsk FROMTS.
+                  }
+                }
+                by rewrite -BUG SCHEDULED' in NOTSCHED.
+              }
+            }
+          }
+        Qed.
+
+        (* Before stating the next lemma, let (num_tasks_exceeding delta) be the
+           number of interfering tasks that can execute on (alpha' tsk) whose
+           interference x is larger than delta. *)
+        Let num_tasks_exceeding delta := count (fun i => x i >= delta) (hp_tasks_in alpha').
+
+        (* 6) Now we prove that, for any delta, if (num_task_exceeding delta > 0), then the
+              cumulative interference caused by the complementary set of interfering tasks fills
+              the remaining, not-completely-full (#|alpha' tsk| - num_tasks_exceeding delta)
+              processors. *)
+        Lemma bertogna_fp_interference_in_non_full_processors :
+          forall delta,
+            0 < num_tasks_exceeding delta < #|alpha' tsk| ->
+            \sum_(i <- hp_tasks_in alpha' | x i < delta) x i >= delta * (#|alpha' tsk| - num_tasks_exceeding delta).
+        Proof.
+          have ATMOST := platform_at_most_one_pending_job_of_each_task.
+          have INV := bertogna_fp_alpha'_is_full.
+          have DIFFTASK := bertogna_fp_interference_by_different_tasks.
+          have COMP := bertogna_fp_previous_interfering_jobs_complete_by_their_period.
+          rename H_all_jobs_from_taskset into FROMTS,
+                 H_valid_task_parameters into PARAMS,
+                 H_job_of_tsk into JOBtsk, H_affinity_subset into SUB,
+                 H_sporadic_tasks into SPO, H_respects_affinity into APA,
+                 H_constrained_deadlines into RESTR,
+                 H_sequential_jobs into SEQ, H_enforces_FP_policy into FP.
+          unfold sporadic_task_model in *.
+          move => delta /andP [HAS LT]. 
+          rewrite -has_count in HAS.
+
+          set some_interference_A := fun t =>
+            has (fun tsk_k => backlogged job_cost sched j t &&
+                              (x tsk_k >= delta) &&
+                              scheduled_on_alpha_tsk t tsk_k)
+              (hp_tasks_in alpha').
+          set total_interference_B := fun t =>
+              backlogged job_cost sched j t *
+              count (fun tsk_k => (x tsk_k < delta) &&
+                    scheduled_on_alpha_tsk t tsk_k) (hp_tasks_in alpha').
+
+          apply leq_trans with ((\sum_(job_arrival j <= t < job_arrival j + R)
+                                some_interference_A t) * (#|alpha' tsk| - num_tasks_exceeding delta)).
+          {
+            rewrite leq_mul2r; apply/orP; right.
+            move: HAS => /hasP HAS; destruct HAS as [tsk_a INa LEa].
+            apply leq_trans with (n := x tsk_a); first by apply LEa.
+            unfold x, task_interference, some_interference_A.
+            apply leq_sum_nat; move => t /andP [GEt LTt] _.
+            destruct (backlogged job_cost sched j t) eqn:BACK;
+              last by rewrite (eq_bigr (fun x => 0)); [by simpl_sum_const | by ins].
+            destruct ([exists cpu, can_execute_on alpha (job_task j) cpu &&
+                                task_scheduled_on job_task sched tsk_a cpu t]) eqn:SCHED;
+              last first.
+            {
+              apply negbT in SCHED; rewrite negb_exists in SCHED; move: SCHED => /forallP ALL.
+              rewrite (eq_bigr (fun x => 0)); first by simpl_sum_const.
+              by intros cpu _; specialize (ALL cpu); apply negbTE in ALL; rewrite ALL.
+            }
+            move: SCHED => /existsP [cpu /andP [CAN SCHED]].
+            apply leq_trans with (n := 1); last first.
+            {
+              rewrite lt0b; apply/hasP; exists tsk_a; first by done.
+              rewrite LEa 2!andTb; apply/existsP; exists cpu.
+              by apply/andP; split; first by rewrite -JOBtsk.
+            }
+            rewrite (bigD1 cpu) /= // SCHED.
+            rewrite (eq_bigr (fun x => 0)); first by simpl_sum_const; rewrite leq_b1.
+            intros cpu' DIFF.
+            apply/eqP; rewrite eqb0; apply/negP.
+            move => /andP [ALPHA' SCHED'].
+            move: DIFF => /negP DIFF; apply DIFF; apply/eqP.
+            unfold task_scheduled_on in *.
+            destruct (sched cpu t) as [j1|] eqn:SCHED1; last by done.
+            destruct (sched cpu' t) as [j2|] eqn:SCHED2; last by done.
+            move: SCHED SCHED' => /eqP JOB /eqP JOB'.
+            subst tsk_a; symmetry in JOB'.
+            assert (BUG: j1 = j2).
+            {
+              assert (PENDING1: pending job_cost sched j1 t).
+              {
+                apply scheduled_implies_pending; try by done.
+                by apply/existsP; exists cpu; rewrite /scheduled_on -SCHED1.
+              }
+              assert (SCHED2': pending job_cost sched j2 t).
+              {
+                apply scheduled_implies_pending; try by done.
+                by apply/existsP; exists cpu'; rewrite /scheduled_on -SCHED2. 
+              }
+              apply platform_fp_no_multiple_jobs_of_interfering_tasks with
+                (task_period0 := task_period) (tsk0 := tsk) (alpha0 := alpha)
+                (higher_eq_priority0 := higher_eq_priority) (t0 := t)
+                (job_cost0 := job_cost) (job_task0 := job_task) (sched0 := sched);
+                  rewrite ?JOBtsk ?SAMEtsk //.
+                {
+                  by intros j0 tsk0 TSK0 LE; subst tsk0; apply COMP.
+                }
+                {
+                  apply/andP; split; last first.
+                  {
+                    apply/existsP; exists cpu; apply/andP; split;
+                      first by rewrite -JOBtsk.
+                    by apply APA with (t := t); apply/eqP.
+                  }
+                  apply/andP; split.
+                  {
+                    by rewrite -JOBtsk; apply FP with (cpu := cpu) (t := t);
+                      [by done | by apply/eqP | by done]. 
+                  }
+                  {
+                    apply DIFFTASK with (t := t); [auto | by done |].
+                    by apply/existsP; exists cpu; apply/eqP.
+                  }
+                }
+              }
+              by subst j2; apply SEQ with (j := j1) (t := t).
+            }
+
+          apply leq_trans with (\sum_(job_arrival j <= t < job_arrival j + R)
+                                     total_interference_B t).
+          {
+            rewrite big_distrl /=.
+            apply leq_sum_nat; move => t LEt _.
+            unfold some_interference_A, total_interference_B. 
+            destruct (backlogged job_cost sched j t) eqn:BACK;
+              [rewrite mul1n /= | by rewrite has_pred0 //].
+
+            destruct (has (fun tsk_k : sporadic_task => (delta <= x tsk_k) &&
+                       scheduled_on_alpha_tsk t tsk_k) (hp_tasks_in alpha')) eqn:HAS';
+              last by done.
+            rewrite mul1n; move: HAS => /hasP [tsk_k INk LEk].
+            unfold num_tasks_exceeding.
+            apply leq_trans with (n := #|alpha' tsk| -
+                         count (fun i => (x i >= delta) &&
+                            scheduled_on_alpha_tsk t i) (hp_tasks_in alpha')).
+            {
+              apply leq_sub2l.
+              rewrite -2!sum1_count big_mkcond /=.
+              rewrite [\sum_(_ <- _ | _ <= _)_]big_mkcond /=.
+              apply leq_sum; intros i _.
+              by destruct (scheduled_on_alpha_tsk t i);
+                [by rewrite andbT | by rewrite andbF].
+            }
+            rewrite -count_filter -[count _ (hp_tasks_in _)]count_filter.
+            eapply leq_trans with (n := count (predC (fun tsk => delta <= x tsk)) _);
+              last by apply eq_leq, eq_in_count; red; ins; rewrite ltnNge.
+            rewrite leq_subLR count_predC size_filter.
+            move: INV => INV. by apply (INV t).
+          }
+          {
+            unfold x at 2, total_interference_B.
+            rewrite exchange_big /=; apply leq_sum; intros t _.
+            destruct (backlogged job_cost sched j t) eqn:BACK; last by ins.
+            rewrite mul1n -sum1_count.
+            rewrite big_mkcond [\sum_(i <- hp_tasks_in alpha' | _ < _) _]big_mkcond /=.
+            apply leq_sum_seq; move => tsk_k IN _.
+            destruct (x tsk_k < delta); [rewrite andTb | by rewrite andFb].
+            destruct (scheduled_on_alpha_tsk t tsk_k) eqn:SCHED; last by done.
+            move: SCHED => /existsP [cpu /andP [ALPHA SCHED]].
+            rewrite (bigD1 cpu) /= // JOBtsk SCHED andbT.
+            by rewrite /can_execute_on ALPHA.
+          }
+        Qed.
+
+        (* 7) Based on lemma (6), we prove that, for any interval delta, if the sum of per-task
+              interference exceeds (delta * |alpha' tsk|), the same applies for the
+              sum of the minimum of the interference and delta. *)
+        Lemma bertogna_fp_minimum_exceeds_interference :
+          forall delta,
+            \sum_(tsk_k <- hp_tasks_in alpha') x tsk_k >= delta * #|alpha' tsk| ->
+               \sum_(tsk_k <- hp_tasks_in alpha') minn (x tsk_k) delta >=
+               delta * #|alpha' tsk|.
+        Proof.
+          intros delta SUMLESS.
+          set more_interf := fun tsk_k => x tsk_k >= delta.
+          rewrite [\sum_(_ <- _) minn _ _](bigID more_interf) /=.
+          unfold more_interf, minn.
+          rewrite [\sum_(_ <- _ | delta <= _)_](eq_bigr (fun i => delta));
+            last by intros i COND; rewrite leqNgt in COND; destruct (delta > x i).
+          rewrite [\sum_(_ <- _ | ~~_)_](eq_big (fun i => x i < delta)
+                                                (fun i => x i));
+            [| by red; ins; rewrite ltnNge
+             | by intros i COND; rewrite -ltnNge in COND; rewrite COND].
+
+          (* Case 1: num_tasks_exceeding = 0 *)
+          destruct (~~ has (fun i => delta <= x i) (hp_tasks_in alpha')) eqn:HASa.
+          {
+            rewrite [\sum_(_ <- _ | _ <= _) _]big_hasC; last by apply HASa.
+            rewrite big_seq_cond; move: HASa => /hasPn HASa.
+            rewrite add0n (eq_bigl (fun i => (i \in hp_tasks_in alpha') && true));
+              last by red; intros tsk_k; destruct (tsk_k \in hp_tasks_in alpha') eqn:INk;
+                [by rewrite andTb ltnNge; apply HASa | by rewrite andFb].
+            by rewrite -big_seq_cond.
+          } apply negbFE in HASa.
+
+          (* Case 2: num_tasks_exceeding >= |alpha' tsk| *)
+          destruct (num_tasks_exceeding delta >= #|alpha' tsk|) eqn:CARD.
+          {
+            apply leq_trans with (delta * num_tasks_exceeding delta);
+              first by rewrite leq_mul2l; apply/orP; right.
+            unfold num_tasks_exceeding; rewrite -sum1_count big_distrr /=.
+            rewrite -[\sum_(_ <- _ | _) _]addn0.
+            by apply leq_add; [by apply leq_sum; ins; rewrite muln1|by ins].
+          } apply negbT in CARD; rewrite -ltnNge in CARD.
+
+          (* Case 3: num_tasks_exceeding < |alpha' tsk| *)
+          rewrite big_const_seq iter_addn addn0; fold num_tasks_exceeding.
+          apply leq_trans with (n := delta * num_tasks_exceeding delta +
+                                     delta * (#|alpha' tsk| - num_tasks_exceeding delta));
+            first by rewrite -mulnDr subnKC //; apply ltnW.
+          rewrite leq_add2l bertogna_fp_interference_in_non_full_processors //.
+          by apply/andP; split; [by rewrite -has_count | by done].
+        Qed.
+
+        (* 8) Note that lemma (7) only refers to interference within subaffinity (alpha' tsk).
+              To reason about the interference incurred by job j in its complete affinity,
+              we prove that an exceeding interference on affinity (alpha tsk) also
+              implies an exceeding interference on the subaffinity (alpha' tsk). *)
+        Lemma bertogna_fp_interference_on_subaffinity :
+          forall delta,
+            \sum_(tsk_k <- hp_tasks_in alpha) x tsk_k >= delta * #|alpha tsk| ->
+            \sum_(tsk_k <- hp_tasks_in alpha') x tsk_k >= delta * #|alpha' tsk|.
+        Proof.
+          have ALL := bertogna_fp_all_cpus_in_affinity_busy.
+          have SUB := bertogna_fp_all_cpus_in_subaffinity_busy.
+          rename H_at_least_one_cpu into NONEMPTY',
+                 H_job_of_tsk into JOBtsk, H_affinity_subset into SUBSET,
+                 H_all_jobs_from_taskset into FROMTS.
+          intros delta LE.
+          rewrite ALL in LE.
+          apply leq_trans with (n := X * #|alpha' tsk|); last by apply SUB.
+          rewrite leq_mul2r in LE;  move: LE => /orP [/eqP EMPTY | LEx];
+            last by rewrite leq_mul2r; apply/orP; right.
+          assert (NONEMPTY: #|alpha tsk| > 0).
+          {
+            apply leq_trans with (n := #|alpha' tsk|);
+              last by apply leq_subaffinity, SUBSET; rewrite -JOBtsk FROMTS.
+            by apply NONEMPTY'; rewrite -JOBtsk FROMTS.
+          }
+          by rewrite EMPTY ltnn in NONEMPTY.
+        Qed.
+
+        (* 9) Next, using lemmas (0), (3), (7) and (8), we prove that the reduction-based
+              interference bound is not enough to cover the sum of the minima over all tasks
+              (artifact of the proof by contradiction). *)
+        Lemma bertogna_fp_sum_exceeds_total_interference:
+          \sum_((tsk_other, R_other) <- hp_bounds | hp_task_in (alpha' tsk) tsk_other)
+           minn (x tsk_other) (R - task_cost tsk + 1) >
+          total_interference_bound_fp task_cost task_period alpha tsk
+                           (alpha' tsk) hp_bounds R higher_eq_priority.
+        Proof.
+          have EXCEEDS := bertogna_fp_minimum_exceeds_interference.
+          have SUB := bertogna_fp_interference_on_subaffinity.
+          have ALLBUSY := bertogna_fp_all_cpus_in_affinity_busy.
+          have TOOMUCH := bertogna_fp_too_much_interference.
+
+          rename H_hp_bounds_has_interfering_tasks into HAS,
+                 H_response_time_recurrence_holds into REC,
+                 H_at_least_one_cpu into NONEMPTY,
+                 H_affinity_subset into SUBSET.
+          unfold total_interference_bound_fp.
+          apply leq_trans with (n := \sum_(tsk_other <- hp_tasks_in alpha') minn (x tsk_other) (R - task_cost tsk + 1));
+            last first.
+          {
+            rewrite (eq_bigr (fun i => minn (x (fst i)) (R - task_cost tsk + 1)));
+              last by ins; destruct i.
+            rewrite (eq_bigl (fun i => hp_task_in (alpha' tsk) (fst i)));
+              last by intro p; destruct p.
+            have MAP := @big_map _ 0 addn _ _ (fun x => fst x) hp_bounds _ (fun y => minn (x y) (R - task_cost tsk + 1)).
+            rewrite -MAP.
+            rewrite -[\sum_(_ <- [seq fst _ | _ <- _] | _)_]big_filter. 
+            apply leq_sum_sub_uniq; first by apply filter_uniq; destruct ts.
+            red; move => tsk0 IN0.
+            rewrite mem_filter in IN0; move: IN0 => /andP [INTERF0 IN0].
+            rewrite mem_filter; apply/andP; split; first by done.
+            apply/mapP.
+            exploit (HAS tsk0); [by done | | move => [R0 INbounds0]].
+            {
+              move: INTERF0 => /andP [HP INTER].
+              apply/andP; split; first by done.
+              move: INTER => /existsP [cpu /andP [IN1 IN2]].
+              by apply/existsP; exists cpu; rewrite IN2 andbT; apply SUBSET.
+            }
+            by exists (tsk0, R0).
+          }
+          apply ltn_div_trunc with (d := #|alpha' tsk|); first by apply NONEMPTY.
+          rewrite -(ltn_add2l (task_cost tsk)) -REC.
+          rewrite -addn1 -leq_subLR.
+          rewrite -[R + 1 - _]subh1; last by rewrite REC; apply leq_addr.
+          rewrite leq_divRL; last by apply NONEMPTY.
+          apply EXCEEDS, SUB.
+          apply leq_trans with (n := X * #|alpha tsk|); last by rewrite ALLBUSY.
+          by rewrite leq_mul2r; apply/orP; right; apply TOOMUCH.
+        Qed.
+
+        (* 10) After concluding that the sum of the minima exceeds (R - e_i + 1),
+              we prove that there exists a tuple (tsk_k, R_k) that satisfies
+              min (x_k, R - e_i + 1) > min (W_k, R - e_i + 1).
+              This implies that x_k > W_k, which is of course a contradiction,
+              since W_k is a valid task interference bound. *)
+        Lemma bertogna_fp_exists_task_that_exceeds_bound :
+          exists tsk_k R_k,
+            (tsk_k, R_k) \in hp_bounds /\
+            (minn (x tsk_k) (R - task_cost tsk + 1) >
+              minn (workload_bound tsk_k R_k) (R - task_cost tsk + 1)).
+        Proof.
+          assert (HAS: has (fun tup : task_with_response_time =>
+                               let (tsk_k, R_k) := tup in
+                                 (minn (x tsk_k) (R - task_cost tsk + 1) >
+                                  minn (workload_bound tsk_k R_k)(R - task_cost tsk + 1)))
+                              hp_bounds).
+            {
+              apply/negP; unfold not; intro NOTHAS.
+              move: NOTHAS => /negP /hasPn ALL.
+              have SUM := bertogna_fp_sum_exceeds_total_interference.
+              rewrite -[_ < _]negbK in SUM.
+              move: SUM => /negP SUM; apply SUM; rewrite -leqNgt.
+              unfold total_interference_bound_fp.
+              rewrite big_seq_cond.
+              rewrite [\sum_(i <- _| let '(tsk_other,_) := i in _)_]big_seq_cond.
+              apply leq_sum; move => [tsk_k R_k /andP [IN INTERk]].
+              specialize (ALL (tsk_k, R_k)); simpl in ALL.
+              by rewrite leqNgt; apply ALL, IN.
+            }
+          move: HAS => /hasP HAS; destruct HAS as [[tsk_k R_k] HPk MIN].
+          by exists tsk_k, R_k; repeat split.
+        Qed.
+
+      End DerivingContradiction.
+
+    End Lemmas.
+    
+    (* Using the lemmas above, we prove that R bounds the response time of tsk. *)
+    Theorem bertogna_cirinei_response_time_bound_fp :
+      response_time_bounded_by tsk R.
+    Proof.
+      have EX := bertogna_fp_exists_task_that_exceeds_bound.
+      have WORKLOAD := bertogna_fp_workload_bounds_interference.
+      rename H_response_time_bounds_ge_cost into GE_COST,
+             H_interfering_tasks_miss_no_deadlines into NOMISS,
+             H_response_time_recurrence_holds into REC,
+             H_response_time_of_interfering_tasks_is_known into RESP,
+             H_response_time_no_larger_than_deadline into LEdl.
+      intros j JOBtsk.
+       
+      (* First, rewrite the claim in terms of the *absolute* response-time bound (arrival + R) *)
+      remember (job_arrival j + R) as ctime.
+      
+      (* Now, we apply strong induction on the absolute response-time bound. *)
+      generalize dependent j.
+      induction ctime as [ctime IH] using strong_ind.
+
+      intros j JOBtsk EQc; subst ctime.
+
+      (* First, let's simplify the induction hypothesis. *)
+      assert (BEFOREok: forall (j0: JobIn arr_seq),
+                          job_task j0 = tsk ->
+                          job_arrival j0 < job_arrival j ->
+                          service sched j0 (job_arrival j0 + R) == job_cost j0).
+      {
+        by ins; apply IH; try (by done); rewrite ltn_add2r.
+      } clear IH.
+              
+      unfold response_time_bounded_by, is_response_time_bound_of_task,
+             completed, completed_jobs_dont_execute, valid_sporadic_job in *.
+
+      (* Now we start the proof. Assume by contradiction that job j
+         is not complete at time (job_arrival j + R). *)
+      destruct (completed job_cost sched j (job_arrival j + R)) eqn:NOTCOMP;
+        first by done.
+      apply negbT in NOTCOMP; exfalso.
+
+      (* We derive a contradiction using the previous lemmas. *)
+      specialize (EX j JOBtsk NOTCOMP BEFOREok).
+      destruct EX as [tsk_k [R_k [HPk LTmin]]].
+      unfold minn at 1 in LTmin.
+      specialize (WORKLOAD j tsk_k R_k HPk).
+      destruct (W task_cost task_period tsk_k R_k R < R - task_cost tsk + 1); rewrite leq_min in LTmin; 
+        last by move: LTmin => /andP [_ BUG]; rewrite ltnn in BUG.
+      move: LTmin => /andP [BUG _]; des.
+      apply leq_trans with (p := W task_cost task_period tsk_k R_k R) in BUG; last by done.
+      by rewrite ltnn in BUG.
+    Qed.
+
+  End ResponseTimeBound.
+
+End ResponseTimeAnalysisFP.
\ No newline at end of file
diff --git a/analysis/apa/interference_bound.v b/analysis/apa/interference_bound.v
new file mode 100644
index 0000000000000000000000000000000000000000..8814c15fd5e5d9dca4d056c30f5488799e982c9c
--- /dev/null
+++ b/analysis/apa/interference_bound.v
@@ -0,0 +1,45 @@
+Require Import rt.util.all.
+Require Import rt.model.apa.schedule.
+Require Import rt.analysis.apa.workload_bound.
+From mathcomp Require Import ssreflect ssrbool eqtype ssrnat seq fintype bigop.
+
+Module InterferenceBoundGeneric.
+
+  Section Definitions.
+
+    Import Schedule WorkloadBound.
+    
+    Context {sporadic_task: eqType}.
+    Variable task_cost: sporadic_task -> time.
+    Variable task_period: sporadic_task -> time.
+    Variable task_deadline: sporadic_task -> time.
+
+    Variable num_cpus: nat.
+    
+    (* Let tsk be the task to be analyzed. *)
+    Variable tsk: sporadic_task.
+
+    Let task_with_response_time := (sporadic_task * time)%type.
+    
+    (* Assume a known response-time bound for each interfering task ... *)
+    Variable R_prev: seq task_with_response_time.
+
+    (* ... and an interval length delta. *)
+    Variable delta: time.
+
+    Section PerTask.
+
+      Variable tsk_R: task_with_response_time.
+      Let tsk_other := fst tsk_R.
+      Let R_other := snd tsk_R.
+    
+      (* Based on the workload bound, Bertogna and Cirinei define the
+         following interference bound for a task. *)
+      Definition interference_bound_generic :=
+        minn (W task_cost task_period tsk_other R_other delta) (delta - (task_cost tsk) + 1).
+
+    End PerTask.
+
+  End Definitions.
+
+End InterferenceBoundGeneric.
\ No newline at end of file
diff --git a/analysis/apa/interference_bound_edf.v b/analysis/apa/interference_bound_edf.v
new file mode 100644
index 0000000000000000000000000000000000000000..ef241c8cca672c9b6c2af616e541b3ce1f87fe01
--- /dev/null
+++ b/analysis/apa/interference_bound_edf.v
@@ -0,0 +1,1189 @@
+Require Import rt.util.all rt.util.divround.
+Require Import rt.model.apa.task rt.model.apa.job rt.model.apa.schedule
+               rt.model.apa.task_arrival rt.model.apa.platform rt.model.apa.response_time
+               rt.model.apa.workload rt.model.apa.priority rt.model.apa.schedulability
+               rt.model.apa.interference rt.model.apa.interference_edf
+               rt.model.apa.affinity.
+Require Import rt.analysis.apa.workload_bound rt.analysis.apa.interference_bound.
+From mathcomp Require Import ssreflect ssrbool eqtype ssrnat seq fintype bigop div path.
+
+(* In this file, we define the reduction-based interference bound for APA
+   scheduling with EDF, based on Bertogna and Cirinei's interference bound. *)
+Module InterferenceBoundEDF.
+
+  Import Job SporadicTaskset Schedule ScheduleOfSporadicTask Schedulability
+         WorkloadBound ResponseTime Priority Affinity
+         SporadicTaskArrival Interference InterferenceEDF.
+  Export InterferenceBoundGeneric.
+
+  (* First we define Bertogna and Cirinei's EDF-specific interference bound. *)
+  Section SpecificBoundDef.
+    
+    Context {sporadic_task: eqType}.
+    Variable task_cost: sporadic_task -> time.
+    Variable task_period: sporadic_task -> time.
+    Variable task_deadline: sporadic_task -> time.
+    
+    (* Let tsk be the task to be analyzed. *)
+    Variable tsk: sporadic_task.
+
+    (* Consider the interference incurred by tsk in a window of length delta... *)
+    Variable delta: time.
+
+    (* ... due to a different task tsk_other, with response-time bound R_other. *)
+    Variable tsk_other: sporadic_task.
+    Variable R_other: time.
+
+    (* Bertogna and Cirinei define the following bound for task interference
+       under EDF scheduling. *)
+    Definition edf_specific_interference_bound :=
+      let d_tsk := task_deadline tsk in
+      let e_other := task_cost tsk_other in
+      let p_other := task_period tsk_other in
+      let d_other := task_deadline tsk_other in
+        (div_floor d_tsk p_other) * e_other +
+        minn e_other ((d_tsk %% p_other) - (d_other - R_other)).
+
+  End SpecificBoundDef.
+  
+  (* Next, we define the total interference bound for EDF, which combines the generic
+     and the EDF-specific bounds. *)
+  Section TotalInterferenceBoundEDF.
+
+    Context {sporadic_task: eqType}.
+    Variable task_cost: sporadic_task -> time.
+    Variable task_period: sporadic_task -> time.
+    Variable task_deadline: sporadic_task -> time.
+
+    Context {num_cpus: nat}.
+    
+    (* Assume that each task has a processor affinity alpha. *)
+    Variable alpha: task_affinity sporadic_task num_cpus.
+
+    Let task_with_response_time := (sporadic_task * time)%type.
+
+    (* Let tsk be the task to be analyzed, ... *)
+    Variable tsk: sporadic_task.
+
+    (* ... and let alpha' be any subaffinity of tsk. *)
+    Variable alpha': affinity num_cpus.
+    
+    (* Assume a known response-time bound for each interfering task ... *)
+    Variable R_prev: seq task_with_response_time.
+
+    (* ... and an interval length delta. *)
+    Variable delta: time.
+
+    (* First we recall the interference bounds. *)
+    Section RecallInterferenceBounds.
+
+      Variable tsk_R: task_with_response_time.
+      Let tsk_other := fst tsk_R.
+      Let R_other := snd tsk_R.
+
+      (* By combining Bertogna's interference bound for a work-conserving
+         scheduler ... *)
+      Let basic_interference_bound := interference_bound_generic task_cost task_period tsk delta tsk_R.
+
+      (* ... with and EDF-specific interference bound, ... *)
+      Let edf_specific_bound := edf_specific_interference_bound task_cost task_period task_deadline tsk tsk_other R_other.
+
+      (* ... Bertogna and Cirinei define the following interference bound
+         under EDF scheduling. *)
+      Definition interference_bound_edf :=
+        minn basic_interference_bound edf_specific_bound.
+
+    End RecallInterferenceBounds.
+
+    (* Next we define the computation of the total interference for APA scheduling. *)
+    Section TotalInterference.
+
+      (* Let (other_task_in alpha') denote the other tasks that can execute on alpha'. *)
+      Let other_task_in alpha' := different_task_in alpha tsk alpha'.
+
+      (* The total interference incurred by tsk is bounded by the sum of individual
+         interferences of the other tasks that can be scheduled on alpha'. *)
+      Definition total_interference_bound_edf :=
+        \sum_((tsk_other, R_other) <- R_prev | other_task_in alpha' tsk_other)
+           interference_bound_edf (tsk_other, R_other).
+
+    End TotalInterference.
+
+  End TotalInterferenceBoundEDF.
+  
+  (* In this section, we show that the EDF-specific interference bound is safe. *)
+  Section ProofSpecificBound.
+
+    Import Schedule Interference Platform SporadicTaskset Affinity.
+    
+    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_cost: Job -> time.
+    Variable job_deadline: Job -> time.
+    Variable job_task: Job -> sporadic_task.
+    
+    (* Assume any job arrival sequence... *)
+    Context {arr_seq: arrival_sequence Job}.
+
+    (* ... in which jobs arrive sporadically and have valid parameters. *)
+    Hypothesis H_sporadic_tasks:
+      sporadic_task_model task_period arr_seq job_task.
+    Hypothesis H_valid_job_parameters:
+      forall (j: JobIn arr_seq),
+        valid_sporadic_job task_cost task_deadline job_cost job_deadline job_task j.
+
+    (* Consider any schedule such that...*)
+    Variable num_cpus: nat.
+    Variable sched: schedule num_cpus arr_seq.
+
+    (* ...jobs do not execute before their arrival times nor longer
+       than their execution costs. *)
+    Hypothesis H_jobs_must_arrive_to_execute:
+      jobs_must_arrive_to_execute sched.
+    Hypothesis H_completed_jobs_dont_execute:
+      completed_jobs_dont_execute job_cost sched.
+
+    (* Also assume that jobs are sequential and that
+       there exists at least one processor. *)
+    Hypothesis H_sequential_jobs: sequential_jobs sched.
+    Hypothesis H_at_least_one_cpu: num_cpus > 0.
+
+    (* Assume that every job at any time has a processor affinity alpha. *)
+    Variable alpha: task_affinity sporadic_task num_cpus.
+    
+    (* Consider a task set ts where all jobs come from the task set
+       and tasks have valid parameters and constrained deadlines. *)
+    Variable ts: taskset_of sporadic_task.
+    Hypothesis all_jobs_from_taskset:
+      forall (j: JobIn arr_seq), job_task j \in ts.
+    Hypothesis H_valid_task_parameters:
+      valid_sporadic_taskset task_cost task_period task_deadline ts.
+    Hypothesis H_constrained_deadlines:
+      forall tsk, tsk \in ts -> task_deadline tsk <= task_period tsk.
+
+    Let no_deadline_is_missed_by_tsk (tsk: sporadic_task) :=
+      task_misses_no_deadline job_cost job_deadline job_task sched tsk.
+    Let response_time_bounded_by (tsk: sporadic_task) :=
+      is_response_time_bound_of_task job_cost job_task tsk sched.
+
+    (* Assume that the scheduler is a work-conserving EDF scheduler. *)
+    Hypothesis H_work_conserving: apa_work_conserving job_cost job_task sched alpha.
+    Hypothesis H_edf_weak_APA_scheduler:
+      enforces_JLDP_policy_under_weak_APA job_cost job_task sched alpha (EDF job_deadline).
+
+    (* Let tsk_i be the task to be analyzed, ...*)
+    Variable tsk_i: sporadic_task.
+    Hypothesis H_tsk_i_in_task_set: tsk_i \in ts.
+    
+    (* ... and j_i one of its jobs. *)
+    Variable j_i: JobIn arr_seq.
+    Hypothesis H_job_of_tsk_i: job_task j_i = tsk_i.
+
+    (* Let tsk_k denote any interfering task, ... *)
+    Variable tsk_k: sporadic_task.
+    Hypothesis H_tsk_k_in_task_set: tsk_k \in ts.
+
+    (* ...and R_k its response-time bound. *)
+    Variable R_k: time.
+    Hypothesis H_R_k_le_deadline: R_k <= task_deadline tsk_k.
+    
+    (* Consider a time window of length delta <= D_i, starting with j_i's arrival time. *)
+    Variable delta: time.
+    Hypothesis H_delta_le_deadline: delta <= task_deadline tsk_i.
+
+    (* Assume that the jobs of tsk_k satisfy the response-time bound before the end of the interval *)
+    Hypothesis H_all_previous_jobs_completed_on_time :
+      forall (j_k: JobIn arr_seq),
+        job_task j_k = tsk_k ->
+        job_arrival j_k + R_k < job_arrival j_i + delta ->
+        completed job_cost sched j_k (job_arrival j_k + R_k).
+
+    (* In this section, we prove that Bertogna and Cirinei's EDF interference bound
+       indeed bounds the interference caused by task tsk_k in the interval [t1, t1 + delta). *)
+    Section MainProof.
+                                    
+      (* Let's call x the task interference incurred by job j due to tsk_k. *)
+      Let x :=
+        task_interference job_cost job_task sched alpha j_i
+                          tsk_k (job_arrival j_i) (job_arrival j_i + delta).
+
+      (* Also, recall the EDF-specific interference bound for EDF. *)
+      Let interference_bound :=
+        edf_specific_interference_bound task_cost task_period task_deadline tsk_i tsk_k R_k.
+
+      (* Let's simplify the names a bit. *)
+      Let t1 := job_arrival j_i.
+      Let t2 := job_arrival j_i + delta.
+      Let D_i := task_deadline tsk_i.
+      Let D_k := task_deadline tsk_k.
+      Let p_k := task_period tsk_k.
+
+      Let n_k := div_floor D_i p_k.
+
+      (* Let's give a simpler name to job interference. *)
+      Let interference_caused_by := job_interference job_cost job_task sched alpha j_i.
+      
+      (* Identify the subset of jobs that actually cause interference *)
+      Let interfering_jobs :=
+        filter (fun (x: JobIn arr_seq) =>
+                 (job_task x == tsk_k) && (interference_caused_by x t1 t2 != 0))
+               (jobs_scheduled_between sched t1 t2).
+      
+      (* Now, consider the list of interfering jobs sorted by arrival time. *)
+      Let earlier_arrival := fun (x y: JobIn arr_seq) => job_arrival x <= job_arrival y.
+      Let sorted_jobs := (sort earlier_arrival interfering_jobs).
+
+      (* Now we proceed with the proof. The first step consists in simplifying the sum corresponding to the workload. *)
+      Section SimplifyJobSequence.
+
+        (* Use the alternative definition of task interference, based on
+           individual job interference. *)
+        Lemma interference_bound_edf_use_another_definition :
+          x <= \sum_(j <- jobs_scheduled_between sched t1 t2 | job_task j == tsk_k)
+                interference_caused_by j t1 t2.
+        Proof.
+          by apply interference_le_interference_joblist.
+        Qed.
+
+        (* Remove the elements that we don't care about from the sum *)
+        Lemma interference_bound_edf_simpl_by_filtering_interfering_jobs :
+          \sum_(j <- jobs_scheduled_between sched t1 t2 | job_task j == tsk_k)
+             interference_caused_by j t1 t2 = 
+          \sum_(j <- interfering_jobs) interference_caused_by j t1 t2.
+        Proof.
+          unfold interfering_jobs; rewrite big_filter.
+          rewrite big_mkcond; rewrite [\sum_(_ <- _ | _) _]big_mkcond /=.
+          apply eq_bigr; intros i _; clear -i.
+          destruct (job_task i == tsk_k); rewrite ?andTb ?andFb; last by done.
+          destruct (interference_caused_by i t1 t2 != 0) eqn:DIFF; first by done.
+          by apply negbT in DIFF; rewrite negbK in DIFF; apply/eqP.
+        Qed.
+
+        (* Then, we consider the sum over the sorted sequence of jobs. *)
+        Lemma interference_bound_edf_simpl_by_sorting_interfering_jobs :
+          \sum_(j <- interfering_jobs) interference_caused_by j t1 t2 =
+           \sum_(j <- sorted_jobs) interference_caused_by j t1 t2.
+        Proof.
+          by rewrite (eq_big_perm sorted_jobs) /=; last by rewrite -(perm_sort earlier_arrival).
+        Qed.
+
+        (* Note that both sequences have the same set of elements. *)
+        Lemma interference_bound_edf_job_in_same_sequence :
+          forall j,
+            (j \in interfering_jobs) = (j \in sorted_jobs).
+        Proof.
+          by apply perm_eq_mem; rewrite -(perm_sort earlier_arrival).
+        Qed.
+
+        (* Also recall that all jobs in the sorted sequence is an interfering job of tsk_k, ... *)
+        Lemma interference_bound_edf_all_jobs_from_tsk_k :
+          forall j,
+            j \in sorted_jobs ->
+            job_task j = tsk_k /\
+            interference_caused_by j t1 t2 != 0 /\
+            j \in jobs_scheduled_between sched t1 t2.
+        Proof.
+          intros j LT.
+          rewrite -interference_bound_edf_job_in_same_sequence mem_filter in LT.
+          by move: LT => /andP [/andP [/eqP JOBi SERVi] INi]; repeat split.
+        Qed.
+
+        (* ...and consecutive jobs are ordered by arrival. *)
+        Lemma interference_bound_edf_jobs_ordered_by_arrival :
+          forall i elem,
+            i < (size sorted_jobs).-1 ->
+            earlier_arrival (nth elem sorted_jobs i) (nth elem sorted_jobs i.+1).
+        Proof.
+          intros i elem LT.
+          assert (SORT: sorted earlier_arrival sorted_jobs).
+            by apply sort_sorted; unfold total, earlier_arrival; ins; apply leq_total.
+          by destruct sorted_jobs; simpl in *; [by rewrite ltn0 in LT | by apply/pathP].
+        Qed.
+
+        (* Finally, for any job of task tsk_k, the interference is bounded by the task cost. *)
+        Lemma interference_bound_edf_interference_le_task_cost :
+          forall j,
+            j \in interfering_jobs ->
+            interference_caused_by j t1 t2 <= task_cost tsk_k.
+        Proof.
+          rename H_valid_job_parameters into PARAMS.
+          intros j; rewrite mem_filter; move => /andP [/andP [/eqP JOBj _] _].
+          specialize (PARAMS j); des.
+          apply leq_trans with (n := service_during sched j t1 t2);
+            first by apply job_interference_le_service.
+          by apply cumulative_service_le_task_cost with (job_task0 := job_task)
+                              (task_deadline0 := task_deadline) (job_cost0 := job_cost)
+                                                        (job_deadline0 := job_deadline).
+        Qed.
+
+      End SimplifyJobSequence.
+
+      (* Next, we show that if the number of jobs is no larger than n_k,
+         the workload bound trivially holds. *)
+      Section InterferenceFewJobs.
+
+        Hypothesis H_few_jobs: size sorted_jobs <= n_k.
+        
+        Lemma interference_bound_edf_holds_for_at_most_n_k_jobs :
+           \sum_(j <- sorted_jobs) interference_caused_by j t1 t2 <=
+             interference_bound.
+        Proof.
+          rewrite -[\sum_(_ <- _ | _) _]addn0 leq_add //.
+          apply leq_trans with (n := \sum_(x <- sorted_jobs) task_cost tsk_k);
+            last by rewrite big_const_seq iter_addn addn0 mulnC leq_mul2r; apply/orP; right.
+          {
+            rewrite [\sum_(_ <- _) interference_caused_by _ _ _]big_seq_cond.
+            rewrite [\sum_(_ <- _) task_cost _]big_seq_cond.
+            apply leq_sum; intros i; move/andP => [INi _].
+            rewrite -interference_bound_edf_job_in_same_sequence in INi.
+            by apply interference_bound_edf_interference_le_task_cost.
+          }
+        Qed.
+
+      End InterferenceFewJobs.
+
+      (* Otherwise, assume that the number of jobs is larger than n_k >= 0. *)
+      Section InterferenceManyJobs.
+
+        Hypothesis H_many_jobs: n_k < size sorted_jobs.
+
+        (* This trivially implies that there's at least one job. *)
+        Lemma interference_bound_edf_at_least_one_job: size sorted_jobs > 0.
+        Proof.
+          by apply leq_ltn_trans with (n := n_k).
+        Qed.
+
+        (* Let j_fst be the first job, and a_fst its arrival time. *)
+        Variable elem: JobIn arr_seq.
+        Let j_fst := nth elem sorted_jobs 0.
+        Let a_fst := job_arrival j_fst.
+
+        (* In this section, we prove some basic lemmas about j_fst. *)
+        Section FactsAboutFirstJob.
+          
+          (* The first job is an interfering job of task tsk_k. *)
+          Lemma interference_bound_edf_j_fst_is_job_of_tsk_k :
+            job_task j_fst = tsk_k /\
+            interference_caused_by j_fst t1 t2 != 0 /\
+            j_fst \in jobs_scheduled_between sched t1 t2.
+          Proof.
+            by apply interference_bound_edf_all_jobs_from_tsk_k, mem_nth,
+                     interference_bound_edf_at_least_one_job.
+          Qed.
+
+          (* The deadline of j_fst is the deadline of tsk_k. *)
+          Lemma interference_bound_edf_j_fst_deadline :
+            job_deadline j_fst = task_deadline tsk_k.
+          Proof.
+            unfold valid_sporadic_job in *.
+            rename H_valid_job_parameters into PARAMS.
+            have FST := interference_bound_edf_j_fst_is_job_of_tsk_k.
+            destruct FST as [FSTtask _].
+            by specialize (PARAMS j_fst); des; rewrite PARAMS1 FSTtask.
+          Qed.
+
+          (* The deadline of j_i is the deadline of tsk_i. *)
+          Lemma interference_bound_edf_j_i_deadline :
+            job_deadline j_i = task_deadline tsk_i.
+          Proof.
+            unfold valid_sporadic_job in *.
+            rename H_valid_job_parameters into PARAMS,
+                   H_job_of_tsk_i into JOBtsk.
+            by specialize (PARAMS j_i); des; rewrite PARAMS1 JOBtsk.
+          Qed.
+
+          (* If j_fst completes by its response-time bound, then t1 <= a_fst + R_k,
+             where t1 is the beginning of the time window (arrival of j_i). *)
+          Lemma interference_bound_edf_j_fst_completion_implies_rt_bound_inside_interval :
+            completed job_cost sched j_fst (a_fst + R_k) ->
+            t1 <= a_fst + R_k.
+          Proof.
+            intros RBOUND.
+            rewrite leqNgt; apply/negP; unfold not; intro BUG.
+            have FST := interference_bound_edf_j_fst_is_job_of_tsk_k.
+            destruct FST as [_ [ FSTserv _]].
+            move: FSTserv => /negP FSTserv; apply FSTserv.
+            rewrite -leqn0; apply leq_trans with (n := service_during sched j_fst t1 t2);
+              first by apply job_interference_le_service.
+            rewrite leqn0; apply/eqP.
+            by apply cumulative_service_after_job_rt_zero with (job_cost0 := job_cost) (R := R_k);
+              try (by done); apply ltnW.
+          Qed. 
+          
+        End FactsAboutFirstJob.
+        
+        (* Now, let's prove the interference bound for the particular case of a single job.
+           This case must be solved separately because the single job can simultaneously
+           be carry-in and carry-out job, so its response time is not necessarily
+           bounded by R_k (from the hypothesis H_all_previous_jobs_completed_on_time). *)
+        Section InterferenceSingleJob.
+
+          (* Assume that there's at least one job in the sorted list. *)
+          Hypothesis H_only_one_job: size sorted_jobs = 1.
+          
+          (* Since there's only one job, we simplify the terms in the interference bound. *)
+          Lemma interference_bound_edf_simpl_when_there's_one_job :
+            D_i %% p_k - (D_k - R_k) = D_i - (D_k - R_k).
+          Proof.
+            rename H_many_jobs into NUM,
+                   H_valid_task_parameters into TASK_PARAMS,
+                   H_tsk_k_in_task_set into INk.
+            unfold valid_sporadic_taskset, is_valid_sporadic_task,
+                   interference_bound, edf_specific_interference_bound in *.
+            rewrite H_only_one_job in NUM.
+            rewrite ltnS leqn0 in NUM; move: NUM => /eqP EQnk.
+            move: EQnk => /eqP EQnk; unfold n_k, div_floor in EQnk.
+            rewrite -leqn0 leqNgt divn_gt0 in EQnk;
+              last by specialize (TASK_PARAMS tsk_k INk); des.
+            by rewrite -ltnNge in EQnk; rewrite modn_small //.
+          Qed.
+
+          (* Next, we show that if j_fst completes by its response-time bound R_k,
+             then then interference bound holds. *)
+          Section ResponseTimeOfSingleJobBounded.
+
+            Hypothesis H_j_fst_completed_by_rt_bound :
+              completed job_cost sched j_fst (a_fst + R_k).
+            
+            Lemma interference_bound_edf_holds_for_single_job_that_completes_on_time :
+              job_interference job_cost job_task sched alpha j_i j_fst t1 t2 <= D_i - (D_k - R_k).
+            Proof.
+              rename H_j_fst_completed_by_rt_bound into RBOUND.
+              have AFTERt1 :=
+                interference_bound_edf_j_fst_completion_implies_rt_bound_inside_interval RBOUND.
+              have FST := interference_bound_edf_j_fst_is_job_of_tsk_k.
+              destruct FST as [_ [ LEdl _]].            
+              apply interference_under_edf_implies_shorter_deadlines with
+                   (job_deadline0 := job_deadline) in LEdl; try (by done).
+              destruct (D_k - R_k <= D_i) eqn:LEdk; last first.
+              {
+                apply negbT in LEdk; rewrite -ltnNge in LEdk.
+                apply leq_trans with (n := 0); last by done.
+                apply leq_trans with (n := job_interference job_cost job_task sched alpha j_i j_fst
+                                                                                  (a_fst + R_k) t2).
+                {
+                  apply extend_sum; last by apply leqnn.
+                  rewrite -(leq_add2r D_i).
+                  rewrite interference_bound_edf_j_fst_deadline
+                          interference_bound_edf_j_i_deadline in LEdl.
+                  apply leq_trans with (n := a_fst + D_k); last by done.
+                  rewrite -addnA leq_add2l.
+                  by apply ltnW; rewrite -ltn_subRL.
+                }
+                apply leq_trans with (n := service_during sched j_fst (a_fst + R_k) t2);
+                  first by apply job_interference_le_service.
+                unfold service_during; rewrite leqn0; apply/eqP.
+                by apply cumulative_service_after_job_rt_zero with (job_cost0 := job_cost) (R := R_k);
+                  try (by done); apply leqnn.
+              }
+              {
+                rewrite -(leq_add2r (D_k - R_k)) subh1 // -addnBA // subnn addn0.
+                assert (SUBST: D_k - R_k = \sum_(a_fst + R_k <= i < a_fst + D_k) 1).
+                {
+                  rewrite big_const_nat iter_addn mul1n addn0.
+                  rewrite addnC -subnBA; last by apply leq_addr.
+                  by rewrite addnC -addnBA // subnn addn0.
+                }
+                apply leq_trans with (n := job_interference job_cost job_task sched alpha j_i j_fst t1
+                                                            (a_fst + D_k) + (D_k - R_k)).
+                {
+                  rewrite leq_add2r.
+                  destruct (t2 <= a_fst + R_k) eqn:LEt2.
+                  {
+                    apply extend_sum; first by apply leqnn.
+                    apply leq_trans with (n := a_fst + R_k); first by done.
+                    by rewrite leq_add2l; apply H_R_k_le_deadline.
+                  }
+                  {
+                    unfold job_interference.
+                    apply negbT in LEt2; rewrite -ltnNge in LEt2.
+                    rewrite -> big_cat_nat with (n := a_fst + R_k);
+                      [simpl | by apply AFTERt1 | by apply ltnW].
+                    apply leq_trans with (n := job_interference job_cost job_task sched alpha j_i j_fst t1
+                                 (a_fst + R_k) + service_during sched j_fst (a_fst + R_k) t2).
+                    {
+                      rewrite leq_add2l.
+                      by apply job_interference_le_service.
+                    }
+                    unfold service_during.
+                    rewrite (cumulative_service_after_job_rt_zero job_cost _ _ _ R_k) //.
+                    rewrite addn0; apply extend_sum; first by apply leqnn.
+                    by rewrite leq_add2l; apply H_R_k_le_deadline.
+                  }
+                }
+
+                unfold job_interference.
+                rewrite -> big_cat_nat with (n := a_fst + R_k);
+                  [simpl| by apply AFTERt1 | by rewrite leq_add2l; apply H_R_k_le_deadline].
+                apply leq_trans with (n := job_interference job_cost job_task sched alpha j_i j_fst t1
+                        (a_fst+R_k) + service_during sched j_fst (a_fst+R_k) (a_fst+D_k) + (D_k-R_k));
+                  first by rewrite leq_add2r leq_add2l job_interference_le_service.
+                unfold service_during.
+                rewrite (cumulative_service_after_job_rt_zero job_cost _ _ _ R_k) // addn0.
+                apply leq_trans with (n := (\sum_(t1 <= t < a_fst + R_k) 1) +
+                                           \sum_(a_fst + R_k <= t < a_fst + D_k) 1).
+                {
+                  apply leq_add; last by rewrite SUBST.
+                  rewrite big_const_nat iter_addn mul1n addn0.
+                  rewrite -{1}[a_fst + R_k](addKn t1) -addnBA //.
+                  by apply job_interference_le_delta.
+                }
+                rewrite -big_cat_nat;
+                  [simpl | by apply AFTERt1 | by rewrite leq_add2l; apply H_R_k_le_deadline ].
+                rewrite big_const_nat iter_addn mul1n addn0 leq_subLR.
+                by unfold D_i, D_k, t1, a_fst; rewrite -interference_bound_edf_j_fst_deadline
+                                                       -interference_bound_edf_j_i_deadline.
+              }    
+            Qed.
+
+          End ResponseTimeOfSingleJobBounded.
+
+          (* Else, if j_fst did not complete by its response-time bound, then
+             we need a separate proof. *)
+          Section ResponseTimeOfSingleJobNotBounded.
+
+            Hypothesis H_j_fst_not_complete_by_rt_bound :
+              ~~ completed job_cost sched j_fst (a_fst + R_k).
+
+            (* This trivially implies that a_fst + R_k lies after the end of the interval,
+               otherwise j_fst would have completed by its response-time bound. *)
+            Lemma interference_bound_edf_response_time_bound_of_j_fst_after_interval :
+              job_arrival j_fst + R_k >= job_arrival j_i + delta.
+            Proof.
+              have FST := interference_bound_edf_j_fst_is_job_of_tsk_k.
+              destruct FST as [FSTtask _].            
+              rewrite leqNgt; apply/negP; intro LT.
+              move: H_j_fst_not_complete_by_rt_bound => /negP BUG; apply BUG.
+              by apply H_all_previous_jobs_completed_on_time.
+            Qed.
+
+            (* If the slack is too big (D_i < D_k - R_k), j_fst causes no interference. *)
+            Lemma interference_bound_edf_holds_for_single_job_with_big_slack :
+              D_i < D_k - R_k ->
+              interference_caused_by j_fst t1 t2 = 0.
+            Proof.
+              intro LTdk.
+              rewrite ltn_subRL in LTdk.
+              rewrite -(ltn_add2l a_fst) addnA in LTdk.
+              apply leq_ltn_trans with (m := t1 + D_i) in LTdk; last first.
+              {
+                rewrite leq_add2r.
+                apply leq_trans with (n := t1 + delta); first by apply leq_addr.
+                by apply interference_bound_edf_response_time_bound_of_j_fst_after_interval.
+              }
+              apply/eqP; rewrite -[_ _ _ _ == 0]negbK; apply/negP; red; intro BUG.
+              apply interference_under_edf_implies_shorter_deadlines with
+                     (job_deadline0 := job_deadline) in BUG; try (by done).
+              rewrite interference_bound_edf_j_fst_deadline
+                      interference_bound_edf_j_i_deadline in BUG.
+              by apply (leq_trans LTdk) in BUG; rewrite ltnn in BUG.
+            Qed.
+
+            (* Else, if the slack is small, j_fst causes interference for no longer than
+               D_i - (D_k - R_k). *)
+            Lemma interference_bound_edf_holds_for_single_job_with_small_slack :
+              D_i >= D_k - R_k ->
+              interference_caused_by j_fst t1 t2 <= D_i - (D_k - R_k).
+            Proof.
+              intro LEdk.
+              have FST := interference_bound_edf_j_fst_is_job_of_tsk_k.
+              destruct FST as [FSTtask [LEdl _]].            
+              have LTr := interference_bound_edf_response_time_bound_of_j_fst_after_interval.
+              apply subh3; last by apply LEdk.
+              apply leq_trans with (n := job_interference job_cost job_task sched alpha j_i j_fst t1
+                                                          (job_arrival j_fst + R_k) + (D_k - R_k));
+                first by rewrite leq_add2r; apply extend_sum; [by apply leqnn|]. 
+              apply leq_trans with (n := \sum_(t1 <= t < a_fst + R_k) 1 +
+                                         \sum_(a_fst + R_k <= t < a_fst + D_k)1).
+              {
+                apply leq_add.
+                {
+                  rewrite big_const_nat iter_addn mul1n addn0.
+                  rewrite -{1}[job_arrival j_fst + R_k](addKn t1) -addnBA;
+                    first by apply job_interference_le_delta.
+                  by apply leq_trans with (n := t1 + delta); first by apply leq_addr.
+                }
+                rewrite big_const_nat iter_addn mul1n addn0 addnC.
+                rewrite -subnBA; last by apply leq_addr.
+                by rewrite addnC -addnBA // subnn addn0.
+              }
+              rewrite -big_cat_nat; simpl; last 2 first.
+              {
+                apply leq_trans with (n := t1 + delta); first by apply leq_addr.
+                by apply interference_bound_edf_response_time_bound_of_j_fst_after_interval.
+              }  
+              by rewrite leq_add2l; apply H_R_k_le_deadline.
+              rewrite big_const_nat iter_addn mul1n addn0 leq_subLR.
+              unfold D_i, D_k, t1, a_fst; rewrite -interference_bound_edf_j_fst_deadline
+                                                  -interference_bound_edf_j_i_deadline.
+              by apply interference_under_edf_implies_shorter_deadlines with
+                (job_deadline0 := job_deadline) in LEdl.
+            Qed.
+
+          End ResponseTimeOfSingleJobNotBounded.
+          
+          (* By combining the results above, we prove that the interference caused by the single job
+             is bounded by D_i - (D_k - R_k), ... *)
+          Lemma interference_bound_edf_interference_of_j_fst_limited_by_slack :
+            interference_caused_by j_fst t1 t2 <= D_i - (D_k - R_k).
+          Proof.
+            destruct (completed job_cost sched j_fst (a_fst + R_k)) eqn:COMP;
+              first by apply interference_bound_edf_holds_for_single_job_that_completes_on_time. 
+            apply negbT in COMP.
+            destruct (ltnP D_i (D_k - R_k)) as [LEdk | LTdk].
+              by rewrite interference_bound_edf_holds_for_single_job_with_big_slack.
+              by apply interference_bound_edf_holds_for_single_job_with_small_slack.
+          Qed.
+
+          (* ... and thus the interference bound holds. *)
+          Lemma interference_bound_edf_holds_for_a_single_job :
+            interference_caused_by j_fst t1 t2 <= interference_bound.
+          Proof.
+            have ONE := interference_bound_edf_simpl_when_there's_one_job.
+            have SLACK := interference_bound_edf_interference_of_j_fst_limited_by_slack.
+            rename H_many_jobs into NUM, H_only_one_job into SIZE.
+            unfold interference_caused_by, interference_bound, edf_specific_interference_bound.
+            fold D_i D_k p_k n_k.
+            rewrite SIZE ltnS leqn0 in NUM; move: NUM => /eqP EQnk.
+            rewrite EQnk mul0n add0n.
+            rewrite leq_min; apply/andP; split.
+            {
+              apply interference_bound_edf_interference_le_task_cost.
+              rewrite interference_bound_edf_job_in_same_sequence.
+              by apply mem_nth; rewrite SIZE.
+            }
+            by rewrite ONE; apply SLACK. 
+          Qed.
+
+        End InterferenceSingleJob.
+
+        (* Next, consider the other case where there are at least two jobs:
+           the first job j_fst, and the last job j_lst. *)
+        Section InterferenceTwoOrMoreJobs.
+
+          (* Assume there are at least two jobs. *)
+          Variable num_mid_jobs: nat.
+          Hypothesis H_at_least_two_jobs : size sorted_jobs = num_mid_jobs.+2.
+
+          (* Let j_lst be the last job of the sequence and a_lst its arrival time. *)
+          Let j_lst := nth elem sorted_jobs num_mid_jobs.+1.
+          Let a_lst := job_arrival j_lst.
+
+          (* In this section, we prove some basic lemmas about the first and last jobs. *)
+          Section FactsAboutFirstAndLastJobs.
+
+            (* The last job is an interfering job of task tsk_k. *)
+            Lemma interference_bound_edf_j_lst_is_job_of_tsk_k :
+              job_task j_lst = tsk_k /\
+              interference_caused_by j_lst t1 t2 != 0 /\
+              j_lst \in jobs_scheduled_between sched t1 t2.
+            Proof.
+              apply interference_bound_edf_all_jobs_from_tsk_k, mem_nth.
+              by rewrite H_at_least_two_jobs.
+            Qed.
+
+            (* The deadline of j_lst is the deadline of tsk_k. *)
+            Lemma interference_bound_edf_j_lst_deadline :
+              job_deadline j_lst = task_deadline tsk_k.
+            Proof.
+              unfold valid_sporadic_job in *.
+              rename H_valid_job_parameters into PARAMS.
+              have LST := interference_bound_edf_j_lst_is_job_of_tsk_k.
+              destruct LST as [LSTtask _].
+              by specialize (PARAMS j_lst); des; rewrite PARAMS1 LSTtask.
+            Qed.
+
+            (* The first job arrives before the last job. *)
+            Lemma interference_bound_edf_j_fst_before_j_lst :
+              job_arrival j_fst <= job_arrival j_lst.
+            Proof.
+              rename H_at_least_two_jobs into SIZE.
+              unfold j_fst, j_lst; rewrite -[num_mid_jobs.+1]add0n.
+              apply prev_le_next; last by rewrite SIZE leqnn.
+              by intros i LT; apply interference_bound_edf_jobs_ordered_by_arrival.
+            Qed.
+
+            (* The last job arrives before the end of the interval. *)
+            Lemma interference_bound_edf_last_job_arrives_before_end_of_interval :
+              job_arrival j_lst < t2.
+            Proof.
+              rewrite leqNgt; apply/negP; unfold not; intro LT2.
+              exploit interference_bound_edf_all_jobs_from_tsk_k.
+              {
+                apply mem_nth; instantiate (1 := num_mid_jobs.+1).
+                by rewrite -(ltn_add2r 1) addn1 H_at_least_two_jobs addn1.
+              }  
+              instantiate (1 := elem); move => [LSTtsk [/eqP LSTserv LSTin]].
+              apply LSTserv; apply/eqP; rewrite -leqn0.
+              apply leq_trans with (n := service_during sched j_lst t1 t2);
+                first by apply job_interference_le_service.
+              rewrite leqn0; apply/eqP; unfold service_during.
+              by apply cumulative_service_before_job_arrival_zero.
+            Qed.
+
+            (* Since there are multiple jobs, j_fst is far enough from the end of
+               the interval that its response-time bound is valid
+               (by the assumption H_all_previous_jobs_completed_on_time). *)
+            Lemma interference_bound_edf_j_fst_completed_on_time :
+              completed job_cost sched j_fst (a_fst + R_k).
+            Proof.
+              have FST := interference_bound_edf_j_fst_is_job_of_tsk_k; des.
+              set j_snd := nth elem sorted_jobs 1.
+              exploit interference_bound_edf_all_jobs_from_tsk_k.
+              {
+                by apply mem_nth; instantiate (1 := 1); rewrite H_at_least_two_jobs.
+              }
+              instantiate (1 := elem); move => [SNDtsk [/eqP SNDserv _]].
+              apply H_all_previous_jobs_completed_on_time; try (by done).
+              apply leq_ltn_trans with (n := job_arrival j_snd); last first.
+              {
+                rewrite ltnNge; apply/negP; red; intro BUG; apply SNDserv.
+                apply/eqP; rewrite -leqn0; apply leq_trans with (n := service_during
+                                                                          sched j_snd t1 t2);
+                  first by apply job_interference_le_service.
+                rewrite leqn0; apply/eqP.
+                by apply cumulative_service_before_job_arrival_zero.
+              }
+              apply leq_trans with (n := a_fst + p_k).
+              {
+                by rewrite leq_add2l; apply leq_trans with (n := D_k);
+                  [by apply H_R_k_le_deadline | by apply H_constrained_deadlines].
+              }
+            
+              (* Since jobs are sporadic, we know that the first job arrives
+                 at least p_k units before the second. *)
+              unfold p_k; rewrite -FST.
+              apply H_sporadic_tasks; [| by rewrite SNDtsk | ]; last first.
+              {
+                apply interference_bound_edf_jobs_ordered_by_arrival.
+                by rewrite H_at_least_two_jobs.
+              }
+              red; move => /eqP BUG.
+              by rewrite nth_uniq in BUG; rewrite ?SIZE //;
+                [ by apply interference_bound_edf_at_least_one_job
+                | by rewrite H_at_least_two_jobs
+                | by rewrite sort_uniq; apply filter_uniq, undup_uniq].
+            Qed.
+
+          End FactsAboutFirstAndLastJobs.
+
+          (* Next, we prove that the distance between the first and last jobs is at least
+             num_mid_jobs + 1 periods. *)
+          Lemma interference_bound_edf_many_periods_in_between :
+            a_lst - a_fst >= num_mid_jobs.+1 * p_k.
+          Proof.
+            unfold a_fst, a_lst, j_fst, j_lst. 
+            assert (EQnk: num_mid_jobs.+1=(size sorted_jobs).-1).
+              by rewrite H_at_least_two_jobs.
+            rewrite EQnk telescoping_sum;
+              last by ins; apply interference_bound_edf_jobs_ordered_by_arrival.
+            rewrite -[_ * _ tsk_k]addn0 mulnC -iter_addn -{1}[_.-1]subn0 -big_const_nat. 
+            rewrite big_nat_cond [\sum_(0 <= i < _)(_-_)]big_nat_cond.
+            apply leq_sum; intros i; rewrite andbT; move => /andP LT; des.
+
+            (* To simplify, call the jobs 'cur' and 'next' *)
+            set cur := nth elem sorted_jobs i.
+            set next := nth elem sorted_jobs i.+1.
+
+            (* Show that cur arrives earlier than next *)
+            assert (ARRle: job_arrival cur <= job_arrival next).
+              by unfold cur, next; apply interference_bound_edf_jobs_ordered_by_arrival.
+             
+            (* Show that both cur and next are in the arrival sequence *)
+            assert (INnth: cur \in interfering_jobs /\ next \in interfering_jobs).
+            {
+              rewrite 2!interference_bound_edf_job_in_same_sequence; split.
+                by apply mem_nth, (ltn_trans LT0); destruct sorted_jobs; ins.
+                by apply mem_nth; destruct sorted_jobs; ins.
+            }
+            rewrite 2?mem_filter in INnth; des.
+
+            (* Use the sporadic task model to conclude that cur and next are separated
+               by at least (task_period tsk) units. Of course this only holds if cur != next.
+               Since we don't know much about the list (except that it's sorted), we must
+               also prove that it doesn't contain duplicates. *)
+            assert (CUR_LE_NEXT: job_arrival cur + task_period (job_task cur) <= job_arrival next).
+            {
+              apply H_sporadic_tasks; last by ins.
+              unfold cur, next, not; intro EQ; move: EQ => /eqP EQ.
+              rewrite nth_uniq in EQ; first by move: EQ => /eqP EQ; intuition.
+                by apply ltn_trans with (n := (size sorted_jobs).-1); destruct sorted_jobs; ins.
+                by destruct sorted_jobs; ins.
+                by rewrite sort_uniq -/interfering_jobs filter_uniq // undup_uniq.  
+                by rewrite INnth INnth0.  
+            }
+            by rewrite subh3 // addnC /p_k -INnth.
+          Qed.
+
+          (* Using the lemma above, we prove that the ratio n_k is at least the number of
+             middle jobs + 1, ... *)
+          Lemma interference_bound_edf_n_k_covers_middle_jobs_plus_one :
+            n_k >= num_mid_jobs.+1.
+          Proof.
+            have DIST := interference_bound_edf_many_periods_in_between.
+            have AFTERt1 :=
+                interference_bound_edf_j_fst_completion_implies_rt_bound_inside_interval
+                interference_bound_edf_j_fst_completed_on_time.
+            rename H_valid_task_parameters into TASK_PARAMS,
+                   H_tsk_k_in_task_set into INk.
+            unfold valid_sporadic_taskset, is_valid_sporadic_task,
+                   interference_bound, edf_specific_interference_bound in *.
+            rewrite leqNgt; apply/negP; unfold not; intro LTnk; unfold n_k in LTnk.
+            rewrite ltn_divLR in LTnk; last by specialize (TASK_PARAMS tsk_k INk); des.
+            apply (leq_trans LTnk) in DIST; rewrite ltn_subRL in DIST.
+            rewrite -(ltn_add2r D_k) -addnA [D_i + _]addnC addnA in DIST. 
+            apply leq_ltn_trans with (m := job_arrival j_i + D_i) in DIST; last first.
+            {
+              rewrite leq_add2r; apply (leq_trans AFTERt1).
+              by rewrite leq_add2l; apply H_R_k_le_deadline.
+            }
+            have LST := interference_bound_edf_j_lst_is_job_of_tsk_k.
+            destruct LST as [_ [ LEdl _]].  
+            apply interference_under_edf_implies_shorter_deadlines with
+                (job_deadline0 := job_deadline) in LEdl; try (by done).
+            unfold D_i, D_k in DIST; rewrite interference_bound_edf_j_lst_deadline
+                                             interference_bound_edf_j_i_deadline in LEdl.
+            by rewrite ltnNge LEdl in DIST.
+          Qed.
+
+          (* ... which allows bounding the interference of the middle and last jobs
+             using n_k multiplied by the cost. *)
+          Lemma interference_bound_edf_holds_for_middle_and_last_jobs :
+            interference_caused_by j_lst t1 t2 +
+              \sum_(0 <= i < num_mid_jobs)
+                interference_caused_by (nth elem sorted_jobs i.+1) t1 t2
+            <= n_k * task_cost tsk_k.
+          Proof.
+            apply leq_trans with (n := num_mid_jobs.+1 * task_cost tsk_k); last first.
+            {
+              rewrite leq_mul2r; apply/orP; right.
+              by apply interference_bound_edf_n_k_covers_middle_jobs_plus_one.
+            }
+            rewrite mulSn; apply leq_add.
+            {
+              apply interference_bound_edf_interference_le_task_cost.
+              rewrite interference_bound_edf_job_in_same_sequence.
+              by apply mem_nth; rewrite H_at_least_two_jobs.
+            }
+            {
+              apply leq_trans with (n := \sum_(0 <= i < num_mid_jobs) task_cost tsk_k);
+                last by rewrite big_const_nat iter_addn addn0 mulnC subn0.
+              rewrite big_nat_cond [\sum_(0 <= i < num_mid_jobs) task_cost _]big_nat_cond.
+              apply leq_sum; intros i; rewrite andbT; move => /andP LT; des.
+              apply interference_bound_edf_interference_le_task_cost.
+              rewrite interference_bound_edf_job_in_same_sequence.
+              apply mem_nth; rewrite H_at_least_two_jobs.
+              by rewrite ltnS; apply leq_trans with (n := num_mid_jobs).
+            }
+          Qed.
+
+          (* Now, since n_k < sorted_jobs = num_mid_jobs + 2, it follows that
+             n_k = num_mid_jobs + 1. *)
+          Lemma interference_bound_edf_n_k_equals_num_mid_jobs_plus_one :
+            n_k = num_mid_jobs.+1.
+          Proof.
+            have NK := interference_bound_edf_n_k_covers_middle_jobs_plus_one.
+            rename H_many_jobs into NUM, H_at_least_two_jobs into SIZE.
+            by move: NK; rewrite leq_eqVlt orbC; move => /orP NK; des;
+             [by rewrite SIZE ltnS leqNgt NK in NUM | by rewrite NK].
+          Qed.
+          
+          (* After proving the bounds of the middle and last jobs, we do the same for
+             the first job. This requires a different proof in order to exploit the slack. *)
+          Section InterferenceOfFirstJob.
+
+            (* As required by the next lemma, in order to move (D_i %% p_k) to the left of
+               the inequality (<=), we must show that it is no smaller than the slack. *)
+            Lemma interference_bound_edf_remainder_ge_slack :
+              D_k - R_k <= D_i %% p_k.
+            Proof.
+              have AFTERt1 :=
+                interference_bound_edf_j_fst_completion_implies_rt_bound_inside_interval
+                interference_bound_edf_j_fst_completed_on_time.
+              have NK := interference_bound_edf_n_k_equals_num_mid_jobs_plus_one.
+              have DIST := interference_bound_edf_many_periods_in_between.
+              rewrite -NK in DIST.
+              rewrite -subndiv_eq_mod leq_subLR.
+              fold (div_floor D_i p_k) n_k.
+              rewrite addnBA; last by apply leq_trunc_div.
+              apply leq_trans with (n := R_k + D_i - (a_lst - a_fst)); last by apply leq_sub2l.
+              rewrite subnBA; last by apply interference_bound_edf_j_fst_before_j_lst.
+              rewrite -(leq_add2r a_lst) subh1; last first.
+              {
+                apply leq_trans with (n := t2);
+                  [by apply ltnW, interference_bound_edf_last_job_arrives_before_end_of_interval|].
+                rewrite addnC addnA.
+                apply leq_trans with (n := t1 + D_i).
+                  unfold t2; rewrite leq_add2l; apply H_delta_le_deadline.
+                by rewrite leq_add2r; apply AFTERt1.
+              }
+              rewrite -addnBA // subnn addn0 [D_k + _]addnC.
+              apply leq_trans with (n := t1 + D_i);
+                last by rewrite -addnA [D_i + _]addnC addnA leq_add2r addnC AFTERt1.
+              have LST := interference_bound_edf_j_lst_is_job_of_tsk_k.
+              destruct LST as [_ [ LSTserv _]].
+              unfold D_i, D_k, a_lst, t1; rewrite -interference_bound_edf_j_lst_deadline
+                                                  -interference_bound_edf_j_i_deadline.
+              by apply interference_under_edf_implies_shorter_deadlines with
+                                (job_deadline0 := job_deadline) in LSTserv.
+            Qed.
+
+            (* To conclude that the interference bound holds, it suffices to show that
+               this reordered inequality holds. *)
+            Lemma interference_bound_edf_simpl_by_moving_to_left_side :
+              interference_caused_by j_fst t1 t2 + (D_k - R_k) + D_i %/ p_k * p_k <= D_i ->
+              interference_caused_by j_fst t1 t2 <= D_i %% p_k - (D_k - R_k).
+            Proof.
+              intro LE.
+              apply subh3; last by apply interference_bound_edf_remainder_ge_slack.
+              by rewrite -subndiv_eq_mod; apply subh3; last by apply leq_trunc_div.
+            Qed.
+              
+            (* Next, we prove that interference caused by j_fst is bounded by the length
+               of the interval [t1, a_fst + R_k), ... *)
+            Lemma interference_bound_edf_interference_of_j_fst_bounded_by_response_time :
+               interference_caused_by j_fst t1 t2 <= \sum_(t1 <= t < a_fst + R_k) 1.
+            Proof.
+              assert (AFTERt1: t1 <= a_fst + R_k).
+              {
+                apply interference_bound_edf_j_fst_completion_implies_rt_bound_inside_interval.
+                by apply interference_bound_edf_j_fst_completed_on_time.
+              }
+              destruct (leqP t2 (a_fst + R_k)) as [LEt2 | GTt2].
+              {
+                apply leq_trans with (n := job_interference job_cost job_task sched alpha j_i j_fst t1
+                                                                              (a_fst + R_k));
+                  first by apply extend_sum; rewrite ?leqnn.
+                simpl_sum_const; rewrite -{1}[_ + R_k](addKn t1) -addnBA //.
+                by apply job_interference_le_delta.
+              }
+              {
+                unfold interference_caused_by, job_interference.
+                rewrite -> big_cat_nat with (n := a_fst + R_k);
+                  [simpl | by apply AFTERt1 | by apply ltnW].
+                rewrite -[\sum_(_ <= _ < _) 1]addn0; apply leq_add.
+                {
+                  simpl_sum_const; rewrite -{1}[_ + R_k](addKn t1) -addnBA //.
+                  by apply job_interference_le_delta.
+                }
+                apply leq_trans with (n := service_during sched j_fst (a_fst + R_k) t2);
+                  first by apply job_interference_le_service.
+                rewrite leqn0; apply/eqP.
+                apply cumulative_service_after_job_rt_zero with (job_cost0 := job_cost) (R := R_k);
+                  [ by done | | by apply leqnn].
+                by apply interference_bound_edf_j_fst_completed_on_time.
+              }
+            Qed.
+
+            (* ..., which leads to the following bounds based on interval lengths. *)
+            Lemma interference_bound_edf_bounding_interference_with_interval_lengths :
+              interference_caused_by j_fst t1 t2 + (D_k - R_k) + D_i %/ p_k * p_k <=
+              \sum_(t1 <= t < a_fst + R_k) 1
+              + \sum_(a_fst + R_k <= t < a_fst + D_k) 1
+              + \sum_(a_fst + D_k <= t < a_lst + D_k) 1.
+            Proof.
+              apply leq_trans with (n := \sum_(t1 <= t < a_fst + R_k) 1 + (D_k - R_k) +
+                                                                       D_i %/ p_k * p_k).
+              {
+                rewrite 2!leq_add2r.
+                apply interference_bound_edf_interference_of_j_fst_bounded_by_response_time.
+              }
+              apply leq_trans with (n := \sum_(t1 <= t < a_fst + R_k) 1 + (D_k - R_k) +
+                                                                        (a_lst - a_fst)).
+              {
+                rewrite leq_add2l; fold (div_floor D_i p_k) n_k.
+                rewrite interference_bound_edf_n_k_equals_num_mid_jobs_plus_one.
+                by apply interference_bound_edf_many_periods_in_between.
+              }
+              apply leq_trans with (n := \sum_(t1 <= t < a_fst + R_k) 1 +
+                  \sum_(a_fst + R_k <= t < a_fst + D_k) 1 + \sum_(a_fst + D_k <= t < a_lst + D_k) 1).
+              {
+                by rewrite -2!addnA leq_add2l; apply leq_add;
+                rewrite big_const_nat iter_addn mul1n addn0;
+                rewrite ?subnDl ?subnDr leqnn.
+              }
+              by apply leqnn.
+            Qed.
+
+            (* To conclude, we show that the concatenation of these interval lengths equals
+               (a_lst + D_k) - 1, ... *)
+            Lemma interference_bound_edf_simpl_by_concatenation_of_intervals :
+              \sum_(t1 <= t < a_fst + R_k) 1
+              + \sum_(a_fst + R_k <= t < a_fst + D_k) 1
+              + \sum_(a_fst + D_k <= t < a_lst + D_k) 1 = (a_lst + D_k) - t1.
+            Proof.
+              assert (AFTERt1: t1 <= a_fst + R_k).
+              {
+                apply interference_bound_edf_j_fst_completion_implies_rt_bound_inside_interval.
+                by apply interference_bound_edf_j_fst_completed_on_time.
+              }
+              rewrite -big_cat_nat;
+                [simpl | by apply AFTERt1 | by rewrite leq_add2l; apply H_R_k_le_deadline].
+              rewrite -big_cat_nat; simpl; last 2 first.
+              {
+                apply leq_trans with (n := a_fst + R_k); first by apply AFTERt1.
+                by rewrite leq_add2l; apply H_R_k_le_deadline.
+              }
+              {
+                rewrite leq_add2r; unfold a_fst, a_lst, j_fst, j_lst.
+                rewrite -[num_mid_jobs.+1]add0n; apply prev_le_next;
+                  last by rewrite add0n H_at_least_two_jobs ltnSn.
+                by ins; apply interference_bound_edf_jobs_ordered_by_arrival.
+              }
+              by rewrite big_const_nat iter_addn mul1n addn0.
+            Qed.
+            
+            (* ... which results in proving that (a_lst + D_k) - t1 <= D_i.
+               This holds because high-priority jobs have earlier deadlines. Therefore,
+               the interference caused by the first job is bounded by D_i %% p_k - (D_k - R_k). *)
+            Lemma interference_bound_edf_interference_of_j_fst_limited_by_remainder_and_slack :
+              interference_caused_by j_fst t1 t2 <= D_i %% p_k - (D_k - R_k).
+            Proof.
+              apply interference_bound_edf_simpl_by_moving_to_left_side.
+              apply (leq_trans interference_bound_edf_bounding_interference_with_interval_lengths).
+              rewrite interference_bound_edf_simpl_by_concatenation_of_intervals leq_subLR.
+              have LST := interference_bound_edf_j_lst_is_job_of_tsk_k.
+              destruct LST as [_ [ LSTserv _]].
+              unfold D_i, D_k, a_lst, t1; rewrite -interference_bound_edf_j_lst_deadline
+                                                  -interference_bound_edf_j_i_deadline.
+              by apply interference_under_edf_implies_shorter_deadlines
+                with (job_deadline0 := job_deadline) in LSTserv.
+            Qed.
+
+          End InterferenceOfFirstJob.
+
+          (* Using the lemmas above we show that the interference bound works in the
+             case of two or more jobs. *)
+          Lemma interference_bound_edf_holds_for_multiple_jobs :
+            \sum_(0 <= i < num_mid_jobs.+2)
+              interference_caused_by (nth elem sorted_jobs i) t1 t2 <= interference_bound.
+          Proof.
+            (* Knowing that we have at least two elements, we take first and last out of the sum *) 
+            rewrite [nth]lock big_nat_recl // big_nat_recr // /= -lock.
+            rewrite addnA addnC addnA.
+
+            have NK := interference_bound_edf_n_k_equals_num_mid_jobs_plus_one. 
+
+            (* We use the lemmas we proved to show that the interference bound holds. *)
+            unfold interference_bound, edf_specific_interference_bound.
+            fold D_i D_k p_k n_k.
+            rewrite addnC addnA; apply leq_add;
+              first by rewrite addnC interference_bound_edf_holds_for_middle_and_last_jobs.
+            rewrite leq_min; apply/andP; split.
+            {
+              apply interference_bound_edf_interference_le_task_cost.
+              rewrite interference_bound_edf_job_in_same_sequence.
+              by apply mem_nth; rewrite H_at_least_two_jobs.
+            }
+            by apply interference_bound_edf_interference_of_j_fst_limited_by_remainder_and_slack.
+          Qed.
+          
+        End InterferenceTwoOrMoreJobs.
+
+      End InterferenceManyJobs.
+      
+      Theorem interference_bound_edf_bounds_interference :
+        x <= interference_bound.
+      Proof.
+        (* Use the definition of workload based on list of jobs. *)
+        apply (leq_trans interference_bound_edf_use_another_definition). 
+
+        (* We only care about the jobs that cause interference. *)
+        rewrite interference_bound_edf_simpl_by_filtering_interfering_jobs.
+
+        (* Now we order the list by job arrival time. *)
+        rewrite interference_bound_edf_simpl_by_sorting_interfering_jobs.
+
+        (* Next, we show that the workload bound holds if n_k
+           is no larger than the number of interferings jobs. *)
+        destruct (size sorted_jobs <= n_k) eqn:NUM;
+          first by apply interference_bound_edf_holds_for_at_most_n_k_jobs.
+        apply negbT in NUM; rewrite -ltnNge in NUM.
+
+        (* Find some dummy element to use in the nth function *)
+        assert (EX: exists elem: JobIn arr_seq, True).
+          destruct sorted_jobs as [| j]; [by rewrite ltn0 in NUM | by exists j].
+        destruct EX as [elem _].
+
+        (* Now we index the sum to access the first and last elements. *)
+        rewrite (big_nth elem).
+
+        (* First, we show that the bound holds for an empty list of jobs. *)
+        destruct (size sorted_jobs) as [| n] eqn:SIZE;
+          first by rewrite big_geq.
+
+        (* Then, we show the same for a single job, or for multiple jobs. *)
+        rewrite SIZE; destruct n as [| num_mid_jobs].
+        {
+          rewrite big_nat_recr // big_geq //.
+          rewrite [nth]lock /= -lock add0n.
+          by apply interference_bound_edf_holds_for_a_single_job; rewrite SIZE.
+        }
+        {
+          by apply interference_bound_edf_holds_for_multiple_jobs; first by rewrite SIZE.
+        }
+      Qed.
+      
+    End MainProof.
+    
+  End ProofSpecificBound.
+
+  (* As required by the proof of convergence of EDF RTA, we show that the
+     EDF-specific bound is monotonically increasing with both the size
+     of the interval and the value of the previous response-time bounds. *)
+  Section MonotonicitySpecificBound.
+
+    Context {sporadic_task: eqType}.
+    Variable task_cost: sporadic_task -> time.
+    Variable task_period: sporadic_task -> time.
+    Variable task_deadline: sporadic_task -> time.
+    
+    Variable tsk tsk_other: sporadic_task.
+    Hypothesis H_period_positive: task_period tsk_other > 0.
+
+    Variable delta delta' R R': time.
+    Hypothesis H_delta_monotonic: delta <= delta'.
+    Hypothesis H_response_time_monotonic: R <= R'.
+    Hypothesis H_cost_le_rt_bound: task_cost tsk_other <= R.
+
+    Lemma interference_bound_edf_monotonic :
+      interference_bound_edf task_cost task_period task_deadline tsk delta (tsk_other, R) <=
+      interference_bound_edf task_cost task_period task_deadline tsk delta' (tsk_other, R').
+    Proof.
+      rename H_response_time_monotonic into LEr, H_delta_monotonic into LEx,
+             H_cost_le_rt_bound into LEcost, H_period_positive into GEperiod.
+      unfold interference_bound_edf, interference_bound_generic.
+      rewrite leq_min; apply/andP; split.
+      {
+        rewrite leq_min; apply/andP; split.
+        apply leq_trans with (n :=  (minn (W task_cost task_period (fst (tsk_other, R))
+                           (snd (tsk_other, R)) delta) (delta - task_cost tsk + 1)));
+          first by apply geq_minl.
+        apply leq_trans with (n := W task_cost task_period (fst (tsk_other, R))
+                                                   (snd (tsk_other, R)) delta);
+          [by apply geq_minl | by apply W_monotonic].
+        apply leq_trans with (n := minn (W task_cost task_period (fst (tsk_other, R)) (snd (tsk_other, R)) delta) (delta - task_cost tsk + 1));
+          first by apply geq_minl.
+        apply leq_trans with (n := delta - task_cost tsk + 1);
+          first by apply geq_minr.
+        by rewrite leq_add2r leq_sub2r.
+      }
+      {
+        apply leq_trans with (n := edf_specific_interference_bound task_cost task_period
+                                                          task_deadline tsk tsk_other R);
+          first by apply geq_minr.
+        unfold edf_specific_interference_bound; simpl.
+        rewrite leq_add2l leq_min; apply/andP; split; first by apply geq_minl.
+        apply leq_trans with (n := task_deadline tsk %% task_period tsk_other -
+                                   (task_deadline tsk_other - R));
+          [by apply geq_minr | by rewrite 2?leq_sub2l 2?leq_sub2r // leq_sub2l].
+      }
+    Qed.
+
+  End MonotonicitySpecificBound.
+
+End InterferenceBoundEDF.
\ No newline at end of file
diff --git a/analysis/apa/interference_bound_fp.v b/analysis/apa/interference_bound_fp.v
new file mode 100644
index 0000000000000000000000000000000000000000..54230846186d9fd11aaf14029edb2501f362bea9
--- /dev/null
+++ b/analysis/apa/interference_bound_fp.v
@@ -0,0 +1,54 @@
+Require Import rt.util.all.
+Require Import rt.model.apa.schedule rt.model.apa.priority rt.model.apa.workload
+               rt.model.apa.interference rt.model.apa.affinity.
+Require Import rt.analysis.apa.workload_bound rt.analysis.apa.interference_bound.
+From mathcomp Require Import ssreflect ssrbool eqtype ssrnat seq fintype bigop.
+
+Module InterferenceBoundFP.
+
+  Import Schedule WorkloadBound Priority Interference Affinity.
+  Export InterferenceBoundGeneric.
+
+    Section Definitions.
+    
+    Context {sporadic_task: eqType}.
+    Variable task_cost: sporadic_task -> time.
+    Variable task_period: sporadic_task -> time.
+    Variable task_deadline: sporadic_task -> time.
+    
+    (* Assume that each task has a processor affinity alpha. *)
+    Context {num_cpus: nat}.
+    Variable alpha: task_affinity sporadic_task num_cpus.
+
+    (* Let tsk be the task to be analyzed ... *)
+    Variable tsk: sporadic_task.
+
+    (* ... under subaffinity alpha'. *)
+    Variable alpha': affinity num_cpus.
+
+    Let task_with_response_time := (sporadic_task * time)%type.
+    
+    (* Assume a known response-time bound for each interfering task ... *)
+    Variable R_prev: seq task_with_response_time.
+
+    (* ... and an interval length delta. *)
+    Variable delta: time.
+      
+    (* Assume an FP policy. *)
+    Variable higher_eq_priority: FP_policy sporadic_task.
+
+    (* Recall the generic interference bound. *)
+    Let total_interference_bound := interference_bound_generic task_cost task_period tsk delta.
+
+    (* Let (hp_task_in alpha') denote the higher-priority tasks that can execute on alpha'. *)
+    Let hp_task_in alpha' := higher_priority_task_in alpha higher_eq_priority tsk alpha'.
+    
+    (* The total interference incurred by tsk is bounded by the sum
+       of individual task interferences of tasks in (hp_task_in alpha'). *)
+    Definition total_interference_bound_fp :=
+      \sum_((tsk_other, R_other) <- R_prev | hp_task_in alpha' tsk_other)
+         total_interference_bound (tsk_other, R_other).
+      
+  End Definitions.
+
+End InterferenceBoundFP.
\ No newline at end of file
diff --git a/analysis/apa/workload_bound.v b/analysis/apa/workload_bound.v
new file mode 100644
index 0000000000000000000000000000000000000000..ce0b848479f04163405b6e5751e1c1f922ac86d2
--- /dev/null
+++ b/analysis/apa/workload_bound.v
@@ -0,0 +1,729 @@
+Require Import rt.util.all rt.util.divround.
+Require Import rt.model.apa.task rt.model.apa.job rt.model.apa.schedule
+               rt.model.apa.task_arrival rt.model.apa.response_time
+               rt.model.apa.workload rt.model.apa.schedulability.
+From mathcomp Require Import ssreflect ssrbool eqtype ssrnat seq div fintype bigop path.
+
+Module WorkloadBound.
+  
+  Import Job SporadicTaskset Schedule ScheduleOfSporadicTask SporadicTaskArrival ResponseTime Schedulability Workload.
+
+  Section WorkloadBoundDef.
+
+    Context {sporadic_task: eqType}.
+    Variable task_cost: sporadic_task -> time.
+    Variable task_period: sporadic_task -> time.
+
+    (* Consider any task tsk with response-time bound R_tsk,
+       that is scheduled in an interval of length delta. *)
+    Variable tsk: sporadic_task.
+    Variable R_tsk: time.
+    Variable delta: time.
+    
+    (* Based on the number of jobs that execute completely in the interval, ... *)
+    Definition max_jobs :=
+      div_floor (delta + R_tsk - task_cost tsk) (task_period tsk).
+
+    (* ... Bertogna and Cirinei's workload bound is defined as follows. *)
+    Definition W :=
+      let e_k := (task_cost tsk) in
+      let p_k := (task_period tsk) in            
+        minn e_k (delta + R_tsk - e_k - max_jobs * p_k) + max_jobs * e_k.
+
+  End WorkloadBoundDef.
+  
+  Section BasicLemmas.
+
+    Context {sporadic_task: eqType}.
+    Variable task_cost: sporadic_task -> time.
+    Variable task_period: sporadic_task -> time.
+
+    (* Let tsk be any task...*)
+    Variable tsk: sporadic_task.
+
+    (* ... with period > 0. *)
+    Hypothesis H_period_positive: task_period tsk > 0.
+
+    (* Let R1 <= R2 be two response-time bounds that
+       are larger than the cost of the tsk. *)
+    Variable R1 R2: time.
+    Hypothesis H_R_lower_bound: R1 >= task_cost tsk.
+    Hypothesis H_R1_le_R2: R1 <= R2.
+      
+    Let workload_bound := W task_cost task_period tsk.
+
+    (* Then, Bertogna and Cirinei's workload bound is monotonically increasing. *) 
+    Lemma W_monotonic :
+      forall t1 t2,
+        t1 <= t2 ->
+        workload_bound R1 t1 <= workload_bound R2 t2.
+    Proof.
+      intros t1 t2 LEt.
+      unfold workload_bound, W, max_jobs, div_floor; rewrite 2!subndiv_eq_mod.
+      set e := task_cost tsk; set p := task_period tsk.
+      set x1 := t1 + R1.
+      set x2 := t2 + R2.
+      set delta := x2 - x1.
+      rewrite -[x2](addKn x1) -addnBA; fold delta;
+        last by apply leq_add.
+      
+      induction delta; first by rewrite addn0 leqnn.
+      {
+         apply (leq_trans IHdelta).
+
+         (* Prove special case for p <= 1. *)
+         destruct (leqP p 1) as [LTp | GTp].
+         {
+           rewrite leq_eqVlt in LTp; move: LTp => /orP LTp; des;
+             last by rewrite ltnS in LTp; apply (leq_trans H_period_positive) in LTp. 
+           {
+             rewrite LTp 2!modn1 2!divn1.
+             rewrite leq_add2l leq_mul2r; apply/orP; right.
+             by rewrite leq_sub2r // leq_add2l.
+           }
+         }
+         (* Harder case: p > 1. *)
+         {
+           assert (EQ: (x1 + delta.+1 - e) = (x1 + delta - e).+1).
+           {
+             rewrite -[(x1 + delta - e).+1]addn1.
+             rewrite [_+1]addnC addnBA; last first.
+             {
+               apply (leq_trans H_R_lower_bound).
+               by rewrite -addnA addnC -addnA leq_addr.
+             }
+             by rewrite [1 + _]addnC -addnA addn1.
+           } rewrite -> EQ in *; clear EQ.
+         
+         have DIV := divSn_cases (x1 + delta - e) p GTp; des.
+         {
+           rewrite DIV leq_add2r leq_min; apply/andP; split;
+             first by rewrite geq_minl.
+           by apply leq_trans with (n := (x1 + delta - e) %% p);
+             [by rewrite geq_minr | by rewrite -DIV0 addn1 leqnSn].
+         }
+         {
+           rewrite -[minn e _]add0n -addnA; apply leq_add; first by ins.
+           rewrite -DIV mulnDl mul1n [_ + e]addnC.
+           by apply leq_add; [by rewrite geq_minl | by ins].
+         }
+       }
+     }
+   Qed.
+
+  End BasicLemmas.
+ 
+  Section ProofWorkloadBound.
+ 
+    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_cost: Job -> time.
+    Variable job_task: Job -> sporadic_task.
+    Variable job_deadline: Job -> time.
+
+    Variable arr_seq: arrival_sequence Job.
+
+    (* Assume that all jobs have valid parameters *)
+    Hypothesis H_jobs_have_valid_parameters :
+      forall (j: JobIn arr_seq),
+        valid_sporadic_job task_cost task_deadline job_cost job_deadline job_task j.
+    
+    (* Consider any schedule. *)
+    Context {num_cpus: nat}.
+    Variable sched: schedule num_cpus arr_seq.
+
+    (* Assumption: jobs only execute if they arrived.
+       This is used to eliminate jobs that arrive after end of the interval t1 + delta. *)
+    Hypothesis H_jobs_must_arrive_to_execute:
+      jobs_must_arrive_to_execute sched.
+
+    (* Assumption: jobs do not execute after they completed.
+       This is used to eliminate jobs that complete before the start of the interval t1. *)
+    Hypothesis H_completed_jobs_dont_execute:
+      completed_jobs_dont_execute job_cost sched.
+
+    (* Assumption: Jobs are sequential.
+       This is required to use interval lengths as a measure of service. *)
+    Hypothesis H_sequential_jobs: sequential_jobs sched.
+
+    (* Assumption: sporadic task model.
+       This is necessary to conclude that consecutive jobs ordered by arrival times
+       are separated by at least 'period' times units. *)
+    Hypothesis H_sporadic_tasks: sporadic_task_model task_period arr_seq job_task.
+
+    (* Before starting the proof, let's give simpler names to the definitions. *)
+    Let job_has_completed_by := completed job_cost sched.
+
+    Let workload_of (tsk: sporadic_task) (t1 t2: time) :=
+      workload job_task sched tsk t1 t2.
+
+    (* Now we define the theorem. Let tsk be any task in the taskset. *)
+    Variable tsk: sporadic_task.
+
+    (* Assumption: the task must have valid parameters:
+         a) period > 0 (used in divisions)
+         b) deadline of the jobs = deadline of the task
+         c) cost <= period
+            (used to prove that the distance between the first and last
+             jobs is at least (cost + n*period), where n is the number
+             of middle jobs. If cost >> period, the claim does not hold
+             for every task set. *)
+    Hypothesis H_valid_task_parameters:
+      is_valid_sporadic_task task_cost task_period task_deadline tsk.
+
+    (* Assumption: the task must have a constrained deadline.
+       This is required to prove that n_k (max_jobs) from Bertogna
+       and Cirinei's formula accounts for at least the number of
+       middle jobs (i.e., number of jobs - 2 in the worst case). *)
+    Hypothesis H_constrained_deadline: task_deadline tsk <= task_period tsk.
+      
+    (* Consider an interval [t1, t1 + delta). *)
+    Variable t1 delta: time.
+
+    (* Assume that a response-time bound R_tsk for that task in any
+       schedule of this processor platform is also given, ... *)
+    Variable R_tsk: time.
+
+    Hypothesis H_response_time_bound :    
+      forall (j: JobIn arr_seq),
+      job_task j = tsk ->
+      job_arrival j + R_tsk < t1 + delta ->
+      job_has_completed_by j (job_arrival j + R_tsk).
+
+    (* ... such that R_tsk >= task_cost tsk and R_tsk <= task_deadline tsk. *)    
+    Hypothesis H_response_time_ge_cost: R_tsk >= task_cost tsk.
+    Hypothesis H_no_deadline_miss: R_tsk <= task_deadline tsk.
+    
+    Section MainProof.
+
+      (* In this section, we prove that the workload of a task in the
+         interval [t1, t1 + delta) is bounded by W. *)
+
+      (* Let's simplify the names a bit. *)
+      Let t2 := t1 + delta.
+      Let n_k := max_jobs task_cost task_period tsk R_tsk delta.
+      Let workload_bound := W task_cost task_period tsk R_tsk delta.
+      
+      (* Since we only care about the workload of tsk, we restrict
+         our view to the set of jobs of tsk scheduled in [t1, t2). *)
+      Let scheduled_jobs :=
+        jobs_of_task_scheduled_between job_task sched tsk t1 t2.
+
+      (* Now, let's consider the list of interfering jobs sorted by arrival time. *)
+      Let earlier_arrival := fun (x y: JobIn arr_seq) => job_arrival x <= job_arrival y.
+      Let sorted_jobs := (sort earlier_arrival scheduled_jobs).
+
+      (* The first step consists in simplifying the sum corresponding
+         to the workload. *)
+      Section SimplifyJobSequence.
+
+        (* After switching to the definition of workload based on a list
+           of jobs, we show that sorting the list preserves the sum. *)
+        Lemma workload_bound_simpl_by_sorting_scheduled_jobs :
+          workload_joblist job_task sched tsk t1 t2 =
+           \sum_(i <- sorted_jobs) service_during sched i t1 t2.
+        Proof.
+          unfold workload_joblist; fold scheduled_jobs.
+          rewrite (eq_big_perm sorted_jobs) /= //.
+          by rewrite -(perm_sort earlier_arrival).
+        Qed.
+
+        (* Remember that both sequences have the same set of elements *)
+        Lemma workload_bound_job_in_same_sequence :
+          forall j,
+            (j \in scheduled_jobs) = (j \in sorted_jobs).
+        Proof.
+          by apply perm_eq_mem; rewrite -(perm_sort earlier_arrival).
+        Qed.
+
+        (* Remember that all jobs in the sorted sequence is an
+           interfering job of task tsk. *)
+        Lemma workload_bound_all_jobs_from_tsk :
+          forall j_i,
+            j_i \in sorted_jobs ->
+            job_task j_i = tsk /\
+            service_during sched j_i t1 t2 != 0 /\
+            j_i \in jobs_scheduled_between sched t1 t2.
+        Proof.
+          intros j_i LTi.
+          rewrite -workload_bound_job_in_same_sequence mem_filter in LTi; des.
+          repeat split; [by done | | by done].
+          unfold jobs_scheduled_between in *; rewrite mem_undup in LTi0.
+          apply mem_bigcat_nat_exists in LTi0; des.
+          rewrite mem_scheduled_jobs_eq_scheduled in LTi0.
+          apply service_implies_cumulative_service with (t := i);
+            first by apply/andP; split.
+          by rewrite -not_scheduled_no_service negbK.
+        Qed.
+
+        (* Remember that consecutive jobs are ordered by arrival. *)
+        Lemma workload_bound_jobs_ordered_by_arrival :
+          forall i elem,
+            i < (size sorted_jobs).-1 ->
+            earlier_arrival (nth elem sorted_jobs i) (nth elem sorted_jobs i.+1).
+        Proof.
+          intros i elem LT.
+          assert (SORT: sorted earlier_arrival sorted_jobs).
+            by apply sort_sorted; unfold total, earlier_arrival; ins; apply leq_total.
+          by destruct sorted_jobs; simpl in *; [by rewrite ltn0 in LT | by apply/pathP].
+        Qed.
+
+      End SimplifyJobSequence.
+
+      (* Next, we show that if the number of jobs is no larger than n_k,
+         the workload bound trivially holds. *)
+      Section WorkloadNotManyJobs.
+
+        Lemma workload_bound_holds_for_at_most_n_k_jobs :
+          size sorted_jobs <= n_k ->
+          \sum_(i <- sorted_jobs) service_during sched i t1 t2 <=
+            workload_bound.
+        Proof.
+        intros LEnk.
+        rewrite -[\sum_(_ <- _ | _) _]add0n leq_add //.
+        apply leq_trans with (n := \sum_(x <- sorted_jobs) task_cost tsk);
+          last by rewrite big_const_seq iter_addn addn0 mulnC leq_mul2r; apply/orP; right.
+        {
+          rewrite [\sum_(_ <- _) service_during _ _ _ _]big_seq_cond.
+          rewrite [\sum_(_ <- _) task_cost _]big_seq_cond.
+          apply leq_sum; intros j_i; move/andP => [INi _].
+          apply workload_bound_all_jobs_from_tsk in INi; des. 
+          eapply cumulative_service_le_task_cost;
+            [by apply H_completed_jobs_dont_execute | by apply INi |].
+          by apply H_jobs_have_valid_parameters.
+        }
+      Qed.
+
+      End WorkloadNotManyJobs.
+
+      (* Otherwise, assume that the number of jobs is larger than n_k >= 0.
+         First, consider the simple case with only one job. *)
+      Section WorkloadSingleJob.
+
+        (* Assume that there's at least one job in the sorted list. *)
+        Hypothesis H_at_least_one_job: size sorted_jobs > 0.
+
+        Variable elem: JobIn arr_seq.
+        Let j_fst := nth elem sorted_jobs 0.
+
+        (* The first job is an interfering job of task tsk. *)
+        Lemma workload_bound_j_fst_is_job_of_tsk :
+          job_task j_fst = tsk /\
+          service_during sched j_fst t1 t2 != 0 /\
+          j_fst \in jobs_scheduled_between sched t1 t2.
+        Proof.
+          by apply workload_bound_all_jobs_from_tsk, mem_nth.
+        Qed.
+
+        (* The workload bound holds for the single job. *)
+        Lemma workload_bound_holds_for_a_single_job :
+          \sum_(0 <= i < 1) service_during sched (nth elem sorted_jobs i) t1 t2 <=
+          workload_bound.
+        Proof.
+          unfold workload_bound, W; fold n_k.
+          have INfst := workload_bound_j_fst_is_job_of_tsk; des.
+          rewrite big_nat_recr // big_geq // [nth]lock /= -lock add0n.
+          destruct n_k; last first.
+          {
+            rewrite -[service_during _ _ _ _]add0n; rewrite leq_add //.
+            rewrite -[service_during _ _ _ _]add0n [_* task_cost tsk]mulSnr.
+            apply leq_add; first by done.
+            by eapply cumulative_service_le_task_cost;
+              [| by apply INfst
+               | by apply H_jobs_have_valid_parameters].
+          }
+          {
+            rewrite 2!mul0n addn0 subn0 leq_min; apply/andP; split.
+            {
+              by eapply cumulative_service_le_task_cost;
+                 [| by apply INfst
+                | by apply H_jobs_have_valid_parameters].
+            }
+            {
+              rewrite -addnBA // -[service_during _ _ _ _]addn0.
+              apply leq_add; last by done.
+              by apply cumulative_service_le_delta.
+            }
+          }
+        Qed.
+
+      End WorkloadSingleJob.
+
+      (* Next, consider the last case where there are at least two jobs:
+         the first job j_fst, and the last job j_lst. *)
+      Section WorkloadTwoOrMoreJobs.
+
+        (* There are at least two jobs. *)
+        Variable num_mid_jobs: nat.
+        Hypothesis H_at_least_two_jobs : size sorted_jobs = num_mid_jobs.+2.
+        
+        Variable elem: JobIn arr_seq.
+        Let j_fst := nth elem sorted_jobs 0.
+        Let j_lst := nth elem sorted_jobs num_mid_jobs.+1.
+
+        (* The last job is an interfering job of task tsk. *)
+        Lemma workload_bound_j_lst_is_job_of_tsk :
+          job_task j_lst = tsk /\
+          service_during sched j_lst t1 t2 != 0 /\
+          j_lst \in jobs_scheduled_between sched t1 t2.
+        Proof.
+          apply workload_bound_all_jobs_from_tsk, mem_nth.
+          by rewrite H_at_least_two_jobs.
+        Qed.
+
+        (* The response time of the first job must fall inside the interval. *)
+        Lemma workload_bound_response_time_of_first_job_inside_interval :
+          t1 <= job_arrival j_fst + R_tsk.
+        Proof.
+          rewrite leqNgt; apply /negP; unfold not; intro LTt1.
+          exploit workload_bound_all_jobs_from_tsk.
+          {
+            apply mem_nth; instantiate (1 := 0).
+            apply ltn_trans with (n := 1); [by done | by rewrite H_at_least_two_jobs].
+          }
+          instantiate (1 := elem); move => [FSTtsk [/eqP FSTserv FSTin]].
+          apply FSTserv.
+          apply (cumulative_service_after_job_rt_zero job_cost) with (R := R_tsk);
+            try (by done); last by apply ltnW.
+          apply H_response_time_bound; first by done.
+          by apply leq_trans with (n := t1); last by apply leq_addr.
+        Qed.
+
+        (* The arrival of the last job must also fall inside the interval. *)
+        Lemma workload_bound_last_job_arrives_before_end_of_interval :
+          job_arrival j_lst < t2.
+        Proof.
+          rewrite leqNgt; apply/negP; unfold not; intro LT2.
+          exploit workload_bound_all_jobs_from_tsk.
+          {
+            apply mem_nth; instantiate (1 := num_mid_jobs.+1).
+            by rewrite -(ltn_add2r 1) addn1 H_at_least_two_jobs addn1.
+          }  
+          instantiate (1 := elem); move => [LSTtsk [/eqP LSTserv LSTin]].
+          by unfold service_during; apply LSTserv, cumulative_service_before_job_arrival_zero.
+        Qed.
+
+        (* Next, we upper-bound the service of the first and last jobs using their arrival times. *)
+        Lemma workload_bound_service_of_first_and_last_jobs :
+          service_during sched j_fst t1 t2 +
+          service_during sched j_lst t1 t2 <=
+            (job_arrival j_fst  + R_tsk - t1) + (t2 - job_arrival j_lst).
+        Proof.
+          apply leq_add; unfold service_during.
+          {
+            rewrite -[_ + _ - _]mul1n -[1*_]addn0 -iter_addn -big_const_nat.
+            apply leq_trans with (n := \sum_(t1 <= t < job_arrival j_fst + R_tsk)
+                                        service_at sched j_fst t);
+              last by apply leq_sum; ins; apply service_at_most_one.
+            destruct (job_arrival j_fst + R_tsk < t2) eqn:LEt2; last first.
+            {
+              unfold t2; apply negbT in LEt2; rewrite -ltnNge in LEt2.
+              rewrite -> big_cat_nat with (n := t1 + delta) (p := job_arrival j_fst + R_tsk);
+                [by apply leq_addr | by apply leq_addr | by done].
+            }
+            {
+              rewrite -> big_cat_nat with (n := job_arrival j_fst + R_tsk);
+                [| by apply workload_bound_response_time_of_first_job_inside_interval
+                 | by apply ltnW].
+              rewrite -{2}[\sum_(_ <= _ < _) _]addn0 /= leq_add2l leqn0; apply/eqP.
+              apply (cumulative_service_after_job_rt_zero job_cost) with (R := R_tsk); try (by done).
+              apply H_response_time_bound; last by done.
+              exploit workload_bound_all_jobs_from_tsk.
+                by apply mem_nth; instantiate (1 := 0); rewrite H_at_least_two_jobs.
+              by instantiate (1 := elem); move => [FSTtsk _].
+            }
+          }
+          {
+            rewrite -[_ - _]mul1n -[1 * _]addn0 -iter_addn -big_const_nat.
+            destruct (job_arrival j_lst <= t1) eqn:LT.
+            {
+              apply leq_trans with (n := \sum_(job_arrival j_lst <= t < t2)
+                                          service_at sched j_lst t);
+                first by rewrite -> big_cat_nat with (m := job_arrival j_lst) (n := t1);
+                  [by apply leq_addl | by ins | by apply leq_addr].
+              by apply leq_sum; ins; apply service_at_most_one.
+            }
+            {
+              apply negbT in LT; rewrite -ltnNge in LT.
+              rewrite -> big_cat_nat with (n := job_arrival j_lst);
+                [| by apply ltnW
+                 | by apply ltnW, workload_bound_last_job_arrives_before_end_of_interval].
+              rewrite /= -[\sum_(_ <= _ < _) 1]add0n; apply leq_add.
+              rewrite cumulative_service_before_job_arrival_zero;
+                [by apply leqnn | by ins | by apply leqnn].
+              by apply leq_sum; ins; apply service_at_most_one.
+            }
+          }
+        Qed.
+
+        (* Simplify the expression from the previous lemma. *)
+        Lemma workload_bound_simpl_expression_with_first_and_last :
+          job_arrival j_fst + R_tsk - t1 + (t2 - job_arrival j_lst) =
+                       delta + R_tsk - (job_arrival j_lst - job_arrival j_fst).
+        Proof.
+          have lemma1 := workload_bound_last_job_arrives_before_end_of_interval.
+          have lemma2 := workload_bound_response_time_of_first_job_inside_interval.
+          rewrite addnBA; last by apply ltnW.
+          rewrite subh1 // -addnBA; last by apply leq_addr.
+          rewrite addnC [job_arrival _ + _]addnC.
+          unfold t2; rewrite [t1 + _]addnC -[delta + t1 - _]subnBA // subnn subn0.
+          rewrite addnA -subnBA; first by ins.
+          unfold j_fst, j_lst. rewrite -[_.+1]add0n.
+          apply prev_le_next; last by rewrite H_at_least_two_jobs add0n leqnn.
+          by ins; apply workload_bound_jobs_ordered_by_arrival.
+        Qed.
+
+        (* Bound the service of the middle jobs. *)
+        Lemma workload_bound_service_of_middle_jobs :
+          \sum_(0 <= i < num_mid_jobs)
+            service_during sched (nth elem sorted_jobs i.+1) t1 t2 <=
+            num_mid_jobs * task_cost tsk.
+        Proof.
+          apply leq_trans with (n := num_mid_jobs * task_cost tsk);
+            last by rewrite leq_mul2l; apply/orP; right. 
+          apply leq_trans with (n := \sum_(0 <= i < num_mid_jobs) task_cost tsk);
+            last by rewrite big_const_nat iter_addn addn0 mulnC subn0.
+          rewrite big_nat_cond [\sum_(0 <= i < num_mid_jobs) task_cost _]big_nat_cond.
+          apply leq_sum; intros i; rewrite andbT; move => /andP LT; des.
+          eapply cumulative_service_le_task_cost;
+            [by apply H_completed_jobs_dont_execute | | by apply H_jobs_have_valid_parameters].
+          exploit workload_bound_all_jobs_from_tsk.
+          {
+            instantiate (1 := nth elem sorted_jobs i.+1).
+            apply mem_nth; rewrite H_at_least_two_jobs.
+            by rewrite ltnS; apply leq_trans with (n := num_mid_jobs).
+          }
+          by ins; des.
+        Qed.
+
+        (* Conclude that the distance between first and last is at least num_mid_jobs + 1 periods. *)
+        Lemma workload_bound_many_periods_in_between :
+          job_arrival j_lst - job_arrival j_fst >= num_mid_jobs.+1 * (task_period tsk).
+        Proof.
+          assert (EQnk: num_mid_jobs.+1=(size sorted_jobs).-1).
+            by rewrite H_at_least_two_jobs.
+          unfold j_fst, j_lst; rewrite EQnk telescoping_sum;
+            last by ins; apply workload_bound_jobs_ordered_by_arrival.
+          rewrite -[_ * _ tsk]addn0 mulnC -iter_addn -{1}[_.-1]subn0 -big_const_nat. 
+          rewrite big_nat_cond [\sum_(0 <= i < _)(_-_)]big_nat_cond.
+          apply leq_sum; intros i; rewrite andbT; move => /andP LT; des.
+
+          (* To simplify, call the jobs 'cur' and 'next' *)
+          set cur := nth elem sorted_jobs i.
+          set next := nth elem sorted_jobs i.+1.
+
+          (* Show that cur arrives earlier than next *)
+          assert (ARRle: job_arrival cur <= job_arrival next).
+            by unfold cur, next; apply workload_bound_jobs_ordered_by_arrival.
+             
+          (* Show that both cur and next are in the arrival sequence *)
+          assert (INnth: cur \in scheduled_jobs /\ next \in scheduled_jobs).
+          {
+            rewrite 2!workload_bound_job_in_same_sequence; split.
+              by apply mem_nth, (ltn_trans LT0); destruct sorted_jobs; ins.
+              by apply mem_nth; destruct sorted_jobs; ins.
+          }
+          rewrite 2?mem_filter in INnth; des.
+
+          (* Use the sporadic task model to conclude that cur and next are separated
+             by at least (task_period tsk) units. Of course this only holds if cur != next.
+             Since we don't know much about the list (except that it's sorted), we must
+             also prove that it doesn't contain duplicates. *)
+          assert (CUR_LE_NEXT: job_arrival cur + task_period (job_task cur) <= job_arrival next).
+          {
+            apply H_sporadic_tasks; last by ins.
+            unfold cur, next, not; intro EQ; move: EQ => /eqP EQ.
+            rewrite nth_uniq in EQ; first by move: EQ => /eqP EQ; intuition.
+              by apply ltn_trans with (n := (size sorted_jobs).-1); destruct sorted_jobs; ins.
+              by destruct sorted_jobs; ins.
+              by rewrite sort_uniq -/scheduled_jobs filter_uniq // undup_uniq.
+              by rewrite INnth INnth0.  
+          }
+          by rewrite subh3 // addnC -INnth.
+        Qed.
+
+        (* Prove that n_k is at least the number of the middle jobs *)
+        Lemma workload_bound_n_k_covers_middle_jobs :
+          n_k >= num_mid_jobs.
+        Proof.
+          rename H_valid_task_parameters into PARAMS.
+          unfold is_valid_sporadic_task in *; des.
+          rewrite leqNgt; apply/negP; unfold not; intro LTnk.
+          assert (DISTmax: job_arrival j_lst - job_arrival j_fst >= delta + task_period tsk).
+          {
+            apply leq_trans with (n := n_k.+2 * task_period tsk).
+            {
+              rewrite -addn1 mulnDl mul1n leq_add2r.
+              apply leq_trans with (n := delta + R_tsk - task_cost tsk);
+                first by rewrite -addnBA //; apply leq_addr.
+              by apply ltnW, ltn_ceil, PARAMS0.
+            }
+            apply leq_trans with (num_mid_jobs.+1 * task_period tsk); 
+              first by rewrite leq_mul2r; apply/orP; right.
+            by apply workload_bound_many_periods_in_between.
+          }
+          rewrite <- leq_add2r with (p := job_arrival j_fst) in DISTmax.
+          rewrite addnC subh1 in DISTmax; last first.
+          {
+            unfold j_fst, j_lst; rewrite -[_.+1]add0n.
+            apply prev_le_next; last by rewrite H_at_least_two_jobs add0n leqnn.
+            by ins; apply workload_bound_jobs_ordered_by_arrival.
+          }
+          rewrite -subnBA // subnn subn0 in DISTmax.
+          rewrite [delta + task_period tsk]addnC addnA in DISTmax.
+          have BEFOREt2 := workload_bound_last_job_arrives_before_end_of_interval.
+          generalize BEFOREt2; move: BEFOREt2; rewrite {1}ltnNge; move => /negP BEFOREt2'.
+          intros BEFOREt2; apply BEFOREt2'; clear BEFOREt2'.
+          apply leq_trans with (n := job_arrival j_fst + task_deadline tsk + delta);
+            last by apply leq_trans with (n := job_arrival j_fst + task_period tsk + delta);
+              [rewrite leq_add2r leq_add2l; apply H_constrained_deadline | apply DISTmax].
+          unfold t2; rewrite leq_add2r.
+          apply leq_trans with (n := job_arrival j_fst + R_tsk);
+            last by rewrite leq_add2l.
+          by apply workload_bound_response_time_of_first_job_inside_interval.
+        Qed.
+
+        (* If n_k = num_mid_jobs, then the workload bound holds. *)
+        Lemma workload_bound_n_k_equals_num_mid_jobs :
+          num_mid_jobs = n_k ->
+          service_during sched j_lst t1 t2 +
+            service_during sched j_fst t1 t2 +
+            \sum_(0 <= i < num_mid_jobs)
+             service_during sched (nth elem sorted_jobs i.+1) t1 t2
+          <= workload_bound.
+        Proof.
+          rename H_valid_task_parameters into PARAMS.
+          unfold is_valid_sporadic_task in *; des.
+          unfold workload_bound, W; fold n_k.
+          move => NK; rewrite -NK.
+          apply leq_add;
+            last by apply workload_bound_service_of_middle_jobs.
+          apply leq_trans with (delta + R_tsk - (job_arrival j_lst - job_arrival j_fst)).
+          {
+            rewrite addnC -workload_bound_simpl_expression_with_first_and_last.
+            by apply workload_bound_service_of_first_and_last_jobs.
+          }
+          rewrite leq_min; apply/andP; split.
+          {
+            rewrite leq_subLR [_ + task_cost _]addnC -leq_subLR.
+            apply leq_trans with (num_mid_jobs.+1 * task_period tsk);
+              last by apply workload_bound_many_periods_in_between.
+            rewrite NK ltnW // -ltn_divLR;
+              last by apply PARAMS0.
+            by unfold n_k, max_jobs, div_floor.
+          }
+          {
+            rewrite -subnDA; apply leq_sub2l.
+            apply leq_trans with (n := num_mid_jobs.+1 * task_period tsk);
+              last by apply workload_bound_many_periods_in_between.
+            rewrite -addn1 addnC mulnDl mul1n.
+            by rewrite leq_add2l; last by apply PARAMS3.
+          }
+        Qed.
+
+        (* If n_k = num_mid_jobs + 1, then the workload bound holds. *)
+        Lemma workload_bound_n_k_equals_num_mid_jobs_plus_1 :
+          num_mid_jobs.+1 = n_k ->
+          service_during sched j_lst t1 t2 +
+            service_during sched j_fst t1 t2 +
+            \sum_(0 <= i < num_mid_jobs)
+             service_during sched (nth elem sorted_jobs i.+1) t1 t2
+          <= workload_bound.
+        Proof.
+          unfold workload_bound, W; fold n_k.
+          move => NK; rewrite -NK.
+          rewrite -{2}addn1 mulnDl mul1n [_* _ + _]addnC addnA addn_minl.
+          apply leq_add; last by apply workload_bound_service_of_middle_jobs. 
+          rewrite leq_min; apply/andP; split.
+          {
+            assert (SIZE: 0 < size sorted_jobs).
+              by rewrite H_at_least_two_jobs.
+            have INfst := workload_bound_j_fst_is_job_of_tsk SIZE elem;
+            have INlst := workload_bound_j_lst_is_job_of_tsk; des.
+            by apply leq_add; apply cumulative_service_le_task_cost with (task_deadline0 := task_deadline)
+                             (job_cost0 := job_cost) (job_deadline0 := job_deadline) (job_task0 := job_task).
+          }
+          {
+            rewrite subnAC subnK; last first.
+            {
+              assert (TMP: delta + R_tsk = task_cost tsk + (delta + R_tsk - task_cost tsk));
+                first by rewrite subnKC; [by ins | by rewrite -[task_cost _]add0n; apply leq_add].
+              rewrite TMP; clear TMP.
+              rewrite -{1}[task_cost _]addn0 -addnBA NK; [by apply leq_add | by apply leq_trunc_div].
+            }
+            apply leq_trans with (delta + R_tsk - (job_arrival j_lst - job_arrival j_fst)).
+            {
+              rewrite addnC -workload_bound_simpl_expression_with_first_and_last.
+              by apply workload_bound_service_of_first_and_last_jobs.
+            }
+            {
+              by apply leq_sub2l, workload_bound_many_periods_in_between.
+            }
+          }
+        Qed.
+        
+      End WorkloadTwoOrMoreJobs.
+
+      (* Using the lemmas above, we prove the main theorem about the workload bound. *)
+      Theorem workload_bounded_by_W :
+        workload_of tsk t1 (t1 + delta) <= workload_bound.
+      Proof.
+        unfold workload_of, workload_bound, W in *; ins; des.
+        fold n_k.
+
+        (* Use the definition of workload based on list of jobs. *)
+        rewrite workload_eq_workload_joblist.
+
+        (* Now we order the list by job arrival time. *)
+        rewrite workload_bound_simpl_by_sorting_scheduled_jobs.
+
+        (* Next, we show that the workload bound holds if n_k
+           is no larger than the number of interferings jobs. *)
+        destruct (size sorted_jobs <= n_k) eqn:NUM;
+          first by apply workload_bound_holds_for_at_most_n_k_jobs.
+        apply negbT in NUM; rewrite -ltnNge in NUM.
+
+        (* Find some dummy element to use in the nth function *)
+        assert (EX: exists elem: JobIn arr_seq, True).
+          destruct sorted_jobs; [ by rewrite ltn0 in NUM | by exists j].
+        destruct EX as [elem _].
+
+        (* Now we index the sum to access the first and last elements. *)
+        rewrite (big_nth elem).
+
+        (* First, we show that the bound holds for an empty list of jobs. *)
+        destruct (size sorted_jobs) as [| n] eqn:SIZE;
+          first by rewrite big_geq.
+        
+        (* Then, we show the same for a singleton set of jobs. *)
+        destruct n as [| num_mid_jobs];
+          first by apply workload_bound_holds_for_a_single_job; rewrite SIZE.
+        
+        (* Knowing that we have at least two elements, we take first and last out of the sum *) 
+        rewrite [nth]lock big_nat_recl // big_nat_recr // /= -lock.
+        rewrite addnA addnC addnA.
+    
+        (* There are two cases to be analyze since n <= n_k < n + 2,
+           where n is the number of middle jobs. *)
+        have NK := workload_bound_n_k_covers_middle_jobs num_mid_jobs SIZE elem.
+        move: NK; rewrite leq_eqVlt orbC leq_eqVlt; move => /orP [NK | /eqP NK].
+        move: NK => /orP [/eqP NK | NK]; last by rewrite ltnS leqNgt NK in NUM.
+        {
+          (* Case 1: n_k = n + 1, where n is the number of middle jobs. *)
+          by apply (workload_bound_n_k_equals_num_mid_jobs_plus_1 num_mid_jobs).
+        }
+        {
+          (* Case 2: n_k = n, where n is the number of middle jobs. *)
+          by apply (workload_bound_n_k_equals_num_mid_jobs num_mid_jobs).
+        }
+      Qed.
+
+    End MainProof.
+    
+  End ProofWorkloadBound.
+
+End WorkloadBound.
\ No newline at end of file
diff --git a/analysis/basic/bertogna_edf_comp.v b/analysis/basic/bertogna_edf_comp.v
index d9493644e86d3a567ec61e0984c3510492750c81..3bc47989a57b84f8797162319c04d987e83d07b0 100755
--- a/analysis/basic/bertogna_edf_comp.v
+++ b/analysis/basic/bertogna_edf_comp.v
@@ -57,7 +57,7 @@ Module ResponseTimeIterationEDF.
     (* To compute the response-time bounds of the entire task set,
        We start the iteration with a sequence of tasks and costs:
        <(task1, cost1), (task2, cost2), ...>. *)
-    Let initial_state (ts: taskset_of sporadic_task) :=
+    Let initial_state (ts: seq sporadic_task) :=
       map (fun t => (t, task_cost t)) ts.
 
     (* Then, we successively update the the response-time bounds based
@@ -68,13 +68,13 @@ Module ResponseTimeIterationEDF.
     (* To ensure that the procedure converges, we stop the iteration
        after a "sufficient" number of times, which corresponds to
        the time complexity of the procedure. *)
-    Let max_steps (ts: taskset_of sporadic_task) :=
+    Let max_steps (ts: seq sporadic_task) :=
       \sum_(tsk <- ts) (task_deadline tsk - task_cost tsk) + 1.
 
     (* This yields the following definition for the RTA. At the end
        we check if all computed response-time bounds are less than
        or equal to the deadline, in which case they are valid. *)
-    Definition edf_claimed_bounds (ts: taskset_of sporadic_task) :=
+    Definition edf_claimed_bounds (ts: seq sporadic_task) :=
       let R_values := iter (max_steps ts) edf_rta_iteration (initial_state ts) in
         if (all R_le_deadline R_values) then
           Some R_values
@@ -82,7 +82,7 @@ Module ResponseTimeIterationEDF.
 
     (* The schedulability test simply checks if we got a list of
        response-time bounds (i.e., if the computation did not fail). *)
-    Definition edf_schedulable (ts: taskset_of sporadic_task) :=
+    Definition edf_schedulable (ts: seq sporadic_task) :=
       edf_claimed_bounds ts != None.
 
     (* In the following section, we prove several helper lemmas about the
@@ -205,7 +205,7 @@ Module ResponseTimeIterationEDF.
     Section Convergence.
 
       (* Consider any valid task set. *)
-      Variable ts: taskset_of sporadic_task.
+      Variable ts: seq sporadic_task.
       Hypothesis H_valid_task_parameters:
         valid_sporadic_taskset task_cost task_period task_deadline ts.
       
@@ -395,11 +395,11 @@ Module ResponseTimeIterationEDF.
 
           assert (SUBST: forall l delta,
                     \sum_(j <- l | let '(tsk_other, _) := j in
-                      jldp_can_interfere_with tsk_i tsk_other)
+                      different_task tsk_i tsk_other)
                         (let '(tsk_other, R_other) := j in
                           interference_bound_edf task_cost task_period task_deadline tsk_i delta
                             (tsk_other, R_other)) =
-                    \sum_(j <- l | jldp_can_interfere_with tsk_i (fst j))
+                    \sum_(j <- l | different_task tsk_i (fst j))
                       interference_bound_edf task_cost task_period task_deadline tsk_i delta j).
           {
             intros l x; clear -l.
@@ -444,13 +444,14 @@ Module ResponseTimeIterationEDF.
           }
           move: GE_COST => /allP GE_COST.
 
-          assert (LESUM: \sum_(j <- x1' | jldp_can_interfere_with tsk_i (fst j))
-                        interference_bound_edf task_cost task_period task_deadline tsk_i delta j <=                                  \sum_(j <- x2' | jldp_can_interfere_with tsk_i (fst j))
+          assert (LESUM: \sum_(j <- x1' | different_task tsk_i (fst j))
+                          interference_bound_edf task_cost task_period task_deadline tsk_i delta j <=
+                         \sum_(j <- x2' | different_task tsk_i (fst j))
                         interference_bound_edf task_cost task_period task_deadline tsk_i delta' j).
           {
             set elem := (tsk0, R0); rewrite 2!(big_nth elem).
             rewrite -SIZE.
-            rewrite big_mkcond [\sum_(_ <- _ | jldp_can_interfere_with _ _)_]big_mkcond.
+            rewrite big_mkcond [\sum_(_ <- _ | different_task _ _)_]big_mkcond.
             rewrite big_seq_cond [\sum_(_ <- _ | true) _]big_seq_cond.
             apply leq_sum; intros j; rewrite andbT; intros INj.
             rewrite mem_iota add0n subn0 in INj; move: INj => /andP [_ INj].
@@ -459,7 +460,7 @@ Module ResponseTimeIterationEDF.
               have MAP := @nth_map _ elem _ tsk0 (fun x => fst x).
               by rewrite -2?MAP -?SIZE //; f_equal.
             } rewrite -FSTeq.
-            destruct (jldp_can_interfere_with tsk_i (fst (nth elem x1' j))) eqn:INTERF;
+            destruct (different_task tsk_i (fst (nth elem x1' j))) eqn:INTERF;
               last by done.
             {
               exploit (LE elem); [by rewrite /= SIZE | | intro LEj].
@@ -485,7 +486,7 @@ Module ResponseTimeIterationEDF.
               by apply interference_bound_edf_monotonic.
             }
           }
-          destruct (jldp_can_interfere_with tsk_i tsk0) eqn:INTERFtsk0; last by done.
+          destruct (different_task tsk_i tsk0) eqn:INTERFtsk0; last by done.
           apply leq_add; last by done.
           {             
             exploit (LE (tsk0, R0)); [by rewrite /= SIZE | | intro LEj];
@@ -820,15 +821,13 @@ Module ResponseTimeIterationEDF.
       Qed.
 
       (* Therefore, with regard to the response-time bound recurrence, ...*)
-      Let rt_recurrence (tsk: sporadic_task) (rt_bounds: seq task_with_response_time) (R: time) :=
-        task_cost tsk + div_floor (I rt_bounds tsk R) num_cpus.
       
       (* ..., the individual response-time bounds (elements of the list) are also fixed points. *)
       Theorem edf_claimed_bounds_finds_fixed_point_for_each_bound :
         forall tsk R rt_bounds,
           edf_claimed_bounds ts = Some rt_bounds ->
           (tsk, R) \in rt_bounds ->
-          R = rt_recurrence tsk rt_bounds R.
+          R = edf_response_time_bound rt_bounds tsk R.
       Proof.
         intros tsk R rt_bounds SOME IN.
         have CONV := edf_claimed_bounds_finds_fixed_point_of_list rt_bounds.
@@ -858,17 +857,14 @@ Module ResponseTimeIterationEDF.
 
     Section MainProof.
 
-      (* Consider a task set ts. *)
+      (* Consider a task set ts where... *)
       Variable ts: taskset_of sporadic_task.
       
-      (* Assume the task set has no duplicates, ... *)
-      Hypothesis H_ts_is_a_set: uniq ts.
-
-      (* ...all tasks have valid parameters, ... *)
+      (* ...all tasks have valid parameters ... *)
       Hypothesis H_valid_task_parameters:
         valid_sporadic_taskset task_cost task_period task_deadline ts.
 
-      (* ...constrained deadlines, ...*)
+      (* ...and constrained deadlines. *)
       Hypothesis H_constrained_deadlines:
         forall tsk, tsk \in ts -> task_deadline tsk <= task_period tsk.
 
diff --git a/analysis/basic/bertogna_edf_theory.v b/analysis/basic/bertogna_edf_theory.v
index b76bc4784d570fc6e6c1487efbe411aa755fd732..b35dc603940d33bdebdcc6f9bec7e8859002c4d9 100644
--- a/analysis/basic/bertogna_edf_theory.v
+++ b/analysis/basic/bertogna_edf_theory.v
@@ -2,7 +2,8 @@ Require Import rt.util.all.
 Require Import rt.model.basic.task rt.model.basic.job rt.model.basic.task_arrival
                rt.model.basic.schedule rt.model.basic.platform rt.model.basic.interference
                rt.model.basic.workload rt.model.basic.schedulability rt.model.basic.priority
-               rt.model.basic.platform rt.model.basic.response_time.
+               rt.model.basic.platform rt.model.basic.response_time
+               rt.model.basic.constrained_deadlines.
 Require Import rt.analysis.basic.workload_bound rt.analysis.basic.interference_bound_edf.
 From mathcomp Require Import ssreflect ssrbool eqtype ssrnat seq fintype bigop div path.
 
@@ -10,10 +11,12 @@ Module ResponseTimeAnalysisEDF.
 
   Export Job SporadicTaskset Schedule ScheduleOfSporadicTask Workload Schedulability ResponseTime
          Priority SporadicTaskArrival WorkloadBound InterferenceBoundEDF
-         Interference Platform.
+         Interference Platform ConstrainedDeadlines.
 
-  (* In this section, we prove that Bertogna and Cirinei's RTA yields
-     safe response-time bounds. *)
+  (* In this section, we prove that any fixed point in Bertogna and
+     Cirinei's RTA for EDF scheduling is a safe response-time bound.
+     This analysis can be found in Chapter 17.1.2 of Baruah et al.'s
+     book Multiprocessor Scheduling for Real-time Systems. *)
   Section ResponseTimeBound.
 
     Context {sporadic_task: eqType}.
@@ -36,48 +39,52 @@ Module ResponseTimeAnalysisEDF.
       forall (j: JobIn arr_seq),
         valid_sporadic_job task_cost task_deadline job_cost job_deadline job_task j.
 
-    (* Consider any schedule such that...*)
-    Variable num_cpus: nat.
-    Variable sched: schedule num_cpus arr_seq.
-
-    (* ...jobs do not execute before their arrival times nor longer
-       than their execution costs. *)
-    Hypothesis H_jobs_must_arrive_to_execute:
-      jobs_must_arrive_to_execute sched.
-    Hypothesis H_completed_jobs_dont_execute:
-      completed_jobs_dont_execute job_cost sched.
-
-    (* Also assume that jobs are sequential and that
-       there exists at least one processor. *)
-    Hypothesis H_sequential_jobs: sequential_jobs sched.
-    Hypothesis H_at_least_one_cpu :
-      num_cpus > 0.
-
-    (* Assume that we have a task set where all tasks have valid
-       parameters and constrained deadlines, ... *)
+    (* Consider a task set ts where all tasks have valid parameters
+       and constrained deadlines, ... *)
     Variable ts: taskset_of sporadic_task.
     Hypothesis H_valid_task_parameters:
       valid_sporadic_taskset task_cost task_period task_deadline ts.
     Hypothesis H_constrained_deadlines:
       forall tsk, tsk \in ts -> task_deadline tsk <= task_period tsk.
 
-    (* ... and that all jobs in the arrival sequence come from the task set. *)
+    (* ... and assume that all jobs in the arrival sequence come from the task set. *)
     Hypothesis H_all_jobs_from_taskset:
       forall (j: JobIn arr_seq), job_task j \in ts.
+
+    (* Next, consider any schedule such that...*)
+    Variable num_cpus: nat.
+    Variable sched: schedule num_cpus arr_seq.
+
+    (* ...jobs are sequential and do not execute before their
+       arrival times nor longer than their execution costs. *)
+    Hypothesis H_sequential_jobs: sequential_jobs sched.
+    Hypothesis H_jobs_must_arrive_to_execute:
+      jobs_must_arrive_to_execute sched.
+    Hypothesis H_completed_jobs_dont_execute:
+      completed_jobs_dont_execute job_cost sched.
+
+    (* Assume that there exists at least one processor. *)
+    Hypothesis H_at_least_one_cpu: num_cpus > 0.
+    
+    (* Assume that the schedule is a work-conserving EDF schedule. *)
+    Hypothesis H_work_conserving: work_conserving job_cost sched.
+    Hypothesis H_edf_policy: enforces_JLDP_policy job_cost sched (EDF job_deadline).
     
+    (* Let's define some local names to avoid passing many parameters. *)
     Let no_deadline_is_missed_by_tsk (tsk: sporadic_task) :=
       task_misses_no_deadline job_cost job_deadline job_task sched tsk.
     Let response_time_bounded_by (tsk: sporadic_task) :=
       is_response_time_bound_of_task job_cost job_task tsk sched.
 
-    (* Assume that a response-time bound R is known...  *)
+    (* Next we consider the response-time recurrence.
+       Assume that a response-time bound R is known...  *)
     Let task_with_response_time := (sporadic_task * time)%type.
     Variable rt_bounds: seq task_with_response_time.
 
     (* ...for any task in the task set, ... *)
     Hypothesis H_rt_bounds_contains_all_tasks: unzip1 rt_bounds = ts.
 
-    (* ... such that R is a fixed-point of the response-time recurrence, ... *)
+    (* ... where R is a fixed-point of the response-time recurrence, ... *)
     Let I (tsk: sporadic_task) (delta: time) :=
       total_interference_bound_edf task_cost task_period task_deadline tsk rt_bounds delta.
     Hypothesis H_response_time_is_fixed_point :
@@ -90,16 +97,7 @@ Module ResponseTimeAnalysisEDF.
       forall tsk_other R,
         (tsk_other, R) \in rt_bounds -> R <= task_deadline tsk_other.
 
-    (* Assume that we have a work-conserving EDF scheduler. *)
-    Hypothesis H_work_conserving: work_conserving job_cost sched.
-    Hypothesis H_edf_policy: enforces_JLDP_policy job_cost sched (EDF job_deadline).
-    
-    (* Assume that the task set has no duplicates. This is required to
-       avoid problems when counting tasks (for example, when stating
-       that the number of interfering tasks is at most num_cpus). *)
-    Hypothesis H_ts_is_a_set : uniq ts.
-
-    (* In order to prove that R is a response-time bound, we first present some lemmas. *)
+    (* In order to prove that R is a response-time bound, we first provide some lemmas. *)
     Section Lemmas.
 
       (* Let (tsk, R) be any task to be analyzed, with its response-time bound R. *)
@@ -127,7 +125,7 @@ Module ResponseTimeAnalysisEDF.
         task_interference job_cost job_task sched j tsk_other
                           (job_arrival j) (job_arrival j + R).
 
-      (* and X the total interference incurred by job j due to any task. *)
+      (* ...and X the total interference incurred by job j due to any task. *)
       Let X := total_interference job_cost sched j (job_arrival j) (job_arrival j + R).
 
       (* Recall Bertogna and Cirinei's workload bound ... *)
@@ -142,9 +140,14 @@ Module ResponseTimeAnalysisEDF.
       Let interference_bound (tsk_other: sporadic_task) (R_other: time) :=
         interference_bound_edf task_cost task_period task_deadline tsk R (tsk_other, R_other). 
       
-      (* Also, let ts_interf be the subset of tasks that interfere with tsk. *)
-      Let ts_interf := [seq tsk_other <- ts | jldp_can_interfere_with tsk tsk_other].
+      (* Based on the definition of a different task, ... *)
+      Let other_task := different_task tsk.
 
+      (* ...let other_tasks denote the set of tasks that are different from tsk. *)
+      Let other_tasks :=
+        [seq tsk_other <- ts | other_task tsk_other].
+
+      (* Now we establish results the interfering tasks. *)
       Section LemmasAboutInterferingTasks.
         
         (* Let (tsk_other, R_other) be any pair of higher-priority task and
@@ -156,7 +159,8 @@ Module ResponseTimeAnalysisEDF.
         (* Note that tsk_other is in the task set, ...*)
         Lemma bertogna_edf_tsk_other_in_ts: tsk_other \in ts.
         Proof.
-          by rewrite -H_rt_bounds_contains_all_tasks; apply/mapP; exists (tsk_other, R_other).
+          rewrite set_mem -H_rt_bounds_contains_all_tasks.
+          by apply/mapP; exists (tsk_other, R_other).
         Qed.
 
         (* ... and R_other is larger than the cost of tsk_other. *)
@@ -253,56 +257,11 @@ Module ResponseTimeAnalysisEDF.
         by apply completion_monotonic with (t := i); try (by done); apply ltnW.
       Qed.
 
-      Let scheduled_task_other_than_tsk (t: time) (tsk_other: sporadic_task) :=
-        task_is_scheduled job_task sched tsk_other t &&
-        jldp_can_interfere_with tsk tsk_other.
-      
-      (* 1) At all times that j is backlogged, all processors are busy with other tasks. *)
-      Lemma bertogna_edf_scheduling_invariant:
-        forall t,
-          job_arrival j <= t < job_arrival j + R ->
-          backlogged job_cost sched j t ->
-          count (scheduled_task_other_than_tsk t) ts = num_cpus.
-      Proof.
-        rename H_all_jobs_from_taskset into FROMTS,
-               H_valid_task_parameters into PARAMS,
-               H_job_of_tsk into JOBtsk,
-               H_sporadic_tasks into SPO,
-               H_tsk_R_in_rt_bounds into INbounds,
-               H_all_previous_jobs_completed_on_time into BEFOREok,
-               H_tasks_miss_no_deadlines into NOMISS,
-               H_rt_bounds_contains_all_tasks into UNZIP,
-               H_constrained_deadlines into RESTR,
-               H_work_conserving into WORK.
-        unfold x, X, total_interference, task_interference.
-        move => t /andP [LEt LTt] BACK.
-        unfold scheduled_task_other_than_tsk in *.
-        eapply platform_cpus_busy_with_interfering_tasks; try (by done);
-          [ by apply WORK | by apply SPO 
-          | apply PARAMS; rewrite -JOBtsk; apply FROMTS
-          | by apply JOBtsk | by apply BACK | ].
-        {
-          intros j0 tsk0 TSK0 LE.
-          cut (tsk0 \in unzip1 rt_bounds = true); last by rewrite UNZIP -TSK0 FROMTS.
-          move => /mapP [p IN EQ]; destruct p as [tsk' R0]; simpl in *; subst tsk'.
-          apply completion_monotonic with (t0 := job_arrival j0 + R0); try (by done).
-          {
-            rewrite leq_add2l TSK0.
-            apply leq_trans with (n := task_deadline tsk0); first by apply NOMISS.
-            by apply RESTR; rewrite -TSK0 FROMTS.
-          }
-          {
-            apply BEFOREok with (tsk_other := tsk0); try (by done).
-            apply leq_ltn_trans with (n := t); last by done.
-            apply leq_trans with (n := job_arrival j0 + task_period tsk0); last by done.
-            rewrite leq_add2l; apply leq_trans with (n := task_deadline tsk0); first by apply NOMISS.
-            by apply RESTR; rewrite -TSK0 FROMTS.
-          }
-        }
-      Qed.
-
-      (* 2) Prove that during the scheduling window of j, any job that is scheduled while j is
-         backlogged comes from a different task. *)
+      (* 1) Next, we prove that during the scheduling window of j, any job that is
+            scheduled while j is backlogged comes from a different task.
+            This follows from the fact that j is the first job not to complete
+            by its response-time bound, so previous jobs of j's task must have
+            completed by their periods and cannot be pending. *)
       Lemma bertogna_edf_interference_by_different_tasks :
         forall t j_other,
           job_arrival j <= t < job_arrival j + R ->
@@ -354,12 +313,96 @@ Module ResponseTimeAnalysisEDF.
         }
       Qed.
 
+      (* 2) In order to use the lemmas in constrained_deadlines.v, we show that
+            all jobs released before the end of the interval complete by their
+            periods. This follows trivially from the hypothesis that all jobs
+            before (job_arrival j + R) complete by their response-time bounds. 
+            With this lemma, we can conclude that during job j's scheduling
+            window there cannot be multiple pending jobs of each task.*)
+      Lemma bertogna_edf_all_previous_jobs_complete_by_their_period:
+        forall t (j0: JobIn arr_seq),
+          t < job_arrival j + R ->
+          job_arrival j0 + task_period (job_task j0) <= t ->
+          completed job_cost sched j0
+             (job_arrival j0 + task_period (job_task j0)).
+      Proof.
+        rename H_rt_bounds_contains_all_tasks into UNZIP,
+               H_constrained_deadlines into CONSTR,
+               H_tasks_miss_no_deadlines into NOMISS,
+               H_all_jobs_from_taskset into FROMTS,
+               H_all_previous_jobs_completed_on_time into BEFOREok.
+        intros t j0 LEt LE.
+        cut ((job_task j0) \in unzip1 rt_bounds = true); last by rewrite UNZIP FROMTS.
+        move => /mapP [p IN EQ]; destruct p as [tsk' R0]; simpl in *; subst tsk'.
+        apply completion_monotonic with (t0 := job_arrival j0 + R0); first by done.
+        {
+          rewrite leq_add2l; apply leq_trans with (n := task_deadline (job_task j0));
+            [by apply NOMISS | by apply CONSTR; rewrite FROMTS].
+        }
+        apply BEFOREok with (tsk_other := (job_task j0)); try by done.
+        apply leq_ltn_trans with (n := t); last by done.
+        apply leq_trans with (n := job_arrival j0 + task_period (job_task j0)); last by done.
+        by rewrite leq_add2l; apply leq_trans with (n := task_deadline (job_task j0));
+          [by apply NOMISS | apply CONSTR; rewrite FROMTS].
+      Qed.
+
+      (* Let's define a predicate to identify the other tasks that are scheduled. *)
+      Let other_scheduled_task (t: time) (tsk_other: sporadic_task) :=
+        task_is_scheduled job_task sched tsk_other t &&
+        other_task tsk_other.
       
-      (* 3) Next, we prove that the sum of the interference of each task is equal
+      (* 3) Now we prove that, at all times that j is backlogged, the number
+            of tasks other than tsk that are scheduled is exactly the number
+            of processors in the system. This is required to prove lemma (4). *)
+      Lemma bertogna_edf_all_cpus_are_busy:
+        forall t,
+          job_arrival j <= t < job_arrival j + R ->
+          backlogged job_cost sched j t ->
+          count (other_scheduled_task t) ts = num_cpus.
+      Proof.
+        rename H_all_jobs_from_taskset into FROMTS,
+               H_valid_task_parameters into PARAMS,
+               H_job_of_tsk into JOBtsk,
+               H_sporadic_tasks into SPO,
+               H_tsk_R_in_rt_bounds into INbounds,
+               H_all_previous_jobs_completed_on_time into BEFOREok,
+               H_tasks_miss_no_deadlines into NOMISS,
+               H_rt_bounds_contains_all_tasks into UNZIP,
+               H_constrained_deadlines into RESTR,
+               H_work_conserving into WORK.
+        unfold x, X, total_interference, task_interference.
+        move => t /andP [LEt LTt] BACK.
+        eapply platform_cpus_busy_with_interfering_tasks; try (by done);
+          [ by apply WORK | by apply SPO 
+          | apply PARAMS; rewrite -JOBtsk; apply FROMTS
+          | by apply JOBtsk | by apply BACK | ].
+        {
+          intros j0 tsk0 TSK0 LE.
+          cut (tsk0 \in unzip1 rt_bounds = true); last by rewrite UNZIP -TSK0 FROMTS.
+          move => /mapP [p IN EQ]; destruct p as [tsk' R0]; simpl in *; subst tsk'.
+          apply completion_monotonic with (t0 := job_arrival j0 + R0); try (by done).
+          {
+            rewrite leq_add2l TSK0.
+            apply leq_trans with (n := task_deadline tsk0); first by apply NOMISS.
+            by apply RESTR; rewrite -TSK0 FROMTS.
+          }
+          {
+            apply BEFOREok with (tsk_other := tsk0); try (by done).
+            apply leq_ltn_trans with (n := t); last by done.
+            apply leq_trans with (n := job_arrival j0 + task_period tsk0); last by done.
+            rewrite leq_add2l; apply leq_trans with (n := task_deadline tsk0); first by apply NOMISS.
+            by apply RESTR; rewrite -TSK0 FROMTS.
+          }
+        }
+      Qed.
+
+      (* 4) Next, we prove that the sum of the interference of each task is equal
             to the total interference multiplied by the number of processors. This
-            holds because interference only occurs when all processors are busy. *)
-      Lemma bertogna_edf_all_cpus_busy :
-        \sum_(tsk_k <- ts_interf) x tsk_k = X * num_cpus.
+            holds because interference only occurs when all processors are busy.
+            With this lemma we can relate per-task interference with the total
+            interference incurred by j (backlogged time). *)
+      Lemma bertogna_edf_interference_on_all_cpus :
+        \sum_(tsk_k <- other_tasks) x tsk_k = X * num_cpus.
       Proof.
         have DIFFTASK := bertogna_edf_interference_by_different_tasks.
         rename H_all_jobs_from_taskset into FROMTS,
@@ -388,7 +431,7 @@ Module ResponseTimeAnalysisEDF.
         apply eq_trans with (y := \sum_(cpu < num_cpus) 1); last by simpl_sum_const.
         apply eq_bigr; intros cpu _.
         move: (WORK j t BACK cpu) => [j_other /eqP SCHED]; unfold scheduled_on in *.
-        rewrite (bigD1_seq (job_task j_other)) /=; last by rewrite filter_uniq.
+        rewrite (bigD1_seq (job_task j_other)) /=; last by rewrite filter_uniq; destruct ts.
         {
           rewrite (eq_bigr (fun i => 0));
             last by intros i DIFF; rewrite /task_scheduled_on SCHED;apply/eqP;rewrite eqb0 eq_sym.
@@ -396,54 +439,25 @@ Module ResponseTimeAnalysisEDF.
           by unfold task_scheduled_on; rewrite SCHED.
         }
         rewrite mem_filter; apply/andP; split; last by apply FROMTS.
-        unfold jldp_can_interfere_with.
         apply DIFFTASK with (t := t); [by auto | by done |].
         by apply/existsP; exists cpu; apply/eqP.
       Qed.
 
-      (* 4) Show that by the induction hypothesis, all jobs released
-         before the end of the interval complete by their periods. *)
-      Lemma bertogna_edf_all_previous_jobs_complete_by_their_period:
-        forall t (j0: JobIn arr_seq),
-          t < job_arrival j + R ->
-          job_arrival j0 + task_period (job_task j0) <= t ->
-          completed job_cost sched j0
-             (job_arrival j0 + task_period (job_task j0)).
-      Proof.
-        rename H_rt_bounds_contains_all_tasks into UNZIP,
-               H_constrained_deadlines into CONSTR,
-               H_tasks_miss_no_deadlines into NOMISS,
-               H_all_jobs_from_taskset into FROMTS,
-               H_all_previous_jobs_completed_on_time into BEFOREok.
-        intros t j0 LEt LE.
-        cut ((job_task j0) \in unzip1 rt_bounds = true); last by rewrite UNZIP FROMTS.
-        move => /mapP [p IN EQ]; destruct p as [tsk' R0]; simpl in *; subst tsk'.
-        apply completion_monotonic with (t0 := job_arrival j0 + R0); first by done.
-        {
-          rewrite leq_add2l; apply leq_trans with (n := task_deadline (job_task j0));
-            [by apply NOMISS | by apply CONSTR; rewrite FROMTS].
-        }
-        apply BEFOREok with (tsk_other := (job_task j0)); try by done.
-        apply leq_ltn_trans with (n := t); last by done.
-        apply leq_trans with (n := job_arrival j0 + task_period (job_task j0)); last by done.
-        by rewrite leq_add2l; apply leq_trans with (n := task_deadline (job_task j0));
-          [by apply NOMISS | apply CONSTR; rewrite FROMTS].
-      Qed.
-
-      (* Let (cardGE delta) be the number of interfering tasks whose interference
-         is larger than delta. *)
-      Let cardGE (delta: time) := count (fun i => x i >= delta) ts_interf.
+      (* Before stating the next lemma, let (num_tasks_exceeding delta) be the
+         number of interfering tasks whose interference x is larger than delta. *)
+      Let num_tasks_exceeding delta := count (fun i => x i >= delta) (other_tasks).
 
-      (* 5) If there is at least one of such tasks (e.g., cardGE > 0), then the
-         cumulative interference caused by the complementary set of interfering
-         tasks fills at least (num_cpus - cardGE) processors. *)
-      Lemma bertogna_edf_helper_lemma :
+      (* 5) Now we prove that, for any delta, if (num_task_exceeding delta > 0), then the
+            cumulative interference caused by the complementary set of interfering tasks fills
+            the remaining, not-completely-full (num_cpus - num_tasks_exceeding delta)
+            processors. *)
+      Lemma bertogna_edf_interference_in_non_full_processors :
         forall delta,
-          0 < cardGE delta < num_cpus ->
-          \sum_(i <- ts_interf | x i < delta) x i >= delta * (num_cpus - cardGE delta).
+          0 < num_tasks_exceeding delta < num_cpus ->
+          \sum_(i <- other_tasks | x i < delta) x i >= delta * (num_cpus - num_tasks_exceeding delta).
       Proof.
         have COMP := bertogna_edf_all_previous_jobs_complete_by_their_period.
-        have INV := bertogna_edf_scheduling_invariant.
+        have INV := bertogna_edf_all_cpus_are_busy.
         rename H_all_jobs_from_taskset into FROMTS,
                H_valid_task_parameters into PARAMS,
                H_job_of_tsk into JOBtsk,
@@ -460,14 +474,14 @@ Module ResponseTimeAnalysisEDF.
         set some_interference_A := fun t =>
           has (fun tsk_k => backlogged job_cost sched j t &&
                             (x tsk_k >= delta) &&
-                            task_is_scheduled job_task sched tsk_k t) ts_interf.
+                            task_is_scheduled job_task sched tsk_k t) other_tasks.
         set total_interference_B := fun t =>
             backlogged job_cost sched j t *
             count (fun tsk_k => (x tsk_k < delta) &&
-                  task_is_scheduled job_task sched tsk_k t) ts_interf.
+                  task_is_scheduled job_task sched tsk_k t) other_tasks.
 
         apply leq_trans with ((\sum_(job_arrival j <= t < job_arrival j + R)
-                              some_interference_A t) * (num_cpus - cardGE delta)).
+                              some_interference_A t) * (num_cpus - num_tasks_exceeding delta)).
         {
           rewrite leq_mul2r; apply/orP; right.
           move: HAS => /hasP HAS; destruct HAS as [tsk_a INa LEa].
@@ -532,13 +546,13 @@ Module ResponseTimeAnalysisEDF.
             [rewrite mul1n /= | by rewrite has_pred0 //].
           
           destruct (has (fun tsk_k : sporadic_task => (delta <= x tsk_k) &&
-                     task_is_scheduled job_task sched tsk_k t) ts_interf) eqn:HAS';
+                     task_is_scheduled job_task sched tsk_k t) other_tasks) eqn:HAS';
             last by done.
           rewrite mul1n; move: HAS => /hasP [tsk_k INk LEk].
-          unfold cardGE.
+          unfold num_tasks_exceeding.
           apply leq_trans with (n := num_cpus -
                        count (fun i => (x i >= delta) &&
-                          task_is_scheduled job_task sched i t) ts_interf).
+                          task_is_scheduled job_task sched i t) other_tasks).
           {
             apply leq_sub2l.
             rewrite -2!sum1_count big_mkcond /=.
@@ -547,11 +561,11 @@ Module ResponseTimeAnalysisEDF.
             by destruct (task_is_scheduled job_task sched i t);
               [by rewrite andbT | by rewrite andbF].
           }
-          rewrite -count_filter -[count _ ts_interf]count_filter.
+          rewrite -count_filter -[count _ other_tasks]count_filter.
           eapply leq_trans with (n := count (predC (fun tsk => delta <= x tsk)) _);
             last by apply eq_leq, eq_in_count; red; ins; rewrite ltnNge.
           rewrite leq_subLR count_predC size_filter.
-          by apply leq_trans with (n := count (scheduled_task_other_than_tsk t) ts);
+          by apply leq_trans with (n := count (other_scheduled_task t) ts);
             [by rewrite INV | by rewrite count_filter].
         }
         {
@@ -559,7 +573,7 @@ Module ResponseTimeAnalysisEDF.
           rewrite exchange_big /=; apply leq_sum; intros t _.
           destruct (backlogged job_cost sched j t) eqn:BACK; last by ins.
           rewrite mul1n -sum1_count.
-          rewrite big_mkcond [\sum_(i <- ts_interf | _ < _) _]big_mkcond /=.
+          rewrite big_mkcond [\sum_(i <- other_tasks | _ < _) _]big_mkcond /=.
           apply leq_sum_seq; move => tsk_k IN _.
           destruct (x tsk_k < delta); [rewrite andTb | by rewrite andFb].
           destruct (task_is_scheduled job_task sched tsk_k t) eqn:SCHED; last by done.
@@ -568,13 +582,13 @@ Module ResponseTimeAnalysisEDF.
         }
       Qed.
 
-      (* 6) Next, we prove that, for any interval delta, if the sum of per-task
-            interference exceeds delta * num_cpus, the same applies for the
-            sum of the minimum between the interference and delta. *)
+      (* 6) Based on lemma (5), we prove that, for any interval delta, if the sum of per-task
+            interference exceeds (delta * num_cpus), the same applies for the
+            sum of the minimum of the interference and delta. *)
       Lemma bertogna_edf_minimum_exceeds_interference :
         forall delta,
-          \sum_(tsk_k <- ts_interf) x tsk_k >= delta * num_cpus ->
-             \sum_(tsk_k <- ts_interf) minn (x tsk_k) delta >=
+          \sum_(tsk_k <- other_tasks) x tsk_k >= delta * num_cpus ->
+             \sum_(tsk_k <- other_tasks) minn (x tsk_k) delta >=
              delta * num_cpus.
       Proof.
         intros delta SUMLESS.
@@ -588,64 +602,63 @@ Module ResponseTimeAnalysisEDF.
           [| by red; ins; rewrite ltnNge
            | by intros i COND; rewrite -ltnNge in COND; rewrite COND].
 
-        (* Case 1: cardGE = 0 *)
-        destruct (~~ has (fun i => delta <= x i) ts_interf) eqn:HASa.
+        (* Case 1: num_tasks_exceeding = 0 *)
+        destruct (~~ has (fun i => delta <= x i) other_tasks) eqn:HASa.
         {
           rewrite [\sum_(_ <- _ | _ <= _) _]big_hasC; last by apply HASa.
           rewrite big_seq_cond; move: HASa => /hasPn HASa.
-          rewrite add0n (eq_bigl (fun i => (i \in ts_interf) && true));
-            last by red; intros tsk_k; destruct (tsk_k \in ts_interf) eqn:INk;
+          rewrite add0n (eq_bigl (fun i => (i \in other_tasks) && true));
+            last by red; intros tsk_k; destruct (tsk_k \in other_tasks) eqn:INk;
               [by rewrite andTb ltnNge; apply HASa | by rewrite andFb].
           by rewrite -big_seq_cond.
         } apply negbFE in HASa.
         
-        (* Case 2: cardGE >= num_cpus *)
-        destruct (cardGE delta >= num_cpus) eqn:CARD.
+        (* Case 2: num_tasks_exceeding >= num_cpus *)
+        destruct (num_tasks_exceeding delta >= num_cpus) eqn:CARD.
         {
-          apply leq_trans with (delta * cardGE delta);
+          apply leq_trans with (delta * num_tasks_exceeding delta);
             first by rewrite leq_mul2l; apply/orP; right.
-          unfold cardGE; rewrite -sum1_count big_distrr /=.
+          unfold num_tasks_exceeding; rewrite -sum1_count big_distrr /=.
           rewrite -[\sum_(_ <- _ | _) _]addn0.
           by apply leq_add; [by apply leq_sum; ins; rewrite muln1|by ins].
         } apply negbT in CARD; rewrite -ltnNge in CARD.
 
-        (* Case 3: cardGE < num_cpus *)
-        rewrite big_const_seq iter_addn addn0; fold cardGE.
-        apply leq_trans with (n := delta * cardGE delta +
-                                   delta * (num_cpus - cardGE delta));
+        (* Case 3: num_tasks_exceeding < num_cpus *)
+        rewrite big_const_seq iter_addn addn0; fold num_tasks_exceeding.
+        apply leq_trans with (n := delta * num_tasks_exceeding delta +
+                                   delta * (num_cpus - num_tasks_exceeding delta));
           first by rewrite -mulnDr subnKC //; apply ltnW.
-        by rewrite leq_add2l; apply bertogna_edf_helper_lemma; apply/andP; split;
-          first by rewrite -has_count.
+        rewrite leq_add2l; apply bertogna_edf_interference_in_non_full_processors.
+        by apply/andP; split; first by rewrite -has_count.
       Qed.
 
-      (* 7) Now, we prove that the Bertogna's interference bound
-            is not enough to cover the sum of the "minimum" term over
-            all tasks (artifact of the proof by contradiction). *)
+      (* 7) Next, using lemmas (0), (4) and (6) we prove that the reduction-based
+            interference bound is not enough to cover the sum of the minima over all tasks
+            (artifact of the proof by contradiction). *)
       Lemma bertogna_edf_sum_exceeds_total_interference:
-        \sum_((tsk_other, R_other) <- rt_bounds | jldp_can_interfere_with tsk tsk_other)
+        \sum_((tsk_other, R_other) <- rt_bounds | other_task tsk_other)
           minn (x tsk_other) (R - task_cost tsk + 1) > I tsk R.
       Proof.
         have GE_COST := bertogna_edf_R_other_ge_cost.
         have EXCEEDS := bertogna_edf_minimum_exceeds_interference.
-        have ALLBUSY := bertogna_edf_all_cpus_busy.
+        have ALLBUSY := bertogna_edf_interference_on_all_cpus.
         have TOOMUCH := bertogna_edf_too_much_interference.
         rename H_rt_bounds_contains_all_tasks into UNZIP,
           H_response_time_is_fixed_point into REC.
-        apply leq_trans with (n := \sum_(tsk_other <- ts_interf) minn (x tsk_other) (R - task_cost tsk + 1));
+        apply leq_trans with (n := \sum_(tsk_other <- other_tasks) minn (x tsk_other) (R - task_cost tsk + 1));
           last first.
         {
           rewrite (eq_bigr (fun i => minn (x (fst i)) (R - task_cost tsk + 1)));
             last by ins; destruct i.
           move: UNZIP => UNZIP.
-          assert (FILTER: filter (jldp_can_interfere_with tsk) (unzip1 rt_bounds) =
-                          filter (jldp_can_interfere_with tsk) ts).
+          assert (FILTER: filter other_task (unzip1 rt_bounds) =
+                          filter other_task ts).
             by f_equal.
-          unfold ts_interf; rewrite -FILTER; clear FILTER.
+          unfold other_tasks; rewrite -FILTER; clear FILTER.
           rewrite -[\sum_(_ <- rt_bounds | _)_]big_filter.
           assert (SUBST: [seq i <- rt_bounds
-                 | let '(tsk_other, _) := i in
-                        jldp_can_interfere_with tsk tsk_other] = [seq i <- rt_bounds
-                           | jldp_can_interfere_with tsk (fst i)]).
+                           | let '(tsk_other, _) := i in other_task tsk_other] =
+                         [seq i <- rt_bounds | other_task (fst i)]).
           {
             by apply eq_filter; red; intro i; destruct i.
           } rewrite SUBST; clear SUBST.         
@@ -664,9 +677,11 @@ Module ResponseTimeAnalysisEDF.
         by rewrite leq_mul2r; apply/orP; right; apply TOOMUCH.
       Qed.
 
-      (* 8) After concluding that the sum of the minimum exceeds (R - e_i + 1),
-            we prove that there exists a tuple (tsk_k, R_k) such that
-            min (x_k, R - e_i + 1) > min (W_k, edf_bound, R - e_i + 1). *)
+      (* 8) After concluding that the sum of the minima exceeds (R - e_i + 1),
+            we prove that there exists a tuple (tsk_k, R_k) that satisfies
+            min (x_k, R - e_i + 1) > min (W_k, I_edf, R - e_i + 1).
+            This implies that either x_k > W_k or x_k > I_edf, which is a contradiction,
+            since both W_k and I_edf are valid task interference bounds. *)
       Lemma bertogna_edf_exists_task_that_exceeds_bound :
         exists tsk_other R_other,
           (tsk_other, R_other) \in rt_bounds /\
@@ -678,7 +693,7 @@ Module ResponseTimeAnalysisEDF.
         rename H_rt_bounds_contains_all_tasks into UNZIP.
         assert (HAS: has (fun tup : task_with_response_time =>
                             let (tsk_other, R_other) := tup in
-                            (tsk_other \in ts) && jldp_can_interfere_with tsk tsk_other &&
+                            (tsk_other \in ts) && other_task tsk_other &&
                               (minn (x tsk_other) (R - task_cost tsk + 1)  >
                               interference_bound tsk_other R_other))
                          rt_bounds).
diff --git a/analysis/basic/bertogna_fp_comp.v b/analysis/basic/bertogna_fp_comp.v
index dfa823f813dfcdf1e1ed3fd1a5abd4ba5a3c62e6..7208a9ee6eb20055852dc3077187d9a6f66444c1 100644
--- a/analysis/basic/bertogna_fp_comp.v
+++ b/analysis/basic/bertogna_fp_comp.v
@@ -48,7 +48,7 @@ Module ResponseTimeIterationFP.
         (fun t => task_cost tsk +
                   div_floor
                     (total_interference_bound_fp task_cost task_period tsk
-                                                R_prev t higher_priority)
+                                                R_prev t)
                     num_cpus)
         (task_cost tsk).
 
@@ -75,12 +75,12 @@ Module ResponseTimeIterationFP.
     (* The response-time analysis for a given task set is defined
        as a left-fold (reduce) based on the function above.
        This either returns a list of task and response-time bounds, or None. *)
-    Definition fp_claimed_bounds (ts: taskset_of sporadic_task) :=
+    Definition fp_claimed_bounds (ts: seq sporadic_task) :=
       foldl fp_bound_of_task (Some [::]) ts.
 
     (* The schedulability test simply checks if we got a list of
        response-time bounds (i.e., if the computation did not fail). *)
-    Definition fp_schedulable (ts: taskset_of sporadic_task) :=
+    Definition fp_schedulable (ts: seq sporadic_task) :=
       fp_claimed_bounds ts != None.
     
     (* In the following section, we prove several helper lemmas about the
@@ -258,7 +258,7 @@ Module ResponseTimeIterationFP.
         forall tsk rt_bounds,
           task_cost tsk +
            div_floor (total_interference_bound_fp task_cost task_period tsk rt_bounds
-                     (per_task_rta tsk rt_bounds (max_steps tsk)) higher_priority) num_cpus
+                     (per_task_rta tsk rt_bounds (max_steps tsk))) num_cpus
           = per_task_rta tsk rt_bounds (max_steps tsk).+1.
       Proof.
           by done.
@@ -273,14 +273,13 @@ Module ResponseTimeIterationFP.
       (* Consider a list of previous tasks and a task tsk to be analyzed. *)
       Variable ts: taskset_of sporadic_task.
 
-      (* Assume that the task set doesn't contain duplicates and is sorted by priority, ... *)
-      Hypothesis H_task_set_is_a_set: uniq ts.
+      (* Assume that the task set is sorted by unique priorities, ... *)
       Hypothesis H_task_set_is_sorted: sorted higher_priority ts.
+      Hypothesis H_task_set_has_unique_priorities:
+        FP_is_antisymmetric_over_task_set higher_priority ts.
 
-      (* ...the priority order is strict (<), ...*)
-      Hypothesis H_priority_irreflexive: irreflexive higher_priority.
-      Hypothesis H_priority_transitive: transitive higher_priority.
-      Hypothesis H_priority_antissymetric: antisymmetric higher_priority.
+      (* ...the priority order is transitive, ...*)
+      Hypothesis H_priority_transitive: FP_is_transitive higher_priority.
       
       (* ... and that the response-time analysis succeeds. *)
       Variable hp_bounds: seq task_with_response_time.
@@ -290,71 +289,30 @@ Module ResponseTimeIterationFP.
       (* Let's refer to tasks by index. *)
       Variable elem: sporadic_task.
       Let TASK := nth elem ts.
-                    
-      (* Then, the tasks in the prefix of fp_claimed_bounds are exactly interfering tasks
-         under FP scheduling.*)
-      Lemma fp_claimed_bounds_interf:
-        forall idx,
+
+      (* We prove that higher-priority tasks have smaller index. *)
+      Lemma fp_claimed_bounds_hp_tasks_have_smaller_index :
+        forall hp_idx idx,
+          hp_idx < size ts ->
           idx < size ts ->
-          [seq tsk_hp <- ts | fp_can_interfere_with higher_priority (TASK idx) tsk_hp] = take idx ts.
+          hp_idx != idx ->
+          higher_priority (TASK hp_idx) (TASK idx) ->
+          hp_idx < idx.
       Proof.
-        rename H_task_set_is_sorted into SORT,
-               H_task_set_is_a_set into UNIQ,
-               H_priority_antissymetric into ANTI,
-               H_priority_irreflexive into IRR.
-        induction idx.
-        {
-          intros LT.
-          destruct ts as [| tsk0 ts']; [by done | simpl in SORT].
-          unfold fp_can_interfere_with; rewrite /= eq_refl andbF.
-          apply eq_trans with (y := filter pred0 ts');
-            last by apply filter_pred0.
-          apply eq_in_filter; red; intros x INx; rewrite /TASK /=.
-          destruct (x != tsk0) eqn:SAME; rewrite ?andbT ?andbF //.
-          apply negbTE; apply/negP; unfold not; intro HP.
-          move: SAME => /eqP SAME; apply SAME; clear SAME.
-          apply ANTI; apply/andP; split; first by done.
-          apply order_path_min in SORT; last by done.
-          by move: SORT => /allP SORT; apply SORT.
-        }
-        {
-          intros LT.
-          generalize LT; intro LT'; apply ltSnm in LT.
-          feed IHidx; first by done.
-          rewrite -filter_idx_le_takeS //.
-          apply eq_in_filter; red; intros x INx.
-          unfold fp_can_interfere_with.
-          generalize INx; intro SUBST; apply nth_index with (x0 := elem) in SUBST.
-          rewrite -SUBST; clear SUBST.
-          rewrite index_uniq; [ | by rewrite index_mem | by done].
-          apply/idP/idP.
-          {
-            move => /andP [HP DIFF].
-            unfold TASK in *.
-            apply sorted_uniq_rel_implies_le_idx in HP; try (by done);
-              last by rewrite index_mem.
-            by rewrite leq_eqVlt in HP; move: HP => /orP [/eqP SAME | LESS];
-                first by rewrite SAME eq_refl in DIFF.
-          }
-          {
-            intros LEidx; apply/andP; split;
-              first by apply sorted_lt_idx_implies_rel.
-            apply/eqP; red; intro BUG.
-            eapply f_equal with (f := fun x => index x ts) in BUG.
-            rewrite nth_index in BUG; last by done.
-            rewrite BUG in LEidx.
-            by rewrite index_uniq // ltnn in LEidx.
-          }
-        }
+        unfold TASK; clear TASK.
+        rename ts into ts'; destruct ts' as [ts UNIQ]; simpl in *.
+        intros hp_idx idx LThp LT NEQ HP.
+        rewrite ltn_neqAle; apply/andP; split; first by done.
+        by apply sorted_rel_implies_le_idx with (leT := higher_priority) (s := ts) (x0 := elem).
       Qed.
-      
+
     End HighPriorityTasks.
 
     (* In this section, we show that the fixed-point iteration converges. *)
     Section Convergence.
 
-      (* Consider any set of higher-priority tasks. *)
-      Variable ts_hp: taskset_of sporadic_task.
+      (* Consider any list of higher-priority tasks. *)
+      Variable ts_hp: list sporadic_task.
 
       (* Assume that the response-time analysis succeeds for the higher-priority tasks. *)
       Variable rt_bounds: seq task_with_response_time.
@@ -384,15 +342,14 @@ Module ResponseTimeIterationFP.
         apply fun_mon_iter_mon; [by ins | by ins; apply leq_addr |].
         clear LEx x1 x2; intros x1 x2 LEx.
         unfold div_floor, total_interference_bound_fp.
-        rewrite big_seq_cond [\sum_(i <- _ | let '(tsk_other, _) := i in
-                                 _ && (tsk_other != tsk))_]big_seq_cond.
-        rewrite leq_add2l leq_div2r // leq_sum //.
-
-        intros i; destruct (i \in rt_bounds) eqn:HP; last by rewrite andFb.
-        destruct i as [i R]; intros _.
-        have GE_COST := fp_claimed_bounds_ge_cost ts_hp rt_bounds i R SOME.
+        rewrite big_seq_cond.
+        rewrite [\sum_(_ <- _ | true) _]big_seq_cond.
+        rewrite leq_add2l leq_div2r //.
+        apply leq_sum; move => i /andP [IN _].
+        destruct i as [i R].
+        have GE_COST := fp_claimed_bounds_ge_cost ts_hp rt_bounds i R SOME IN.
         have UNZIP := fp_claimed_bounds_unzip ts_hp rt_bounds SOME.
-        assert (IN: i \in ts_hp).
+        assert (IN': i \in ts_hp).
         {
           by rewrite -UNZIP; apply/mapP; exists (i,R).
         }
@@ -400,8 +357,8 @@ Module ResponseTimeIterationFP.
         rewrite leq_min; apply/andP; split.
         {
           apply leq_trans with (n := W task_cost task_period i R x1); first by apply geq_minl.
-            exploit (VALID i); [by rewrite mem_rcons in_cons IN orbT | ins; des].
-            by apply W_monotonic; try by ins; apply GE_COST.
+          exploit (VALID i); [by rewrite mem_rcons in_cons IN' orbT | ins; des].
+          by apply W_monotonic; try (by done); apply GE_COST.
         }
         {
           apply leq_trans with (n := x1 - task_cost tsk + 1); first by apply geq_minr.
@@ -514,28 +471,22 @@ Module ResponseTimeIterationFP.
       (* Consider a task set ts. *)
       Variable ts: taskset_of sporadic_task.
       
-      (* Assume that higher_priority is a total strict order (<).
-         TODO: it doesn't have to be total over the universe of tasks,
-         but only within the task set. However, to weaken this hypothesis
-         we need to re-prove some lemmas from ssreflect. *)
-      Hypothesis H_irreflexive: irreflexive higher_priority.
-      Hypothesis H_transitive: transitive higher_priority.
-      Hypothesis H_unique_priorities: antisymmetric higher_priority.
-      Hypothesis H_total: total higher_priority.
-
-      (* Assume the task set has no duplicates, ... *)
-      Hypothesis H_ts_is_a_set: uniq ts.
-
-      (* ...all tasks have valid parameters, ... *)
+      (* Assume that all tasks have valid parameters, ... *)
       Hypothesis H_valid_task_parameters:
         valid_sporadic_taskset task_cost task_period task_deadline ts.
 
-      (* ...constrained deadlines, ...*)
+      (* ...constrained deadlines.*)
       Hypothesis H_constrained_deadlines:
         forall tsk, tsk \in ts -> task_deadline tsk <= task_period tsk.
 
-      (* ...and tasks are ordered by increasing priorities. *)
-      Hypothesis H_sorted_ts: sorted higher_priority ts.
+      (* Assume that the task set is totally ordered by unique priorities,
+         and that the priority order is transitive. *)
+      Hypothesis H_task_set_is_sorted: sorted higher_priority ts.
+      Hypothesis H_task_set_has_unique_priorities:
+        FP_is_antisymmetric_over_task_set higher_priority ts.
+      Hypothesis H_priority_is_total:
+        FP_is_total_over_task_set higher_priority ts.
+      Hypothesis H_priority_transitive: FP_is_transitive higher_priority.
 
       (* Next, consider any arrival sequence such that...*)
       Context {arr_seq: arrival_sequence Job}.
@@ -552,11 +503,10 @@ Module ResponseTimeIterationFP.
       (* ... and satisfy the sporadic task model.*)
       Hypothesis H_sporadic_tasks:
         sporadic_task_model task_period arr_seq job_task.
-      
+
       (* Then, consider any platform with at least one CPU such that...*)
       Variable sched: schedule num_cpus arr_seq.
-      Hypothesis H_at_least_one_cpu :
-        num_cpus > 0.
+      Hypothesis H_at_least_one_cpu: num_cpus > 0.
 
       (* ...jobs only execute after they arrived and no longer
          than their execution costs. *)
@@ -658,11 +608,37 @@ Module ResponseTimeIterationFP.
               (higher_eq_priority := higher_priority); try (by done).
         {
           cut (NTH idx \in hp_bounds = true); [intros IN | by apply mem_nth].
-          by rewrite -UNZIP; apply/mapP; exists (TASK idx, RESP idx); rewrite PAIR.
+          by rewrite set_mem -UNZIP; apply/mapP; exists (TASK idx, RESP idx); rewrite PAIR.
         }
         {
-          unfold unzip1 in *; rewrite map_take UNZIP SUBST //.
-          by apply fp_claimed_bounds_interf with (hp_bounds := hp_bounds); rewrite -?SIZE.
+          intros hp_tsk IN INTERF.
+          exists (RESP (index hp_tsk ts)).
+          move: (IN) => INDEX; apply nth_index with (x0 := tsk) in INDEX.
+          rewrite -{1}[hp_tsk]INDEX -SUBST; last by rewrite SIZE index_mem.
+          assert (UNIQ: uniq hp_bounds).
+          {
+            apply map_uniq with (f := fst); unfold unzip1 in *; rewrite UNZIP.
+            by destruct ts.
+          }
+          rewrite -filter_idx_lt_take //.
+          {
+            rewrite PAIR mem_filter; apply/andP; split;
+              last by apply mem_nth; rewrite SIZE index_mem.
+            {
+              rewrite /NTH index_uniq; [| by rewrite SIZE index_mem | by done ].
+              {
+                move: INTERF => /andP [HP NEQ].
+                apply fp_claimed_bounds_hp_tasks_have_smaller_index with
+                  (ts := ts) (elem := tsk) (hp_bounds := hp_bounds);
+                  try (by done);
+                  [by rewrite index_mem | by rewrite -SIZE | | by rewrite INDEX -SUBST].
+                apply/eqP; intro BUG; subst idx.
+                rewrite SUBST -{1}INDEX in NEQ;
+                  first by rewrite eq_refl in NEQ.
+                by rewrite SIZE index_mem INDEX.
+              }
+            }
+          }
         }
         {
           intros hp_tsk R_hp IN; apply mem_take in IN.
@@ -701,7 +677,7 @@ Module ResponseTimeIterationFP.
         feed (UNZIP rt_bounds); first by done.
         assert (EX: exists R, (tsk, R) \in rt_bounds).
         {
-          rewrite -UNZIP in INtsk; move: INtsk => /mapP EX.
+          rewrite set_mem -UNZIP in INtsk; move: INtsk => /mapP EX.
           by destruct EX as [p]; destruct p as [tsk' R]; simpl in *; subst tsk'; exists R.
         } des.
         exploit (RLIST tsk R); [by ins | by apply JOBtsk | intro COMPLETED].
diff --git a/analysis/basic/bertogna_fp_theory.v b/analysis/basic/bertogna_fp_theory.v
index 250e35f2efb9053d3e0b9d49a694c2b3156c1f4f..637f1f5f439908a43e8cdfa9150dc698fc5022d9 100644
--- a/analysis/basic/bertogna_fp_theory.v
+++ b/analysis/basic/bertogna_fp_theory.v
@@ -1,6 +1,6 @@
 Require Import rt.util.all.
 Require Import rt.model.basic.task rt.model.basic.job rt.model.basic.task_arrival
-               rt.model.basic.schedule rt.model.basic.platform rt.model.basic.platform_fp
+               rt.model.basic.schedule rt.model.basic.platform rt.model.basic.constrained_deadlines
                rt.model.basic.workload rt.model.basic.schedulability rt.model.basic.priority
                rt.model.basic.response_time rt.model.basic.interference.
 Require Import rt.analysis.basic.workload_bound rt.analysis.basic.interference_bound_fp.
@@ -8,9 +8,14 @@ From mathcomp Require Import ssreflect ssrbool eqtype ssrnat seq fintype bigop d
 
 Module ResponseTimeAnalysisFP.
 
-  Export Job SporadicTaskset ScheduleOfSporadicTask Workload Interference InterferenceBoundFP
-         Platform PlatformFP Schedulability ResponseTime Priority SporadicTaskArrival WorkloadBound.
+  Export Job SporadicTaskset ScheduleOfSporadicTask Workload Interference
+         InterferenceBoundFP Platform Schedulability ResponseTime
+         Priority SporadicTaskArrival WorkloadBound ConstrainedDeadlines.
 
+  (* In this section, we prove that any fixed point in Bertogna and
+     Cirinei's RTA for FP scheduling is a safe response-time bound.
+     This analysis can be found in Chapter 18.2 of Baruah et al.'s
+     book Multiprocessor Scheduling for Real-time Systems. *)
   Section ResponseTimeBound.
 
     Context {sporadic_task: eqType}.
@@ -33,46 +38,56 @@ Module ResponseTimeAnalysisFP.
       forall (j: JobIn arr_seq),
         valid_sporadic_job task_cost task_deadline job_cost job_deadline job_task j.
 
-    (* Consider any schedule such that...*)
+    (* Assume that we have a task set where all tasks have valid
+       parameters and constrained deadlines, ... *)
+    Variable ts: taskset_of sporadic_task.
+    Hypothesis H_valid_task_parameters:
+      valid_sporadic_taskset task_cost task_period task_deadline ts.
+    Hypothesis H_constrained_deadlines:
+      forall tsk, tsk \in ts -> task_deadline tsk <= task_period tsk.
+
+    (* ... and that all jobs in the arrival sequence come from the task set. *)
+    Hypothesis H_all_jobs_from_taskset:
+      forall (j: JobIn arr_seq), job_task j \in ts.
+
+    (* Next, consider any schedule such that...*)
     Variable num_cpus: nat.
     Variable sched: schedule num_cpus arr_seq.
 
-    (* ...jobs do not execute before their arrival times nor longer
-       than their execution costs. *)
+    (* ...jobs are sequential and do not execute before their
+       arrival times nor longer than their execution costs. *)
+    Hypothesis H_sequential_jobs: sequential_jobs sched.
     Hypothesis H_jobs_must_arrive_to_execute:
       jobs_must_arrive_to_execute sched.
     Hypothesis H_completed_jobs_dont_execute:
       completed_jobs_dont_execute job_cost sched.
 
-    (* Also assume that jobs are sequential and that there exists
-       at least one processor. *)
-    Hypothesis H_sequential_jobs: sequential_jobs sched.
+    (* Assume that there exists at least one processor. *)
     Hypothesis H_at_least_one_cpu: num_cpus > 0.
 
-    (* Consider a task set ts with no duplicates where all jobs
-       come from the task set and all tasks have valid parameters
-       and constrained deadlines. *)
-    Variable ts: taskset_of sporadic_task.
-    Hypothesis H_ts_is_a_set: uniq ts.
-    Hypothesis H_all_jobs_from_taskset:
-      forall (j: JobIn arr_seq), job_task j \in ts.
-    Hypothesis H_valid_task_parameters:
-      valid_sporadic_taskset task_cost task_period task_deadline ts.
-    Hypothesis H_constrained_deadlines:
-      forall tsk, tsk \in ts -> task_deadline tsk <= task_period tsk.
+    (* Consider a given FP policy, ... *)
+    Variable higher_eq_priority: FP_policy sporadic_task.
 
-    (* Next, consider a task tsk that is to be analyzed. *)
-    Variable tsk: sporadic_task.
-    Hypothesis task_in_ts: tsk \in ts.
+    (* ... and assume that the schedule is a work-conserving
+       schedule that enforces this policy. *)
+    Hypothesis H_work_conserving: work_conserving job_cost sched.
+    Hypothesis H_enforces_FP_policy:
+      enforces_FP_policy job_cost job_task sched higher_eq_priority.
 
+    (* Let's define some local names to avoid passing many parameters. *)    
     Let no_deadline_is_missed_by_tsk (tsk: sporadic_task) :=
       task_misses_no_deadline job_cost job_deadline job_task sched tsk.
     Let response_time_bounded_by (tsk: sporadic_task) :=
       is_response_time_bound_of_task job_cost job_task tsk sched.
 
-    (* Assume a given FP policy. *)
-    Variable higher_eq_priority: FP_policy sporadic_task.
-    Let can_interfere_with_tsk := fp_can_interfere_with higher_eq_priority tsk.
+    (* Next, we consider the response-time recurrence.
+       Let tsk be a task in ts that is to be analyzed. *)
+    Variable tsk: sporadic_task.
+    Hypothesis task_in_ts: tsk \in ts.
+
+    (* Let is_hp_task denote whether a task is a higher-priority task
+       (with respect to tsk). *)
+    Let is_hp_task := higher_priority_task higher_eq_priority tsk.
 
     (* Assume a response-time bound is known... *)
     Let task_with_response_time := (sporadic_task * time)%type.
@@ -82,39 +97,36 @@ Module ResponseTimeAnalysisFP.
         (hp_tsk, R) \in hp_bounds ->
         response_time_bounded_by hp_tsk R.
     
-    (* ... for any task in ts that interferes with tsk. *)
+    (* ... for every higher-priority task. *)
     Hypothesis H_hp_bounds_has_interfering_tasks:
-      [seq tsk_hp <- ts | can_interfere_with_tsk tsk_hp] = unzip1 hp_bounds.
+      forall hp_tsk,
+        hp_tsk \in ts ->
+        is_hp_task hp_tsk ->
+          exists R, (hp_tsk, R) \in hp_bounds.
 
     (* Assume that the response-time bounds are larger than task costs. *)
     Hypothesis H_response_time_bounds_ge_cost:
       forall hp_tsk R,
         (hp_tsk, R) \in hp_bounds -> R >= task_cost hp_tsk.
     
-    (* Assume that no deadline is missed by any interfering task, i.e.,
-       response-time bound R_other <= deadline. *)
+    (* Assume that no deadline is missed by any higher-priority task. *)
     Hypothesis H_interfering_tasks_miss_no_deadlines:
       forall hp_tsk R,
         (hp_tsk, R) \in hp_bounds -> R <= task_deadline hp_tsk.
 
-    (* Assume that the scheduler is work-conserving and enforces the FP policy. *)
-    Hypothesis H_work_conserving: work_conserving job_cost sched.
-    Hypothesis H_enforces_FP_policy:
-      enforces_FP_policy job_cost job_task sched higher_eq_priority.
-    
     (* Let R be the fixed point of Bertogna and Cirinei's recurrence, ...*)
     Variable R: time.
     Hypothesis H_response_time_recurrence_holds :
       R = task_cost tsk +
           div_floor
-            (total_interference_bound_fp task_cost task_period tsk hp_bounds R higher_eq_priority)
+            (total_interference_bound_fp task_cost task_period tsk hp_bounds R)
             num_cpus.
 
     (* ... and assume that R is no larger than the deadline of tsk.*)
     Hypothesis H_response_time_no_larger_than_deadline:
       R <= task_deadline tsk.
 
-    (* In order to prove that R is a response-time bound, we first present some lemmas. *)
+    (* In order to prove that R is a response-time bound, we first provide some lemmas. *)
     Section Lemmas.
 
       (* Consider any job j of tsk. *)
@@ -134,17 +146,18 @@ Module ResponseTimeAnalysisFP.
         task_interference job_cost job_task sched j tsk_other
                           (job_arrival j) (job_arrival j + R).
 
-      (* and X the total interference incurred by job j due to any task. *)
+      (* ...and X the total interference incurred by job j due to any task. *)
       Let X := total_interference job_cost sched j (job_arrival j) (job_arrival j + R).
 
       (* Recall Bertogna and Cirinei's workload bound. *)
       Let workload_bound (tsk_other: sporadic_task) (R_other: time) :=
         W task_cost task_period tsk_other R_other R.
 
-      (* Also, let ts_interf be the subset of tasks that interfere with tsk. *)
-      Let ts_interf := [seq tsk_other <- ts | can_interfere_with_tsk tsk_other].
+      (* Let hp_tasks denote the set of higher-priority tasks. *)
+      Let hp_tasks := [seq tsk_other <- ts | is_hp_task tsk_other].
 
-      Section LemmasAboutInterferingTasks.
+      (* Now we establish results about the higher-priority tasks. *)
+      Section LemmasAboutHPTasks.
         
         (* Let (tsk_other, R_other) be any pair of higher-priority task and
            response-time bound computed in previous iterations. *)
@@ -152,30 +165,6 @@ Module ResponseTimeAnalysisFP.
         Variable R_other: time.
         Hypothesis H_response_time_of_tsk_other: (tsk_other, R_other) \in hp_bounds.
 
-        (* Note that tsk_other is in task set ts ...*)
-        Lemma bertogna_fp_tsk_other_in_ts: tsk_other \in ts.
-          Proof.
-            rename H_hp_bounds_has_interfering_tasks into UNZIP,
-                   H_response_time_of_tsk_other into INbounds.
-            move: UNZIP => UNZIP.
-            cut (tsk_other \in ts_interf = true);
-              first by rewrite mem_filter; move => /andP [_ IN].
-            unfold ts_interf; rewrite UNZIP.
-            by apply/mapP; exists (tsk_other, R_other).
-        Qed.
-
-        (*... and interferes with tsk. *)
-        Lemma bertogna_fp_tsk_other_interferes: can_interfere_with_tsk tsk_other.
-          Proof.
-            rename H_hp_bounds_has_interfering_tasks into UNZIP,
-                   H_response_time_of_tsk_other into INbounds.
-            move: UNZIP => UNZIP.
-            cut (tsk_other \in ts_interf = true);
-              first by rewrite mem_filter; move => /andP [INTERF _].
-            unfold ts_interf; rewrite UNZIP.
-            by apply/mapP; exists (tsk_other, R_other).
-        Qed.
-
         (* Since tsk_other cannot interfere more than it executes, we show that
            the interference caused by tsk_other is no larger than workload bound W. *)
         Lemma bertogna_fp_workload_bounds_interference :
@@ -184,13 +173,34 @@ Module ResponseTimeAnalysisFP.
           unfold response_time_bounded_by, is_response_time_bound_of_task,
                  completed, completed_jobs_dont_execute, valid_sporadic_job in *.
           rename H_valid_job_parameters into PARAMS,
+                 H_all_jobs_from_taskset into FROMTS,
                  H_valid_task_parameters into TASK_PARAMS,
                  H_constrained_deadlines into RESTR,
                  H_response_time_of_interfering_tasks_is_known into RESP,
                  H_interfering_tasks_miss_no_deadlines into NOMISS,
                  H_response_time_bounds_ge_cost into GE_COST.
           unfold x, workload_bound.
-          have INts := bertogna_fp_tsk_other_in_ts.
+          destruct ([exists t: 'I_(job_arrival j + R),
+                       task_is_scheduled job_task sched tsk_other t]) eqn: SCHED;
+            last first.
+          {
+            apply negbT in SCHED; rewrite negb_exists in SCHED.
+            move: SCHED => /forallP SCHED.
+            apply leq_trans with (n := 0); last by done.
+            apply leq_trans with (n := \sum_(job_arrival j <= t < job_arrival j + R) 0);
+              last by rewrite big1.
+            apply leq_sum_nat; move => i /andP [_ LTi] _.
+            specialize (SCHED (Ordinal LTi)).
+            rewrite negb_exists in SCHED; move: SCHED => /forallP SCHED.
+            rewrite big1 //; intros cpu _.
+            specialize (SCHED cpu); apply negbTE in SCHED.
+            by rewrite SCHED andbF.
+          }
+          move: SCHED => /existsP [t /existsP [cpu SCHED]].
+          unfold task_scheduled_on in SCHED.
+          destruct (sched cpu t) as [j0 |]; last by done.
+          assert (INts: tsk_other \in ts).
+            by move: SCHED => /eqP <-; rewrite FROMTS.
           apply leq_trans with (n := workload job_task sched tsk_other
                                               (job_arrival j) (job_arrival j + R));
             first by apply task_interference_le_workload.
@@ -204,7 +214,7 @@ Module ResponseTimeAnalysisFP.
               | by ins; apply RESP with (hp_tsk := tsk_other)].
         Qed.
 
-      End LemmasAboutInterferingTasks.
+      End LemmasAboutHPTasks.
 
       (* Next we prove some lemmas that help to derive a contradiction.*)
       Section DerivingContradiction.
@@ -254,68 +264,11 @@ Module ResponseTimeAnalysisFP.
           by apply completion_monotonic with (t := i); try (by done); apply ltnW.
         Qed.
 
-        Let scheduled_task_other_than_tsk (t: time) (tsk_other: sporadic_task) :=
-          task_is_scheduled job_task sched tsk_other t &&
-          can_interfere_with_tsk tsk_other.
-        
-        (* 1) At all times that j is backlogged, all processors are busy with other tasks. *)
-        Lemma bertogna_fp_scheduling_invariant:
-          forall t,
-            job_arrival j <= t < job_arrival j + R ->
-            backlogged job_cost sched j t ->
-            count (scheduled_task_other_than_tsk t) ts = num_cpus.
-        Proof.
-          rename H_valid_task_parameters into PARAMS,
-                 H_all_jobs_from_taskset into FROMTS,
-                 H_job_of_tsk into JOBtsk,
-                 H_sporadic_tasks into SPO,
-                 H_valid_job_parameters into JOBPARAMS,
-                 H_constrained_deadlines into RESTR,
-                 H_hp_bounds_has_interfering_tasks into UNZIP,
-                 H_interfering_tasks_miss_no_deadlines into NOMISS,
-                 H_response_time_of_interfering_tasks_is_known into PREV.
-          unfold sporadic_task_model, is_response_time_bound_of_task in *.
-          move => t /andP [LEt LTt] BACK.
-          apply platform_fp_cpus_busy_with_interfering_tasks with (task_cost0 := task_cost)
-          (task_period0 := task_period) (task_deadline0 := task_deadline) (job_task0 := job_task)
-          (ts0 := ts) (tsk0 := tsk) (higher_eq_priority0 := higher_eq_priority) in BACK;
-            try (by done); first by apply PARAMS.
-          {
-            apply leq_trans with (n := job_arrival j + R); first by done.
-            rewrite leq_add2l.
-            by apply leq_trans with (n := task_deadline tsk); last by apply RESTR.
-          }      
-          {
-            intros j_other tsk_other JOBother INTERF.
-            move: UNZIP => UNZIP.
-            cut (tsk_other \in unzip1 hp_bounds = true); last first.
-            {
-              rewrite -UNZIP mem_filter; apply/andP; split; first by done.
-              by rewrite -JOBother; apply FROMTS.
-            } intro IN.
-            move: IN => /mapP [p IN EQ]; destruct p as [tsk' R']; simpl in *; subst tsk'.
-            apply completion_monotonic with (t0 := job_arrival j_other + R'); try (by done);
-              last by apply PREV with (hp_tsk := tsk_other).
-            {
-              rewrite leq_add2l.
-              apply leq_trans with (n := task_deadline tsk_other); first by apply NOMISS.
-              apply RESTR.
-              cut (tsk_other \in unzip1 hp_bounds = true);
-                last by apply/mapP; exists (tsk_other, R').
-              by rewrite -UNZIP mem_filter; move => /andP [_ IN'].
-            }
-          }
-          {
-            ins; apply completion_monotonic with (t0 := job_arrival j0 + R); try (by done);
-              last by apply H_previous_jobs_of_tsk_completed.
-            rewrite leq_add2l.
-            by apply leq_trans with (n := task_deadline tsk); last by apply RESTR.
-          }
-        Qed.
-
-
-        (* 2) Prove that during the scheduling window of j, any job that is scheduled while j is
-           backlogged comes from a different task. *)
+        (* 1) Next, we prove that during the scheduling window of j, any job that is
+              scheduled while j is backlogged comes from a different task.
+              This follows from the fact that j is the first job not to complete
+              by its response-time bound, so previous jobs of j's task must have
+              completed by their periods and cannot be pending. *)
         Lemma bertogna_fp_interference_by_different_tasks :
           forall t j_other,
             job_arrival j <= t < job_arrival j + R ->
@@ -365,12 +318,67 @@ Module ResponseTimeAnalysisFP.
             by red; intros EQtsk; subst j_other; rewrite /backlogged SCHED andbF in BACK.
           }
         Qed.
+
+        (* Let's define a predicate to identify the other tasks that are scheduled. *)
+        Let other_scheduled_task (t: time) (tsk_other: sporadic_task) :=
+          task_is_scheduled job_task sched tsk_other t &&
+          is_hp_task tsk_other.
+      
+        (* 2) Now we prove that, at all times that j is backlogged, the number
+              of tasks other than tsk that are scheduled is exactly the number
+              of processors in the system. This is required to prove lemma (3). *)
+        Lemma bertogna_fp_all_cpus_are_busy:
+          forall t,
+            job_arrival j <= t < job_arrival j + R ->
+            backlogged job_cost sched j t ->
+            count (other_scheduled_task t) ts = num_cpus.
+        Proof.
+          rename H_valid_task_parameters into PARAMS,
+                 H_all_jobs_from_taskset into FROMTS,
+                 H_job_of_tsk into JOBtsk,
+                 H_sporadic_tasks into SPO,
+                 H_valid_job_parameters into JOBPARAMS,
+                 H_constrained_deadlines into RESTR,
+                 H_hp_bounds_has_interfering_tasks into HAS,
+                 H_interfering_tasks_miss_no_deadlines into NOMISS,
+                 H_response_time_of_interfering_tasks_is_known into PREV.
+          unfold sporadic_task_model, is_response_time_bound_of_task in *.
+          move => t /andP [LEt LTt] BACK.
+          apply platform_fp_cpus_busy_with_interfering_tasks with (task_cost0 := task_cost)
+          (task_period0 := task_period) (task_deadline0 := task_deadline) (job_task0 := job_task)
+          (ts0 := ts) (tsk0 := tsk) (higher_eq_priority0 := higher_eq_priority) in BACK;
+            try (by done); first by apply PARAMS.
+          {
+            apply leq_trans with (n := job_arrival j + R); first by done.
+            rewrite leq_add2l.
+            by apply leq_trans with (n := task_deadline tsk); last by apply RESTR.
+          }      
+          {
+            intros j_other tsk_other JOBother INTERF.
+            move: HAS => HAS.
+            feed (HAS tsk_other); first by rewrite -JOBother FROMTS.
+            move: (HAS INTERF) => [R' IN].
+            apply completion_monotonic with (t0 := job_arrival j_other + R'); try (by done);
+              last by apply PREV with (hp_tsk := tsk_other).
+            {
+              rewrite leq_add2l.
+              apply leq_trans with (n := task_deadline tsk_other); first by apply NOMISS.
+              by apply RESTR; rewrite -JOBother FROMTS.
+            }
+          }
+          {
+            ins; apply completion_monotonic with (t0 := job_arrival j0 + R); try (by done);
+              last by apply H_previous_jobs_of_tsk_completed.
+            rewrite leq_add2l.
+            by apply leq_trans with (n := task_deadline tsk); last by apply RESTR.
+          }
+        Qed.
         
-        (* 3) Next, we prove that the sum of the interference of each task is equal
-              to the total interference multiplied by the number of processors. This
-              holds because interference only occurs when all processors are busy. *)
-        Lemma bertogna_fp_all_cpus_busy :
-          \sum_(tsk_k <- ts_interf) x tsk_k = X * num_cpus.
+        (* 3) Now we prove that, at all times that j is backlogged, the number
+              of tasks other than tsk that are scheduled is exactly the number
+              of processors in the system. This is required to prove lemma (4). *)
+        Lemma bertogna_fp_interference_on_all_cpus :
+          \sum_(tsk_k <- hp_tasks) x tsk_k = X * num_cpus.
         Proof.
           have DIFFTASK := bertogna_fp_interference_by_different_tasks.
           rename H_work_conserving into WORK, H_enforces_FP_policy into FP,
@@ -387,7 +395,7 @@ Module ResponseTimeAnalysisFP.
           apply eq_trans with (y := \sum_(cpu < num_cpus) 1); last by simpl_sum_const.
           apply eq_bigr; intros cpu _.
           move: (WORK j t BACK cpu) => [j_other /eqP SCHED]; unfold scheduled_on in *.
-          rewrite (bigD1_seq (job_task j_other)) /=; last by rewrite filter_uniq.
+          rewrite (bigD1_seq (job_task j_other)) /=; last by rewrite filter_uniq; destruct ts.
           {
             rewrite (eq_bigr (fun i => 0));
               last by intros i DIFF; rewrite /task_scheduled_on SCHED;apply/eqP;rewrite eqb0 eq_sym.
@@ -395,7 +403,7 @@ Module ResponseTimeAnalysisFP.
             by unfold task_scheduled_on; rewrite SCHED.
           }
           rewrite mem_filter; apply/andP; split; last by apply FROMTS.
-          unfold can_interfere_with_tsk, fp_can_interfere_with; apply/andP; split.
+          apply/andP; split.
           {
             rewrite -JOBtsk; apply FP with (t := t); try by done.
             by apply/existsP; exists cpu; apply/eqP.
@@ -404,19 +412,20 @@ Module ResponseTimeAnalysisFP.
           by apply/existsP; exists cpu; apply/eqP.
         Qed.
 
-        (* Let (cardGE delta) be the number of interfering tasks whose interference
-           is larger than delta. *)
-        Let cardGE (delta: time) := count (fun i => x i >= delta) ts_interf.
+        (* Before stating the next lemma, let (num_tasks_exceeding delta) be the
+           number of interfering tasks whose interference x is larger than delta. *)
+        Let num_tasks_exceeding delta := count (fun i => x i >= delta) (hp_tasks).
 
-        (* 4) If there is at least one of such tasks (e.g., cardGE > 0), then the
-           cumulative interference caused by the complementary set of interfering
-           tasks fills at least (num_cpus - cardGE) processors. *)
-        Lemma bertogna_fp_helper_lemma :
+        (* 4) Now we prove that, for any delta, if (num_task_exceeding delta > 0), then the
+              cumulative interference caused by the complementary set of interfering tasks fills
+              the remaining, not-completely-full (num_cpus - num_tasks_exceeding delta)
+              processors. *)
+        Lemma bertogna_fp_interference_in_non_full_processors :
           forall delta,
-            0 < cardGE delta < num_cpus ->
-            \sum_(i <- ts_interf | x i < delta) x i >= delta * (num_cpus - cardGE delta).
+            0 < num_tasks_exceeding delta < num_cpus ->
+            \sum_(i <- hp_tasks | x i < delta) x i >= delta * (num_cpus - num_tasks_exceeding delta).
         Proof.
-          have INV := bertogna_fp_scheduling_invariant.
+          have INV := bertogna_fp_all_cpus_are_busy.
           rename H_all_jobs_from_taskset into FROMTS,
                  H_valid_task_parameters into PARAMS,
                  H_job_of_tsk into JOBtsk,
@@ -426,7 +435,7 @@ Module ResponseTimeAnalysisFP.
                  H_constrained_deadlines into CONSTR,
                  H_sequential_jobs into SEQ,
                  H_enforces_FP_policy into FP,
-                 H_hp_bounds_has_interfering_tasks into UNZIP,
+                 H_hp_bounds_has_interfering_tasks into HASHP,
                  H_interfering_tasks_miss_no_deadlines into NOMISSHP.
           unfold sporadic_task_model in *.
           move => delta /andP [HAS LT]. 
@@ -435,14 +444,14 @@ Module ResponseTimeAnalysisFP.
           set some_interference_A := fun t =>
             has (fun tsk_k => backlogged job_cost sched j t &&
                               (x tsk_k >= delta) &&
-                              task_is_scheduled job_task sched tsk_k t) ts_interf.
+                              task_is_scheduled job_task sched tsk_k t) hp_tasks.
           set total_interference_B := fun t =>
               backlogged job_cost sched j t *
               count (fun tsk_k => (x tsk_k < delta) &&
-                    task_is_scheduled job_task sched tsk_k t) ts_interf.
+                    task_is_scheduled job_task sched tsk_k t) hp_tasks.
 
           apply leq_trans with ((\sum_(job_arrival j <= t < job_arrival j + R)
-                                some_interference_A t) * (num_cpus - cardGE delta)).
+                                some_interference_A t) * (num_cpus - num_tasks_exceeding delta)).
           {
             rewrite leq_mul2r; apply/orP; right.
             move: HAS => /hasP HAS; destruct HAS as [tsk_a INa LEa].
@@ -510,7 +519,7 @@ Module ResponseTimeAnalysisFP.
                 by rewrite -SAMEjob; apply/existsP; exists cpu; apply/eqP.
               }
               {
-                assert (INTERF: fp_can_interfere_with higher_eq_priority tsk (job_task j1)).
+                assert (INTERF: is_hp_task (job_task j1)).
                 {
                   apply/andP; split; last by rewrite SAMEtsk.
                   rewrite -JOBtsk; apply FP with (t := t); first by done.
@@ -522,11 +531,10 @@ Module ResponseTimeAnalysisFP.
                   rewrite ?JOBtsk ?SAMEtsk //.
                 {
                   intros j0 tsk0 JOB0 INTERF0.
-                  assert (IN: tsk0 \in (unzip1 hp_bounds)).
-                    by rewrite -UNZIP mem_filter; apply/andP; split; last by rewrite -JOB0 FROMTS.
-                  move: IN => /mapP [p IN EQ]; destruct p as [tsk0' R0]; simpl in *; subst tsk0'.
+                  feed (HASHP tsk0); first by rewrite -JOB0 FROMTS.
+                  move: (HASHP INTERF0) => [R0 IN0].
                   apply completion_monotonic with (t0 := job_arrival j0 + R0); try (by done);
-                    last by eapply H_response_time_of_interfering_tasks_is_known; first by apply IN.
+                    last by eapply H_response_time_of_interfering_tasks_is_known; first by apply IN0.
                   rewrite leq_add2l.
                   by apply leq_trans with (n := task_deadline tsk0);
                     [by apply NOMISSHP | by apply CONSTR; rewrite -JOB0 FROMTS].
@@ -546,13 +554,13 @@ Module ResponseTimeAnalysisFP.
               [rewrite mul1n /= | by rewrite has_pred0 //].
 
             destruct (has (fun tsk_k : sporadic_task => (delta <= x tsk_k) &&
-                       task_is_scheduled job_task sched tsk_k t) ts_interf) eqn:HAS';
+                       task_is_scheduled job_task sched tsk_k t) hp_tasks) eqn:HAS';
               last by done.
             rewrite mul1n; move: HAS => /hasP [tsk_k INk LEk].
-            unfold cardGE.
+            unfold num_tasks_exceeding.
             apply leq_trans with (n := num_cpus -
                          count (fun i => (x i >= delta) &&
-                            task_is_scheduled job_task sched i t) ts_interf).
+                            task_is_scheduled job_task sched i t) hp_tasks).
             {
               apply leq_sub2l.
               rewrite -2!sum1_count big_mkcond /=.
@@ -561,11 +569,11 @@ Module ResponseTimeAnalysisFP.
               by destruct (task_is_scheduled job_task sched i t);
                 [by rewrite andbT | by rewrite andbF].
             }
-            rewrite -count_filter -[count _ ts_interf]count_filter.
+            rewrite -count_filter -[count _ hp_tasks]count_filter.
             eapply leq_trans with (n := count (predC (fun tsk => delta <= x tsk)) _);
               last by apply eq_leq, eq_in_count; red; ins; rewrite ltnNge.
             rewrite leq_subLR count_predC size_filter.
-            by apply leq_trans with (n := count (scheduled_task_other_than_tsk t) ts);
+            apply leq_trans with (n := count (other_scheduled_task t) ts);
               [by rewrite INV | by rewrite count_filter].
           }
           {
@@ -573,7 +581,7 @@ Module ResponseTimeAnalysisFP.
             rewrite exchange_big /=; apply leq_sum; intros t _.
             destruct (backlogged job_cost sched j t) eqn:BACK; last by ins.
             rewrite mul1n -sum1_count.
-            rewrite big_mkcond [\sum_(i <- ts_interf | _ < _) _]big_mkcond /=.
+            rewrite big_mkcond [\sum_(i <- hp_tasks | _ < _) _]big_mkcond /=.
             apply leq_sum_seq; move => tsk_k IN _.
             destruct (x tsk_k < delta); [rewrite andTb | by rewrite andFb].
             destruct (task_is_scheduled job_task sched tsk_k t) eqn:SCHED; last by done.
@@ -582,13 +590,13 @@ Module ResponseTimeAnalysisFP.
           }
         Qed.
 
-        (* 4) Next, we prove that, for any interval delta, if the sum of per-task
-              interference exceeds delta * num_cpus, the same applies for the
-              sum of the minimum between the interference and delta. *)
+        (* 5) Based on lemma (4), we prove that, for any interval delta, if the sum of per-task
+              interference exceeds (delta * num_cpus), the same applies for the
+              sum of the minimum of the interference and delta. *)
         Lemma bertogna_fp_minimum_exceeds_interference :
           forall delta,
-            \sum_(tsk_k <- ts_interf) x tsk_k >= delta * num_cpus ->
-               \sum_(tsk_k <- ts_interf) minn (x tsk_k) delta >=
+            \sum_(tsk_k <- hp_tasks) x tsk_k >= delta * num_cpus ->
+               \sum_(tsk_k <- hp_tasks) minn (x tsk_k) delta >=
                delta * num_cpus.
         Proof.
           intros delta SUMLESS.
@@ -602,58 +610,63 @@ Module ResponseTimeAnalysisFP.
             [| by red; ins; rewrite ltnNge
              | by intros i COND; rewrite -ltnNge in COND; rewrite COND].
 
-          (* Case 1: cardGE = 0 *)
-          destruct (~~ has (fun i => delta <= x i) ts_interf) eqn:HASa.
+          (* Case 1: num_tasks_exceeding = 0 *)
+          destruct (~~ has (fun i => delta <= x i) hp_tasks) eqn:HASa.
           {
             rewrite [\sum_(_ <- _ | _ <= _) _]big_hasC; last by apply HASa.
             rewrite big_seq_cond; move: HASa => /hasPn HASa.
-            rewrite add0n (eq_bigl (fun i => (i \in ts_interf) && true));
-              last by red; intros tsk_k; destruct (tsk_k \in ts_interf) eqn:INk;
+            rewrite add0n (eq_bigl (fun i => (i \in hp_tasks) && true));
+              last by red; intros tsk_k; destruct (tsk_k \in hp_tasks) eqn:INk;
                 [by rewrite andTb ltnNge; apply HASa | by rewrite andFb].
             by rewrite -big_seq_cond.
           } apply negbFE in HASa.
 
-          (* Case 2: cardGE >= num_cpus *)
-          destruct (cardGE delta >= num_cpus) eqn:CARD.
+          (* Case 2: num_tasks_exceeding >= num_cpus *)
+          destruct (num_tasks_exceeding delta >= num_cpus) eqn:CARD.
           {
-            apply leq_trans with (delta * cardGE delta);
+            apply leq_trans with (delta * num_tasks_exceeding delta);
               first by rewrite leq_mul2l; apply/orP; right.
-            unfold cardGE; rewrite -sum1_count big_distrr /=.
+            unfold num_tasks_exceeding; rewrite -sum1_count big_distrr /=.
             rewrite -[\sum_(_ <- _ | _) _]addn0.
             by apply leq_add; [by apply leq_sum; ins; rewrite muln1|by ins].
           } apply negbT in CARD; rewrite -ltnNge in CARD.
 
-          (* Case 3: cardGE < num_cpus *)
-          rewrite big_const_seq iter_addn addn0; fold cardGE.
-          apply leq_trans with (n := delta * cardGE delta +
-                                     delta * (num_cpus - cardGE delta));
+          (* Case 3: num_tasks_exceeding < num_cpus *)
+          rewrite big_const_seq iter_addn addn0; fold num_tasks_exceeding.
+          apply leq_trans with (n := delta * num_tasks_exceeding delta +
+                                     delta * (num_cpus - num_tasks_exceeding delta));
             first by rewrite -mulnDr subnKC //; apply ltnW.
-          rewrite leq_add2l; apply bertogna_fp_helper_lemma.
+          rewrite leq_add2l; apply bertogna_fp_interference_in_non_full_processors.
           by apply/andP; split; first by rewrite -has_count.
         Qed.
 
-        (* 5) Now, we prove that the Bertogna's interference bound
-              is not enough to cover the sum of the "minimum" term over
-              all tasks (artifact of the proof by contradiction). *)
+        (* 6) Next, using lemmas (0), (3) and (5) we prove that the reduction-based
+              interference bound is not enough to cover the sum of the minima over all tasks
+              (artifact of the proof by contradiction). *)
         Lemma bertogna_fp_sum_exceeds_total_interference:
           \sum_((tsk_k, R_k) <- hp_bounds)
             minn (x tsk_k) (R - task_cost tsk + 1) >
-          total_interference_bound_fp task_cost task_period tsk hp_bounds
-                                      R higher_eq_priority.
+          total_interference_bound_fp task_cost task_period tsk hp_bounds R.
         Proof.
           have EXCEEDS := bertogna_fp_minimum_exceeds_interference.
-          have ALLBUSY := bertogna_fp_all_cpus_busy.
+          have ALLBUSY := bertogna_fp_interference_on_all_cpus.
           have TOOMUCH := bertogna_fp_too_much_interference.
-          rename H_hp_bounds_has_interfering_tasks into UNZIP,
+          rename H_hp_bounds_has_interfering_tasks into HAS,
                  H_response_time_recurrence_holds into REC.
-          apply leq_trans with (n := \sum_(tsk_k <- ts_interf) minn (x tsk_k) (R - task_cost tsk + 1));
+          apply leq_trans with (n := \sum_(tsk_k <- hp_tasks) minn (x tsk_k) (R - task_cost tsk + 1));
             last first.
           {
             rewrite (eq_bigr (fun i => minn (x (fst i)) (R - task_cost tsk + 1)));
               last by ins; destruct i.
-            rewrite big_filter.
-            have MAP := big_map (fun x => fst x) (fun i => true) (fun i => minn (x i) (R - task_cost tsk + 1)).
-            by unfold unzip1 in *; rewrite -MAP -UNZIP -big_filter.
+            have MAP := @big_map _ 0 addn _ _ (fun x => fst x) hp_bounds (fun x => true) (fun y => minn (x y) (R - task_cost tsk + 1)).
+            rewrite -MAP.
+            apply leq_sum_sub_uniq; first by apply filter_uniq; destruct ts.
+            red; move => tsk0 IN0.
+            rewrite mem_filter in IN0; move: IN0 => /andP [INTERF0 IN0].
+            apply/mapP.
+            feed (HAS tsk0); first by done.
+            move: (HAS INTERF0) => [R0 IN].
+            by exists (tsk0, R0).
           }
           apply ltn_div_trunc with (d := num_cpus);
             first by apply H_at_least_one_cpu.
@@ -666,9 +679,11 @@ Module ResponseTimeAnalysisFP.
           by rewrite leq_mul2r; apply/orP; right; apply TOOMUCH.
         Qed.
 
-        (* 6) After concluding that the sum of the minimum exceeds (R - e_i + 1),
-              we prove that there exists a tuple (tsk_k, R_k) such that
-              min (x_k, R - e_i + 1) > min (W_k, R - e_i + 1). *)
+        (* 7) After concluding that the sum of the minima exceeds (R - e_i + 1),
+              we prove that there exists a tuple (tsk_k, R_k) that satisfies
+              min (x_k, R - e_i + 1) > min (W_k, R - e_i + 1).
+              This implies that x_k > W_k, which is of course a contradiction,
+              since W_k is a valid task interference bound. *)
         Lemma bertogna_fp_exists_task_that_exceeds_bound :
           exists tsk_k R_k,
             (tsk_k, R_k) \in hp_bounds /\
@@ -676,27 +691,25 @@ Module ResponseTimeAnalysisFP.
               minn (workload_bound tsk_k R_k) (R - task_cost tsk + 1)).
         Proof.
           have SUM := bertogna_fp_sum_exceeds_total_interference.
-          have INTERFk := bertogna_fp_tsk_other_interferes.
-          rename H_hp_bounds_has_interfering_tasks into UNZIP.
+          rename H_hp_bounds_has_interfering_tasks into HASHP.
           assert (HAS: has (fun tup : task_with_response_time =>
-                             let (tsk_k, R_k) := tup in
+                            let (tsk_k, R_k) := tup in
                                (minn (x tsk_k) (R - task_cost tsk + 1) >
                                 minn (workload_bound tsk_k R_k)(R - task_cost tsk + 1)))
                             hp_bounds).
           {
-            apply/negP; unfold not; intro NOTHAS.
-            move: NOTHAS => /negP /hasPn ALL.
-            rewrite -[_ < _]negbK in SUM.
-            move: SUM => /negP SUM; apply SUM; rewrite -leqNgt.
-            unfold total_interference_bound_fp.
-            rewrite [\sum_(i <- _ | let '(tsk_other, _) := i in _)_]big_mkcond.
-            rewrite big_seq_cond [\sum_(i <- _ | true) _]big_seq_cond.
-            apply leq_sum; move => tsk_k /andP [HPk _]; destruct tsk_k as [tsk_k R_k].
-            specialize (ALL (tsk_k, R_k) HPk).
-            rewrite -leqNgt in ALL.
-            specialize (INTERFk tsk_k R_k HPk).
-            fold (can_interfere_with_tsk); rewrite INTERFk.
-            by apply ALL.
+              apply/negP; unfold not; intro NOTHAS.
+              move: NOTHAS => /negP /hasPn ALL.
+              rewrite -[_ < _]negbK in SUM.
+              move: SUM => /negP SUM; apply SUM; rewrite -leqNgt.
+              rewrite (eq_bigr (fun i => minn (x (fst i)) (R - task_cost tsk + 1)));
+                last by ins; destruct i.
+              unfold total_interference_bound_fp.
+              rewrite big_seq_cond.
+              rewrite [\sum_(_ <- _ | true)_]big_seq_cond.
+              apply leq_sum.
+              intros p; rewrite andbT; intros IN.
+              by specialize (ALL p IN); destruct p; rewrite leqNgt.
           }
           move: HAS => /hasP HAS; destruct HAS as [[tsk_k R_k] HPk MINk]; exists tsk_k, R_k.
           by repeat split.
@@ -716,7 +729,7 @@ Module ResponseTimeAnalysisFP.
              H_interfering_tasks_miss_no_deadlines into NOMISS,
              H_response_time_recurrence_holds into REC,
              H_response_time_of_interfering_tasks_is_known into RESP,
-             H_hp_bounds_has_interfering_tasks into UNZIP,
+             H_hp_bounds_has_interfering_tasks into HAS,
              H_response_time_no_larger_than_deadline into LEdl.
       intros j JOBtsk.
        
diff --git a/analysis/basic/interference_bound.v b/analysis/basic/interference_bound.v
index 63229dee756439782224ada2545ffea7a6c154cc..c5ecc342dd9afda68f7867f69427e01361fe2b7a 100644
--- a/analysis/basic/interference_bound.v
+++ b/analysis/basic/interference_bound.v
@@ -34,7 +34,7 @@ Module InterferenceBoundGeneric.
       (* Based on the workload bound, Bertogna and Cirinei define the
          following interference bound for a task. *)
       Definition interference_bound_generic :=
-        minn (W task_cost task_period tsk_other R_other delta) (delta - (task_cost tsk) + 1).
+        minn (W task_cost task_period tsk_other R_other delta) (delta - task_cost tsk + 1).
 
     End PerTask.
 
diff --git a/analysis/basic/interference_bound_edf.v b/analysis/basic/interference_bound_edf.v
index bf569094fe92c4d5e7aa0739082d1c804cf431df..592123f1bd3cd522916db50e814be9bada0dd6d6 100644
--- a/analysis/basic/interference_bound_edf.v
+++ b/analysis/basic/interference_bound_edf.v
@@ -64,7 +64,7 @@ Module InterferenceBoundEDF.
     (* ... and an interval length delta. *)
     Variable delta: time.
 
-    Section PerTask.
+    Section RecallInterferenceBounds.
 
       Variable tsk_R: task_with_response_time.
       Let tsk_other := fst tsk_R.
@@ -82,19 +82,21 @@ Module InterferenceBoundEDF.
       Definition interference_bound_edf :=
         minn basic_interference_bound edf_specific_bound.
 
-    End PerTask.
+    End RecallInterferenceBounds.
 
-    Section AllTasks.
+    (* Next we define the computation of the total interference for APA scheduling. *)
+    Section TotalInterference.
 
-      Let interferes_with_tsk := jldp_can_interfere_with tsk.
+      (* Let other_task denote tasks different from tsk. *)
+      Let other_task := different_task tsk.
       
       (* The total interference incurred by tsk is bounded by the sum
-         of individual task interferences. *)
+         of individual task interferences of the other tasks. *)
       Definition total_interference_bound_edf :=
-        \sum_((tsk_other, R_other) <- R_prev | interferes_with_tsk tsk_other)
+        \sum_((tsk_other, R_other) <- R_prev | other_task tsk_other)
            interference_bound_edf (tsk_other, R_other).
 
-    End AllTasks.
+    End TotalInterference.
 
   End TotalInterferenceBoundEDF.
   
diff --git a/analysis/basic/interference_bound_fp.v b/analysis/basic/interference_bound_fp.v
index 35052c7e523e468f7b721b04179d16da3c2f60c1..da6ad7ca34fc537a69ef91aee025f042dd375dfd 100644
--- a/analysis/basic/interference_bound_fp.v
+++ b/analysis/basic/interference_bound_fp.v
@@ -21,22 +21,22 @@ Module InterferenceBoundFP.
 
     Let task_with_response_time := (sporadic_task * time)%type.
     
-    (* Assume a known response-time bound for each interfering task ... *)
+    (* Assume a known response-time bound for each higher-priority task ... *)
     Variable R_prev: seq task_with_response_time.
 
-    (* ... and an interval length delta. *)
+    (* ... in any interval of length delta. *)
     Variable delta: time.
       
     (* Assume an FP policy. *)
     Variable higher_eq_priority: FP_policy sporadic_task.
 
-    Let can_interfere_with_tsk := fp_can_interfere_with higher_eq_priority tsk.  
+    (* Recall the generic interference bound. *)
     Let total_interference_bound := interference_bound_generic task_cost task_period tsk delta.
     
     (* The total interference incurred by tsk is bounded by the sum
        of individual task interferences. *)
     Definition total_interference_bound_fp :=
-      \sum_((tsk_other, R_other) <- R_prev | can_interfere_with_tsk tsk_other)
+      \sum_((tsk_other, R_other) <- R_prev)
          total_interference_bound (tsk_other, R_other).
       
   End Definitions.
diff --git a/analysis/jitter/bertogna_edf_comp.v b/analysis/jitter/bertogna_edf_comp.v
index cf5a0572b4a53b25ad4b394a815fd3daa3a9815b..980b598fff516ba93779aaad4352e4660d6ac5b6 100755
--- a/analysis/jitter/bertogna_edf_comp.v
+++ b/analysis/jitter/bertogna_edf_comp.v
@@ -7,7 +7,7 @@ Module ResponseTimeIterationEDF.
   Import ResponseTimeAnalysisEDFJitter.
 
   (* In this section, we define the algorithm for Bertogna and Cirinei's
-     response-time analysis for EDF scheduling. *)
+     response-time analysis for EDF scheduling with release jitter. *)
   Section Analysis.
     
     Context {sporadic_task: eqType}.
@@ -59,7 +59,7 @@ Module ResponseTimeIterationEDF.
     (* To compute the response-time bounds of the entire task set,
        We start the iteration with a sequence of tasks and costs:
        <(task1, cost1), (task2, cost2), ...>. *)
-    Let initial_state (ts: taskset_of sporadic_task) :=
+    Let initial_state (ts: seq sporadic_task) :=
       map (fun t => (t, task_cost t)) ts.
 
     (* Then, we successively update the the response-time bounds based
@@ -70,14 +70,14 @@ Module ResponseTimeIterationEDF.
     (* To ensure that the procedure converges, we run the iteration a
        "sufficient" number of times: task_deadline tsk - task_cost tsk + 1.
        This corresponds to the time complexity of the procedure. *)
-    Let max_steps (ts: taskset_of sporadic_task) :=
+    Let max_steps (ts: seq sporadic_task) :=
       \sum_(tsk <- ts) (task_deadline tsk - task_cost tsk) + 1.
 
     (* This yields the following definition for the RTA. At the end of
        the iteration, we check if all computed response-time bounds
        are less than or equal to the deadline, in which case they are
        valid. *)
-    Definition edf_claimed_bounds (ts: taskset_of sporadic_task) :=
+    Definition edf_claimed_bounds (ts: seq sporadic_task) :=
       let R_values := iter (max_steps ts) edf_rta_iteration (initial_state ts) in
         if (all jitter_plus_R_le_deadline R_values) then
           Some R_values
@@ -85,7 +85,7 @@ Module ResponseTimeIterationEDF.
 
     (* The schedulability test simply checks if we got a list of
        response-time bounds (i.e., if the computation did not fail). *)
-    Definition edf_schedulable (ts: taskset_of sporadic_task) :=
+    Definition edf_schedulable (ts: seq sporadic_task) :=
       edf_claimed_bounds ts != None.
 
     (* In the following section, we prove several helper lemmas about the
@@ -258,8 +258,8 @@ Module ResponseTimeIterationEDF.
        of the iteration at (max_steps ts) is equal to the value at (max_steps ts) + 1. *)
     Section Convergence.
 
-      (* Consider any valid task set. *)
-      Variable ts: taskset_of sporadic_task.
+      (* Consider any list of tasks with valid parameters. *)
+      Variable ts: list sporadic_task.
       Hypothesis H_valid_task_parameters:
         valid_sporadic_taskset task_cost task_period task_deadline ts.
       
@@ -341,7 +341,7 @@ Module ResponseTimeIterationEDF.
           {
             assert (UNZIP0 := edf_claimed_bounds_unzip1_iteration ts 0).
             by simpl in UNZIP0; rewrite UNZIP0 edf_claimed_bounds_unzip1_iteration.
-          }  
+          }
           destruct ts as [| tsk0 ts'].
           {
             clear -step; induction step; first by done.
@@ -449,11 +449,11 @@ Module ResponseTimeIterationEDF.
 
           assert (SUBST: forall l delta,
                     \sum_(j <- l | let '(tsk_other, _) := j in
-                      jldp_can_interfere_with tsk_i tsk_other)
+                      different_task tsk_i tsk_other)
                         (let '(tsk_other, R_other) := j in
                           interference_bound_edf task_cost task_period task_deadline task_jitter tsk_i delta
                             (tsk_other, R_other)) =
-                    \sum_(j <- l | jldp_can_interfere_with tsk_i (fst j))
+                    \sum_(j <- l | different_task tsk_i (fst j))
                       interference_bound_edf task_cost task_period task_deadline task_jitter tsk_i delta j).
           {
             intros l x; clear -l.
@@ -498,14 +498,14 @@ Module ResponseTimeIterationEDF.
           }
           move: GE_COST => /allP GE_COST.
 
-          assert (LESUM: \sum_(j <- x1' | jldp_can_interfere_with tsk_i (fst j))
+          assert (LESUM: \sum_(j <- x1' | different_task tsk_i (fst j))
                           interference_bound_edf task_cost task_period task_deadline task_jitter tsk_i delta j <=
-                         \sum_(j <- x2' | jldp_can_interfere_with tsk_i (fst j))
+                         \sum_(j <- x2' | different_task tsk_i (fst j))
                         interference_bound_edf task_cost task_period task_deadline task_jitter tsk_i delta' j).
           {
             set elem := (tsk0, R0); rewrite 2!(big_nth elem).
             rewrite -SIZE.
-            rewrite big_mkcond [\sum_(_ <- _ | jldp_can_interfere_with _ _)_]big_mkcond.
+            rewrite big_mkcond [\sum_(_ <- _ | different_task _ _)_]big_mkcond.
             rewrite big_seq_cond [\sum_(_ <- _ | true) _]big_seq_cond.
             apply leq_sum; intros j; rewrite andbT; intros INj.
             rewrite mem_iota add0n subn0 in INj; move: INj => /andP [_ INj].
@@ -514,7 +514,7 @@ Module ResponseTimeIterationEDF.
               have MAP := @nth_map _ elem _ tsk0 (fun x => fst x).
               by rewrite -2?MAP -?SIZE //; f_equal.
             } rewrite -FSTeq.
-            destruct (jldp_can_interfere_with tsk_i (fst (nth elem x1' j))) eqn:INTERF;
+            destruct (different_task tsk_i (fst (nth elem x1' j))) eqn:INTERF;
               last by done.
             {
               exploit (LE elem); [by rewrite /= SIZE | | intro LEj].
@@ -540,7 +540,7 @@ Module ResponseTimeIterationEDF.
               by apply interference_bound_edf_monotonic.
             }
           }
-          destruct (jldp_can_interfere_with tsk_i tsk0) eqn:INTERFtsk0; last by done.
+          destruct (different_task tsk_i tsk0) eqn:INTERFtsk0; last by done.
           apply leq_add; last by done.
           {             
             exploit (LE (tsk0, R0)); [by rewrite /= SIZE | | intro LEj];
@@ -876,15 +876,13 @@ Module ResponseTimeIterationEDF.
       Qed.
 
       (* Therefore, with regard to the response-time bound recurrence, ...*)
-      Let rt_recurrence (tsk: sporadic_task) (rt_bounds: seq task_with_response_time) (R: time) :=
-        task_cost tsk + div_floor (I rt_bounds tsk R) num_cpus.
       
       (* ..., the individual response-time bounds (elements of the list) are also fixed points. *)
       Theorem edf_claimed_bounds_finds_fixed_point_for_each_bound :
         forall tsk R rt_bounds,
           edf_claimed_bounds ts = Some rt_bounds ->
           (tsk, R) \in rt_bounds ->
-          R = rt_recurrence tsk rt_bounds R.
+          R = edf_response_time_bound rt_bounds tsk R.
       Proof.
         intros tsk R rt_bounds SOME IN.
         have CONV := edf_claimed_bounds_finds_fixed_point_of_list rt_bounds.
@@ -946,17 +944,14 @@ Module ResponseTimeIterationEDF.
 
     Section MainProof.
 
-      (* Consider a task set ts. *)
+      (* Consider a task set ts where... *)
       Variable ts: taskset_of sporadic_task.
       
-      (* Assume the task set has no duplicates, ... *)
-      Hypothesis H_ts_is_a_set: uniq ts.
-
-      (* ...all tasks have valid parameters, ... *)
+      (* ...all tasks have valid parameters... *)
       Hypothesis H_valid_task_parameters:
         valid_sporadic_taskset task_cost task_period task_deadline ts.
 
-      (* ...constrained deadlines, ...*)
+      (* ...and constrained deadlines.*)
       Hypothesis H_constrained_deadlines:
         forall tsk, tsk \in ts -> task_deadline tsk <= task_period tsk.
 
@@ -979,8 +974,7 @@ Module ResponseTimeIterationEDF.
       
       (* Then, consider any platform with at least one CPU such that...*)
       Variable sched: schedule num_cpus arr_seq.
-      Hypothesis H_at_least_one_cpu :
-        num_cpus > 0.
+      Hypothesis H_at_least_one_cpu: num_cpus > 0.
 
       (* ...jobs only execute after jitter and no longer than their execution costs. *)
       Hypothesis H_jobs_execute_after_jitter:
diff --git a/analysis/jitter/bertogna_edf_theory.v b/analysis/jitter/bertogna_edf_theory.v
index 3ea4ad152dadba7bda035b648a224d03303bdadd..71cd4dcba4cbb3a05da10d4da714c77c51518cb1 100644
--- a/analysis/jitter/bertogna_edf_theory.v
+++ b/analysis/jitter/bertogna_edf_theory.v
@@ -2,7 +2,8 @@ Require Import rt.util.all.
 Require Import rt.model.jitter.job rt.model.jitter.task rt.model.jitter.task_arrival
                rt.model.jitter.schedule rt.model.jitter.platform rt.model.jitter.interference
                rt.model.jitter.workload rt.model.jitter.schedulability
-               rt.model.jitter.priority rt.model.jitter.platform rt.model.jitter.response_time.
+               rt.model.jitter.priority rt.model.jitter.constrained_deadlines
+               rt.model.jitter.response_time.
 Require Import rt.analysis.jitter.workload_bound
                rt.analysis.jitter.interference_bound_edf.
 From mathcomp Require Import ssreflect ssrbool eqtype ssrnat seq fintype bigop div path.
@@ -11,10 +12,13 @@ Module ResponseTimeAnalysisEDFJitter.
 
   Export JobWithJitter SporadicTaskset ScheduleOfSporadicTaskWithJitter Workload
          Schedulability ResponseTime Priority SporadicTaskArrival WorkloadBoundJitter
-         InterferenceBoundEDFJitter Platform Interference.
+         InterferenceBoundEDFJitter Platform Interference ConstrainedDeadlines.
 
-  (* In this section, we prove that Bertogna and Cirinei's RTA yields
-     safe response-time bounds. *)
+  (* In this section, we prove that any fixed point in Bertogna and
+     Cirinei's RTA for EDF scheduling modified to account for jitter
+     yields a safe response-time bound. This is an extension of the
+     analysis found in Chapter 17.1.2 of Baruah et al.'s book
+     Multiprocessor Scheduling for Real-time Systems. *)
   Section ResponseTimeBound.
 
     Context {sporadic_task: eqType}.
@@ -41,42 +45,49 @@ Module ResponseTimeAnalysisEDFJitter.
         valid_sporadic_job_with_jitter task_cost task_deadline task_jitter job_cost
                                                  job_deadline job_task job_jitter j.
 
-    (* Consider any schedule such that...*)
+    (* Assume that we have a task set where all tasks have valid
+       parameters and constrained deadlines, ... *)
+    Variable ts: taskset_of sporadic_task.
+    Hypothesis H_valid_task_parameters:
+      valid_sporadic_taskset task_cost task_period task_deadline ts.
+    Hypothesis H_constrained_deadlines:
+      forall tsk, tsk \in ts -> task_deadline tsk <= task_period tsk.
+
+    (* ... and that all jobs in the arrival sequence come from the task set. *)
+    Hypothesis H_all_jobs_from_taskset:
+      forall (j: JobIn arr_seq), job_task j \in ts.
+
+    (* Next, consider any schedule such that...*)
     Variable num_cpus: nat.
     Variable sched: schedule num_cpus arr_seq.
 
-    (* ...jobs execute after jitter and no longer than their execution costs. *)
-    Hypothesis H_jobs_execute_after_jitter:
+    (* ...jobs are sequential, do not execute before the
+       jitter has passed and nor longer than their execution costs. *)
+    Hypothesis H_sequential_jobs: sequential_jobs sched.
+    Hypothesis H_execute_after_jitter:
       jobs_execute_after_jitter job_jitter sched.
     Hypothesis H_completed_jobs_dont_execute:
       completed_jobs_dont_execute job_cost sched.
 
-    (* Also assume that jobs are sequential and that there exists
-       at least one processor. *)
-    Hypothesis H_sequential_jobs: sequential_jobs sched.
+    (* Assume that there exists at least one processor. *)
     Hypothesis H_at_least_one_cpu: num_cpus > 0.
 
-    (* Assume that we have a task set ts, such that all jobs come
-       from the task set, and all tasks have valid parameters and
-       constrained deadlines. *)
-    Variable ts: taskset_of sporadic_task.
-    Hypothesis H_all_jobs_from_taskset:
-      forall (j: JobIn arr_seq), job_task j \in ts.
-    Hypothesis H_valid_task_parameters:
-      valid_sporadic_taskset task_cost task_period task_deadline ts.
-    Hypothesis H_constrained_deadlines:
-      forall tsk, tsk \in ts -> task_deadline tsk <= task_period tsk.
+    (* Assume that the schedule is a work-conserving EDF schedule. *)
+    Hypothesis H_work_conserving: work_conserving job_cost job_jitter sched.
+    Hypothesis H_edf_policy: enforces_JLDP_policy job_cost job_jitter sched (EDF job_deadline).
 
+    (* Let's define some local names to avoid passing many parameters. *)
     Let no_deadline_is_missed_by_tsk (tsk: sporadic_task) :=
       task_misses_no_deadline job_cost job_deadline job_task sched tsk.
     Let response_time_bounded_by (tsk: sporadic_task) :=
       is_response_time_bound_of_task job_cost job_task tsk sched.
 
-    (* Assume a known response-time bound R is known...  *)
+    (* Next we consider the response-time recurrence.
+       Assume that a response-time bound R is known...  *)
     Let task_with_response_time := (sporadic_task * time)%type.
     Variable rt_bounds: seq task_with_response_time.
 
-    (* ...for any task in the task set. *)
+    (* ...for any task in the task set, ... *)
     Hypothesis H_rt_bounds_contains_all_tasks: unzip1 rt_bounds = ts.
 
     (* Also, assume that R is a fixed-point of the response-time recurrence, ... *)
@@ -93,16 +104,7 @@ Module ResponseTimeAnalysisEDFJitter.
         (tsk, R) \in rt_bounds ->
         task_jitter tsk + R <= task_deadline tsk.
 
-    (* Assume that we have a work-conserving EDF scheduler. *)
-    Hypothesis H_work_conserving: work_conserving job_cost job_jitter sched.
-    Hypothesis H_edf_policy: enforces_JLDP_policy job_cost job_jitter sched (EDF job_deadline).
-
-    (* Assume that the task set has no duplicates. This is required to
-       avoid problems when counting tasks (for example, when stating
-       that the number of interfering tasks is at most num_cpus). *)
-    Hypothesis H_ts_is_a_set : uniq ts.
-
-    (* In order to prove that R is a response-time bound, we first present some lemmas. *)
+    (* In order to prove that R is a response-time bound, we first provide some lemmas. *)
     Section Lemmas.
 
       (* Let (tsk, R) be any task to be analyzed, with its response-time bound R. *)
@@ -120,7 +122,7 @@ Module ResponseTimeAnalysisEDFJitter.
       (* Assume that job j did not complete on time, ... *)
       Hypothesis H_j_not_completed: ~~ completed job_cost sched j (t1 + R).
 
-      (* and that it is the first job not to satisfy its response-time bound. *)
+      (* ...and that it is the first job not to satisfy its response-time bound. *)
       Hypothesis H_all_previous_jobs_completed_on_time :
         forall (j_other: JobIn arr_seq) tsk_other R_other,
           job_task j_other = tsk_other ->
@@ -147,9 +149,14 @@ Module ResponseTimeAnalysisEDFJitter.
       Let interference_bound (tsk_other: sporadic_task) (R_other: time) :=
         interference_bound_edf task_cost task_period task_deadline task_jitter tsk R (tsk_other, R_other). 
       
-      (* Also, let ts_interf be the subset of tasks that interfere with tsk. *)
-      Let ts_interf := [seq tsk_other <- ts | jldp_can_interfere_with tsk tsk_other].
+      (* Based on the definition of a different task, ... *)
+      Let other_task := different_task tsk.
+
+      (* ...let other_tasks denote the set of tasks that are different from tsk. *)
+      Let other_tasks :=
+        [seq tsk_other <- ts | other_task tsk_other].
 
+      (* Now we establish results the interfering tasks. *)
       Section LemmasAboutInterferingTasks.
         
         (* Let (tsk_other, R_other) be any pair of higher-priority task and
@@ -161,7 +168,7 @@ Module ResponseTimeAnalysisEDFJitter.
         (* Note that tsk_other is in task set ts ...*)
         Lemma bertogna_edf_tsk_other_in_ts: tsk_other \in ts.
         Proof.
-          by rewrite -H_rt_bounds_contains_all_tasks; apply/mapP; exists (tsk_other, R_other).
+          by rewrite set_mem -H_rt_bounds_contains_all_tasks; apply/mapP; exists (tsk_other, R_other).
         Qed.
 
         (* Also, R_other is larger than the cost of tsk_other. *)
@@ -267,65 +274,11 @@ Module ResponseTimeAnalysisEDFJitter.
           by apply completion_monotonic with (t := i); try (by done); apply ltnW.
         Qed.
 
-        Let scheduled_task_other_than_tsk (t: time) (tsk_other: sporadic_task) :=
-          task_is_scheduled job_task sched tsk_other t &&
-          jldp_can_interfere_with tsk tsk_other.
-      
-        (* 1) At all times that j is backlogged, all processors are busy with other tasks. *)
-        Lemma bertogna_edf_scheduling_invariant:
-          forall t,
-            t1 <= t < t1 + R ->
-            backlogged job_cost job_jitter sched j t ->
-            count (scheduled_task_other_than_tsk t) ts = num_cpus.
-        Proof.
-          rename H_all_jobs_from_taskset into FROMTS,
-                 H_valid_task_parameters into PARAMS,
-                 H_valid_job_parameters into JOBPARAMS,
-                 H_job_of_tsk into JOBtsk,
-                 H_sporadic_tasks into SPO,
-                 H_tsk_R_in_rt_bounds into INbounds,
-                 H_all_previous_jobs_completed_on_time into BEFOREok,
-                 H_tasks_miss_no_deadlines into NOMISS,
-                 H_rt_bounds_contains_all_tasks into UNZIP,
-                 H_constrained_deadlines into RESTR,
-                 H_work_conserving into WORK.
-          unfold x, X, total_interference, task_interference,
-                 valid_sporadic_job_with_jitter, valid_sporadic_job in *.
-          move => t /andP [LEt LTt] BACK.
-          unfold scheduled_task_other_than_tsk in *.
-          eapply platform_cpus_busy_with_interfering_tasks; try (by done);
-            [ by apply WORK | by done | by apply SPO 
-            | apply PARAMS; rewrite -JOBtsk; apply FROMTS
-            | by apply JOBtsk | by apply BACK | ].
-          {
-            intros j0 tsk0 TSK0 LE.
-            cut (tsk0 \in unzip1 rt_bounds = true); last by rewrite UNZIP -TSK0 FROMTS.
-            move => /mapP [p IN EQ]; destruct p as [tsk' R0]; simpl in *; subst tsk'.
-            apply completion_monotonic with (t0 := job_arrival j0 + task_jitter tsk0 + R0); try (by done).
-            {
-              rewrite -addnA leq_add2l TSK0.
-              apply leq_trans with (n := task_deadline tsk0); first by apply NOMISS.
-              by apply RESTR; rewrite -TSK0 FROMTS.
-            }
-            {
-              apply BEFOREok with (tsk_other := tsk0); try (by done).
-              apply leq_trans with (n := t1 + R); last first.
-              {
-                  rewrite leq_add2r leq_add2l -JOBtsk.
-                  by specialize (JOBPARAMS j); des.
-              }
-              apply leq_ltn_trans with (n := t); last by done.
-              apply leq_trans with (n := job_arrival j0 + task_period tsk0);
-                last by done.
-              rewrite -addnA leq_add2l.
-              apply leq_trans with (n := task_deadline tsk0); first by apply NOMISS.
-              by apply RESTR; rewrite -TSK0; apply FROMTS.
-            }
-          }
-        Qed.
-      
-        (* 2) Prove that during the scheduling window of j, any job that is scheduled while j is
-           backlogged comes from a different task. *)
+      (* 1) Next, we prove that during the scheduling window of j, any job that is
+            scheduled while j is backlogged comes from a different task.
+            This follows from the fact that j is the first job not to complete
+            by its response-time bound, so previous jobs of j's task must have
+            completed by their periods and cannot be pending. *)
         Lemma bertogna_edf_interference_by_different_tasks :
           forall t j_other,
             t1 <= t < t1 + R ->
@@ -397,49 +350,12 @@ Module ResponseTimeAnalysisEDFJitter.
           }
         Qed.
 
-      (* 3) Next, we prove that the sum of the interference of each task is equal
-          to the total interference multiplied by the number of processors. This
-          holds because interference only occurs when all processors are busy. *)
-        Lemma bertogna_edf_all_cpus_busy :
-          \sum_(tsk_k <- ts_interf) x tsk_k = X * num_cpus.
-        Proof.
-          have DIFFTASK := bertogna_edf_interference_by_different_tasks.
-          rename H_all_jobs_from_taskset into FROMTS,
-                 H_valid_task_parameters into PARAMS,
-                 H_job_of_tsk into JOBtsk, H_sporadic_tasks into SPO,
-                 H_work_conserving into WORK,
-                 H_tsk_R_in_rt_bounds into INbounds,
-                 H_all_previous_jobs_completed_on_time into BEFOREok,
-                 H_tasks_miss_no_deadlines into NOMISS,
-                 H_rt_bounds_contains_all_tasks into UNZIP,
-                 H_constrained_deadlines into CONSTR.
-          unfold sporadic_task_model in *.
-          unfold x, X, total_interference, task_interference.
-          rewrite -big_mkcond -exchange_big big_distrl /= mul1n.
-          rewrite [\sum_(_ <= _ < _ | backlogged _ _ _ _ _) _]big_mkcond.
-          apply eq_big_nat; move => t /andP [GEt LTt].
-          destruct (backlogged job_cost job_jitter sched j t) eqn:BACK;
-            last by rewrite big1 //; ins; rewrite big1.
-          rewrite big_mkcond /=.
-          rewrite exchange_big /=.
-          apply eq_trans with (y := \sum_(cpu < num_cpus) 1); last by simpl_sum_const.
-          apply eq_bigr; intros cpu _.
-          move: (WORK j t BACK cpu) => [j_other /eqP SCHED]; unfold scheduled_on in *.
-          rewrite (bigD1_seq (job_task j_other)) /=; last by rewrite filter_uniq.
-          {
-            rewrite (eq_bigr (fun i => 0));
-              last by intros i DIFF; rewrite /task_scheduled_on SCHED;apply/eqP;rewrite eqb0 eq_sym.
-            simpl_sum_const; apply/eqP; rewrite eqb1.
-            by unfold task_scheduled_on; rewrite SCHED.
-          }
-          rewrite mem_filter; apply/andP; split; last by apply FROMTS.
-          unfold jldp_can_interfere_with.
-          apply DIFFTASK with (t := t); [by auto | by done |].
-          by apply/existsP; exists cpu; apply/eqP.
-        Qed.
-
-        (* 4) Show that by the induction hypothesis, all jobs released
-           before the end of the interval complete by their periods. *)
+      (* 2) In order to use the lemmas in constrained_deadlines.v, we show that
+            all jobs released before the end of the interval complete by their
+            periods. This follows trivially from the hypothesis that all jobs
+            before (t1 + R) complete by their response-time bounds. 
+            With this lemma, we can conclude that during job j's scheduling
+            window there cannot be multiple pending jobs of each task.*)
         Lemma bertogna_edf_all_previous_jobs_complete_by_their_period:
           forall t (j0: JobIn arr_seq),
             t < t1 + R ->
@@ -476,21 +392,125 @@ Module ResponseTimeAnalysisEDFJitter.
           by rewrite -addnA leq_add2l; apply leq_trans with (n := task_deadline (job_task j0));
             [by apply NOMISS | apply CONSTR; rewrite FROMTS].
         Qed.
-        
-        (* Let (cardGE delta) be the number of interfering tasks whose interference
-           is larger than delta. *)
-        Let cardGE (delta: time) := count (fun i => x i >= delta) ts_interf.
-
-        (* 5) If there is at least one of such tasks (e.g., cardGE > 0), then the
-           cumulative interference caused by the complementary set of interfering
-           tasks fills at least (num_cpus - cardGE) processors. *)
-        Lemma bertogna_edf_helper_lemma :
+
+
+        (* Let's define a predicate to identify the other tasks that are scheduled. *)
+        Let other_scheduled_task (t: time) (tsk_other: sporadic_task) :=
+          task_is_scheduled job_task sched tsk_other t &&
+          other_task tsk_other.
+      
+        (* 3) Now we prove that, at all times that j is backlogged, the number
+              of tasks other than tsk that are scheduled is exactly the number
+              of processors in the system. This is required to prove lemma (4). *)
+        Lemma bertogna_edf_all_cpus_are_busy:
+          forall t,
+            t1 <= t < t1 + R ->
+            backlogged job_cost job_jitter sched j t ->
+            count (other_scheduled_task t) ts = num_cpus.
+        Proof.
+          rename H_all_jobs_from_taskset into FROMTS,
+                 H_valid_task_parameters into PARAMS,
+                 H_valid_job_parameters into JOBPARAMS,
+                 H_job_of_tsk into JOBtsk,
+                 H_sporadic_tasks into SPO,
+                 H_tsk_R_in_rt_bounds into INbounds,
+                 H_all_previous_jobs_completed_on_time into BEFOREok,
+                 H_tasks_miss_no_deadlines into NOMISS,
+                 H_rt_bounds_contains_all_tasks into UNZIP,
+                 H_constrained_deadlines into RESTR,
+                 H_work_conserving into WORK.
+          unfold x, X, total_interference, task_interference,
+                 valid_sporadic_job_with_jitter, valid_sporadic_job in *.
+          move => t /andP [LEt LTt] BACK.
+          eapply platform_cpus_busy_with_interfering_tasks; try (by done);
+            [ by apply WORK | by done | by apply SPO 
+            | apply PARAMS; rewrite -JOBtsk; apply FROMTS
+            | by apply JOBtsk | by apply BACK | ].
+          {
+            intros j0 tsk0 TSK0 LE.
+            cut (tsk0 \in unzip1 rt_bounds = true); last by rewrite UNZIP -TSK0 FROMTS.
+            move => /mapP [p IN EQ]; destruct p as [tsk' R0]; simpl in *; subst tsk'.
+            apply completion_monotonic with (t0 := job_arrival j0 + task_jitter tsk0 + R0); try (by done).
+            {
+              rewrite -addnA leq_add2l TSK0.
+              apply leq_trans with (n := task_deadline tsk0); first by apply NOMISS.
+              by apply RESTR; rewrite -TSK0 FROMTS.
+            }
+            {
+              apply BEFOREok with (tsk_other := tsk0); try (by done).
+              apply leq_trans with (n := t1 + R); last first.
+              {
+                  rewrite leq_add2r leq_add2l -JOBtsk.
+                  by specialize (JOBPARAMS j); des.
+              }
+              apply leq_ltn_trans with (n := t); last by done.
+              apply leq_trans with (n := job_arrival j0 + task_period tsk0);
+                last by done.
+              rewrite -addnA leq_add2l.
+              apply leq_trans with (n := task_deadline tsk0); first by apply NOMISS.
+              by apply RESTR; rewrite -TSK0; apply FROMTS.
+            }
+          }
+        Qed.
+      
+
+      (* 4) Next, we prove that the sum of the interference of each task is equal
+          to the total interference multiplied by the number of processors. This
+          holds because interference only occurs when all processors are busy.
+          With this lemma we can relate per-task interference with the total
+          interference incurred by j (backlogged time). *)
+        Lemma bertogna_edf_interference_on_all_cpus :
+          \sum_(tsk_k <- other_tasks) x tsk_k = X * num_cpus.
+        Proof.
+          have DIFFTASK := bertogna_edf_interference_by_different_tasks.
+          rename H_all_jobs_from_taskset into FROMTS,
+                 H_valid_task_parameters into PARAMS,
+                 H_job_of_tsk into JOBtsk, H_sporadic_tasks into SPO,
+                 H_work_conserving into WORK,
+                 H_tsk_R_in_rt_bounds into INbounds,
+                 H_all_previous_jobs_completed_on_time into BEFOREok,
+                 H_tasks_miss_no_deadlines into NOMISS,
+                 H_rt_bounds_contains_all_tasks into UNZIP,
+                 H_constrained_deadlines into CONSTR.
+          unfold sporadic_task_model in *.
+          unfold x, X, total_interference, task_interference.
+          rewrite -big_mkcond -exchange_big big_distrl /= mul1n.
+          rewrite [\sum_(_ <= _ < _ | backlogged _ _ _ _ _) _]big_mkcond.
+          apply eq_big_nat; move => t /andP [GEt LTt].
+          destruct (backlogged job_cost job_jitter sched j t) eqn:BACK;
+            last by rewrite big1 //; ins; rewrite big1.
+          rewrite big_mkcond /=.
+          rewrite exchange_big /=.
+          apply eq_trans with (y := \sum_(cpu < num_cpus) 1); last by simpl_sum_const.
+          apply eq_bigr; intros cpu _.
+          move: (WORK j t BACK cpu) => [j_other /eqP SCHED]; unfold scheduled_on in *.
+          rewrite (bigD1_seq (job_task j_other)) /=; last by rewrite filter_uniq; destruct ts.
+          {
+            rewrite (eq_bigr (fun i => 0));
+              last by intros i DIFF; rewrite /task_scheduled_on SCHED;apply/eqP;rewrite eqb0 eq_sym.
+            simpl_sum_const; apply/eqP; rewrite eqb1.
+            by unfold task_scheduled_on; rewrite SCHED.
+          }
+          rewrite mem_filter; apply/andP; split; last by apply FROMTS.
+          apply DIFFTASK with (t := t); [by auto | by done |].
+          by apply/existsP; exists cpu; apply/eqP.
+        Qed.
+
+        (* Before stating the next lemma, let (num_tasks_exceeding delta) be the
+           number of interfering tasks whose interference x is larger than delta. *)
+        Let num_tasks_exceeding delta := count (fun i => x i >= delta) (other_tasks).
+
+        (* 5) Now we prove that, for any delta, if (num_task_exceeding delta > 0), then the
+              cumulative interference caused by the complementary set of interfering tasks fills
+              the remaining, not-completely-full (num_cpus - num_tasks_exceeding delta)
+              processors. *)
+        Lemma bertogna_edf_interference_in_non_full_processors :
           forall delta,
-            0 < cardGE delta < num_cpus ->
-            \sum_(i <- ts_interf | x i < delta) x i >= delta * (num_cpus - cardGE delta).
+            0 < num_tasks_exceeding delta < num_cpus ->
+            \sum_(i <- other_tasks | x i < delta) x i >= delta * (num_cpus - num_tasks_exceeding delta).
         Proof.
           have COMP := bertogna_edf_all_previous_jobs_complete_by_their_period.
-          have INV := bertogna_edf_scheduling_invariant.
+          have INV := bertogna_edf_all_cpus_are_busy.
           rename H_all_jobs_from_taskset into FROMTS,
                  H_valid_task_parameters into PARAMS,
                  H_job_of_tsk into JOBtsk, H_sporadic_tasks into SPO,
@@ -505,14 +525,14 @@ Module ResponseTimeAnalysisEDFJitter.
           set some_interference_A := fun t =>
             has (fun tsk_k => backlogged job_cost job_jitter sched j t &&
                               (x tsk_k >= delta) &&
-                              task_is_scheduled job_task sched tsk_k t) ts_interf.
+                              task_is_scheduled job_task sched tsk_k t) other_tasks.
           set total_interference_B := fun t =>
               backlogged job_cost job_jitter sched j t *
               count (fun tsk_k => (x tsk_k < delta) &&
-                    task_is_scheduled job_task sched tsk_k t) ts_interf.
+                    task_is_scheduled job_task sched tsk_k t) other_tasks.
 
           apply leq_trans with ((\sum_(t1 <= t < t1 + R)
-                                some_interference_A t) * (num_cpus - cardGE delta)).
+                                some_interference_A t) * (num_cpus - num_tasks_exceeding delta)).
           {
             rewrite leq_mul2r; apply/orP; right.
             move: HAS => /hasP HAS; destruct HAS as [tsk_a INa LEa].
@@ -576,13 +596,13 @@ Module ResponseTimeAnalysisEDFJitter.
               [rewrite mul1n /= | by rewrite has_pred0 //].
 
             destruct (has (fun tsk_k : sporadic_task => (delta <= x tsk_k) &&
-                       task_is_scheduled job_task sched tsk_k t) ts_interf) eqn:HAS';
+                       task_is_scheduled job_task sched tsk_k t) other_tasks) eqn:HAS';
               last by done.
             rewrite mul1n; move: HAS => /hasP [tsk_k INk LEk].
-            unfold cardGE.
+            unfold num_tasks_exceeding.
             apply leq_trans with (n := num_cpus -
                          count (fun i => (x i >= delta) &&
-                            task_is_scheduled job_task sched i t) ts_interf).
+                            task_is_scheduled job_task sched i t) other_tasks).
             {
               apply leq_sub2l.
               rewrite -2!sum1_count big_mkcond /=.
@@ -591,11 +611,11 @@ Module ResponseTimeAnalysisEDFJitter.
               by destruct (task_is_scheduled job_task sched i t);
                 [by rewrite andbT | by rewrite andbF].
             }
-            rewrite -count_filter -[count _ ts_interf]count_filter.
+            rewrite -count_filter -[count _ other_tasks]count_filter.
             eapply leq_trans with (n := count (predC (fun tsk => delta <= x tsk)) _);
               last by apply eq_leq, eq_in_count; red; ins; rewrite ltnNge.
             rewrite leq_subLR count_predC size_filter.
-            by apply leq_trans with (n := count (scheduled_task_other_than_tsk t) ts);
+            by apply leq_trans with (n := count (other_scheduled_task t) ts);
               [by rewrite INV | by rewrite count_filter].
           }
           {
@@ -603,7 +623,7 @@ Module ResponseTimeAnalysisEDFJitter.
             rewrite exchange_big /=; apply leq_sum; intros t _.
             destruct (backlogged job_cost job_jitter sched j t) eqn:BACK; last by ins.
             rewrite mul1n -sum1_count.
-            rewrite big_mkcond [\sum_(i <- ts_interf | _ < _) _]big_mkcond /=.
+            rewrite big_mkcond [\sum_(i <- other_tasks | _ < _) _]big_mkcond /=.
             apply leq_sum_seq; move => tsk_k IN _.
             destruct (x tsk_k < delta); [rewrite andTb | by rewrite andFb].
             destruct (task_is_scheduled job_task sched tsk_k t) eqn:SCHED; last by done.
@@ -612,13 +632,13 @@ Module ResponseTimeAnalysisEDFJitter.
           }
         Qed.
 
-        (* 6) Next, we prove that, for any interval delta, if the sum of per-task
-              interference exceeds delta * num_cpus, the same applies for the
-              sum of the minimum between the interference and delta. *)
+        (* 6) Based on lemma (5), we prove that, for any interval delta, if the sum of per-task
+              interference exceeds (delta * num_cpus), the same applies for the
+              sum of the minimum of the interference and delta. *)
         Lemma bertogna_edf_minimum_exceeds_interference :
           forall delta,
-            \sum_(tsk_k <- ts_interf) x tsk_k >= delta * num_cpus ->
-               \sum_(tsk_k <- ts_interf) minn (x tsk_k) delta >=
+            \sum_(tsk_k <- other_tasks) x tsk_k >= delta * num_cpus ->
+               \sum_(tsk_k <- other_tasks) minn (x tsk_k) delta >=
                delta * num_cpus.
         Proof.
           intros delta SUMLESS.
@@ -632,64 +652,63 @@ Module ResponseTimeAnalysisEDFJitter.
             [| by red; ins; rewrite ltnNge
              | by intros i COND; rewrite -ltnNge in COND; rewrite COND].
 
-          (* Case 1: cardGE = 0 *)
-          destruct (~~ has (fun i => delta <= x i) ts_interf) eqn:HASa.
+          (* Case 1: num_tasks_exceeding = 0 *)
+          destruct (~~ has (fun i => delta <= x i) other_tasks) eqn:HASa.
           {
             rewrite [\sum_(_ <- _ | _ <= _) _]big_hasC; last by apply HASa.
             rewrite big_seq_cond; move: HASa => /hasPn HASa.
-            rewrite add0n (eq_bigl (fun i => (i \in ts_interf) && true));
-              last by red; intros tsk_k; destruct (tsk_k \in ts_interf) eqn:INk;
+            rewrite add0n (eq_bigl (fun i => (i \in other_tasks) && true));
+              last by red; intros tsk_k; destruct (tsk_k \in other_tasks) eqn:INk;
                 [by rewrite andTb ltnNge; apply HASa | by rewrite andFb].
             by rewrite -big_seq_cond.
           } apply negbFE in HASa.
 
-          (* Case 2: cardGE >= num_cpus *)
-          destruct (cardGE delta >= num_cpus) eqn:CARD.
+          (* Case 2: num_tasks_exceeding >= num_cpus *)
+          destruct (num_tasks_exceeding delta >= num_cpus) eqn:CARD.
           {
-            apply leq_trans with (delta * cardGE delta);
+            apply leq_trans with (delta * num_tasks_exceeding delta);
               first by rewrite leq_mul2l; apply/orP; right.
-            unfold cardGE; rewrite -sum1_count big_distrr /=.
+            unfold num_tasks_exceeding; rewrite -sum1_count big_distrr /=.
             rewrite -[\sum_(_ <- _ | _) _]addn0.
             by apply leq_add; [by apply leq_sum; ins; rewrite muln1|by ins].
           } apply negbT in CARD; rewrite -ltnNge in CARD.
 
-          (* Case 3: cardGE < num_cpus *)
-          rewrite big_const_seq iter_addn addn0; fold cardGE.
-          apply leq_trans with (n := delta * cardGE delta +
-                                     delta * (num_cpus - cardGE delta));
+          (* Case 3: num_tasks_exceeding < num_cpus *)
+          rewrite big_const_seq iter_addn addn0; fold num_tasks_exceeding.
+          apply leq_trans with (n := delta * num_tasks_exceeding delta +
+                                     delta * (num_cpus - num_tasks_exceeding delta));
             first by rewrite -mulnDr subnKC //; apply ltnW.
-          rewrite leq_add2l; apply bertogna_edf_helper_lemma.
+          rewrite leq_add2l; apply bertogna_edf_interference_in_non_full_processors.
           by apply/andP; split; first by rewrite -has_count.
         Qed.
 
-        (* 7) Now, we prove that the Bertogna's interference bound
-              is not enough to cover the sum of the "minimum" term over
-              all tasks (artifact of the proof by contradiction). *)
+        (* 7) Next, using lemmas (0), (4) and (6) we prove that the reduction-based
+              interference bound is not enough to cover the sum of the minima over all tasks
+              (artifact of the proof by contradiction). *)
         Lemma bertogna_edf_sum_exceeds_total_interference:
-          \sum_((tsk_other, R_other) <- rt_bounds | jldp_can_interfere_with tsk tsk_other)
+          \sum_((tsk_other, R_other) <- rt_bounds | other_task tsk_other)
             minn (x tsk_other) (R - task_cost tsk + 1) > I tsk R.
         Proof.
           have GE_COST := bertogna_edf_R_other_ge_cost.
           have EXCEEDS := bertogna_edf_minimum_exceeds_interference.
-          have ALLBUSY := bertogna_edf_all_cpus_busy.
+          have ALLBUSY := bertogna_edf_interference_on_all_cpus.
           have TOOMUCH := bertogna_edf_too_much_interference.
           rename H_rt_bounds_contains_all_tasks into UNZIP,
             H_response_time_is_fixed_point into REC.
-          apply leq_trans with (n := \sum_(tsk_other <- ts_interf) minn (x tsk_other) (R - task_cost tsk + 1));
+          apply leq_trans with (n := \sum_(tsk_other <- other_tasks) minn (x tsk_other) (R - task_cost tsk + 1));
             last first.
           {
             rewrite (eq_bigr (fun i => minn (x (fst i)) (R - task_cost tsk + 1)));
               last by ins; destruct i.
             move: UNZIP => UNZIP.
-            assert (FILTER: filter (jldp_can_interfere_with tsk) (unzip1 rt_bounds) =
-                            filter (jldp_can_interfere_with tsk) ts).
+            assert (FILTER: filter other_task (unzip1 rt_bounds) =
+                            filter other_task ts).
               by f_equal.
-            unfold ts_interf; rewrite -FILTER; clear FILTER.
+            unfold other_tasks; rewrite -FILTER; clear FILTER.
             rewrite -[\sum_(_ <- rt_bounds | _)_]big_filter.
             assert (SUBST: [seq i <- rt_bounds
-                   | let '(tsk_other, _) := i in
-                          jldp_can_interfere_with tsk tsk_other] = [seq i <- rt_bounds
-                             | jldp_can_interfere_with tsk (fst i)]).
+                             | let '(tsk_other, _) := i in other_task tsk_other] =
+                           [seq i <- rt_bounds | other_task (fst i)]).
             {
               by apply eq_filter; red; intro i; destruct i.
             } rewrite SUBST; clear SUBST.         
@@ -707,9 +726,11 @@ Module ResponseTimeAnalysisEDFJitter.
           by rewrite leq_mul2r; apply/orP; right; apply TOOMUCH.
         Qed.
 
-        (* 8) After concluding that the sum of the minimum exceeds (R - e_i + 1),
-              we prove that there exists a tuple (tsk_k, R_k) such that
-              min (x_k, R - e_i + 1) > min (W_k, edf_bound, R - e_i + 1). *)
+        (* 8) After concluding that the sum of the minima exceeds (R - e_i + 1),
+              we prove that there exists a tuple (tsk_k, R_k) that satisfies
+              min (x_k, R - e_i + 1) > min (W_k, I_edf, R - e_i + 1).
+              This implies that either x_k > W_k or x_k > I_edf, which is a contradiction,
+              since both W_k and I_edf are valid task interference bounds. *)
         Lemma bertogna_edf_exists_task_that_exceeds_bound :
           exists tsk_other R_other,
             (tsk_other, R_other) \in rt_bounds /\
@@ -721,7 +742,7 @@ Module ResponseTimeAnalysisEDFJitter.
           rename H_rt_bounds_contains_all_tasks into UNZIP.
           assert (HAS: has (fun tup : task_with_response_time =>
                               let (tsk_other, R_other) := tup in
-                              (tsk_other \in ts) && jldp_can_interfere_with tsk tsk_other &&
+                              (tsk_other \in ts) && other_task tsk_other &&
                                 (minn (x tsk_other) (R - task_cost tsk + 1)  >
                                 interference_bound tsk_other R_other))
                            rt_bounds).
diff --git a/analysis/jitter/bertogna_fp_comp.v b/analysis/jitter/bertogna_fp_comp.v
index 5a60ea7f15cb7d53993fd7d48476f49e6ce78550..5e9f582142411085eaf419f82baa151677be2504 100644
--- a/analysis/jitter/bertogna_fp_comp.v
+++ b/analysis/jitter/bertogna_fp_comp.v
@@ -7,7 +7,7 @@ Module ResponseTimeIterationFP.
   Import ResponseTimeAnalysisFP.
 
   (* In this section, we define the algorithm of Bertogna and Cirinei's
-     response-time analysis for FP scheduling. *)
+     response-time analysis for FP scheduling with release jitter. *)
   Section Analysis.
     
     Context {sporadic_task: eqType}.
@@ -49,8 +49,7 @@ Module ResponseTimeIterationFP.
       iter step
         (fun t => task_cost tsk +
                   div_floor
-                    (total_interference_bound_fp task_cost task_period task_jitter tsk
-                                                R_prev t higher_priority)
+                    (total_interference_bound_fp task_cost task_period task_jitter tsk  R_prev t)
                     num_cpus)
         (task_cost tsk).
 
@@ -77,12 +76,12 @@ Module ResponseTimeIterationFP.
     (* The response-time analysis for a given task set is defined
        as a left-fold (reduce) based on the function above.
        This either returns a list of task and response-time bounds, or None. *)
-    Definition fp_claimed_bounds (ts: taskset_of sporadic_task) :=
+    Definition fp_claimed_bounds (ts: seq sporadic_task) :=
       foldl fp_bound_of_task (Some [::]) ts.
 
     (* The schedulability test simply checks if we got a list of
        response-time bounds (i.e., if the computation did not fail). *)
-    Definition fp_schedulable (ts: taskset_of sporadic_task) :=
+    Definition fp_schedulable (ts: seq sporadic_task) :=
       fp_claimed_bounds ts != None.
     
     (* In the following section, we prove several helper lemmas about the
@@ -261,7 +260,7 @@ Module ResponseTimeIterationFP.
         forall tsk rt_bounds,
           task_cost tsk +
            div_floor (total_interference_bound_fp task_cost task_period task_jitter tsk rt_bounds
-                     (per_task_rta tsk rt_bounds (max_steps tsk)) higher_priority) num_cpus
+                     (per_task_rta tsk rt_bounds (max_steps tsk))) num_cpus
           = per_task_rta tsk rt_bounds (max_steps tsk).+1.
       Proof.
           by done.
@@ -276,14 +275,13 @@ Module ResponseTimeIterationFP.
       (* Consider a list of previous tasks and a task tsk to be analyzed. *)
       Variable ts: taskset_of sporadic_task.
 
-      (* Assume that the task set doesn't contain duplicates and is sorted by priority, ... *)
-      Hypothesis H_task_set_is_a_set: uniq ts.
+      (* Assume that the task set is sorted by unique priorities, ... *)
       Hypothesis H_task_set_is_sorted: sorted higher_priority ts.
+      Hypothesis H_task_set_has_unique_priorities:
+        FP_is_antisymmetric_over_task_set higher_priority ts.
 
-      (* ...the priority order is strict (<), ...*)
-      Hypothesis H_priority_irreflexive: irreflexive higher_priority.
-      Hypothesis H_priority_transitive: transitive higher_priority.
-      Hypothesis H_priority_antissymetric: antisymmetric higher_priority.
+      (* ...the priority order is transitive, ...*)
+      Hypothesis H_priority_transitive: FP_is_transitive higher_priority.
 
       (* ... and that the response-time analysis succeeds. *)
       Variable hp_bounds: seq task_with_response_time.
@@ -294,61 +292,20 @@ Module ResponseTimeIterationFP.
       Variable elem: sporadic_task.
       Let TASK := nth elem ts.
 
-      (* Then, the tasks in the prefix of fp_claimed_bounds are exactly interfering tasks
-         under FP scheduling.*)
-      Lemma fp_claimed_bounds_interf:
-        forall idx,
+      (* We prove that higher-priority tasks have smaller index. *)
+      Lemma fp_claimed_bounds_hp_tasks_have_smaller_index :
+        forall hp_idx idx,
+          hp_idx < size ts ->
           idx < size ts ->
-          [seq tsk_hp <- ts | fp_can_interfere_with higher_priority (TASK idx) tsk_hp] = take idx ts.
+          hp_idx != idx ->
+          higher_priority (TASK hp_idx) (TASK idx) ->
+          hp_idx < idx.
       Proof.
-        rename H_task_set_is_sorted into SORT,
-               H_task_set_is_a_set into UNIQ,
-               H_priority_antissymetric into ANTI,
-               H_priority_irreflexive into IRR.
-        induction idx.
-        {
-          intros LT.
-          destruct ts as [| tsk0 ts']; [by done | simpl in SORT].
-          unfold fp_can_interfere_with; rewrite /= eq_refl andbF.
-          apply eq_trans with (y := filter pred0 ts');
-            last by apply filter_pred0.
-          apply eq_in_filter; red; intros x INx; rewrite /TASK /=.
-          destruct (x != tsk0) eqn:SAME; rewrite ?andbT ?andbF //.
-          apply negbTE; apply/negP; unfold not; intro HP.
-          move: SAME => /eqP SAME; apply SAME; clear SAME.
-          apply ANTI; apply/andP; split; first by done.
-          apply order_path_min in SORT; last by done.
-          by move: SORT => /allP SORT; apply SORT.
-        }
-        {
-          intros LT.
-          generalize LT; intro LT'; apply ltSnm in LT.
-          feed IHidx; first by done.
-          rewrite -filter_idx_le_takeS //.
-          apply eq_in_filter; red; intros x INx.
-          unfold fp_can_interfere_with.
-          generalize INx; intro SUBST; apply nth_index with (x0 := elem) in SUBST.
-          rewrite -SUBST; clear SUBST.
-          rewrite index_uniq; [ | by rewrite index_mem | by done].
-          apply/idP/idP.
-          {
-            move => /andP [HP DIFF].
-            unfold TASK in *.
-            apply sorted_uniq_rel_implies_le_idx in HP; try (by done);
-              last by rewrite index_mem.
-            by rewrite leq_eqVlt in HP; move: HP => /orP [/eqP SAME | LESS];
-                first by rewrite SAME eq_refl in DIFF.
-          }
-          {
-            intros LEidx; apply/andP; split;
-              first by apply sorted_lt_idx_implies_rel.
-            apply/eqP; red; intro BUG.
-            eapply f_equal with (f := fun x => index x ts) in BUG.
-            rewrite nth_index in BUG; last by done.
-            rewrite BUG in LEidx.
-            by rewrite index_uniq // ltnn in LEidx.
-          }
-        }
+        unfold TASK; clear TASK.
+        rename ts into ts'; destruct ts' as [ts UNIQ]; simpl in *.
+        intros hp_idx idx LThp LT NEQ HP.
+        rewrite ltn_neqAle; apply/andP; split; first by done.
+        by apply sorted_rel_implies_le_idx with (leT := higher_priority) (s := ts) (x0 := elem).
       Qed.
       
     End HighPriorityTasks.
@@ -357,7 +314,7 @@ Module ResponseTimeIterationFP.
     Section Convergence.
 
       (* Consider any set of higher-priority tasks. *)
-      Variable ts_hp: taskset_of sporadic_task.
+      Variable ts_hp: seq sporadic_task.
 
       (* Assume that the response-time analysis succeeds for the higher-priority tasks. *)
       Variable rt_bounds: seq task_with_response_time.
@@ -387,27 +344,22 @@ Module ResponseTimeIterationFP.
         apply fun_mon_iter_mon; [by ins | by ins; apply leq_addr |].
         clear LEx x1 x2; intros x1 x2 LEx.
         unfold div_floor, total_interference_bound_fp.
-        rewrite big_seq_cond [\sum_(i <- _ | let '(tsk_other, _) := i in
-                                 _ && (tsk_other != tsk))_]big_seq_cond.
-        rewrite leq_add2l leq_div2r // leq_sum //.
-
-        intros i; destruct (i \in rt_bounds) eqn:HP; last by rewrite andFb.
-        destruct i as [i R]; intros _.
-        unfold fp_claimed_bounds in SOME.
-        have GE_COST := (fp_claimed_bounds_ge_cost ts_hp rt_bounds i R SOME).
+        rewrite leq_add2l leq_div2r //.
+        rewrite big_seq_cond.
+        rewrite [\sum_(_ <- _ | true) _]big_seq_cond.
+        apply leq_sum; move => i /andP [IN _].
+        destruct i as [i R].
+        have GE_COST := fp_claimed_bounds_ge_cost ts_hp rt_bounds i R SOME IN.
         have UNZIP := fp_claimed_bounds_unzip ts_hp rt_bounds SOME.
-        assert (IN: i \in ts_hp).
-        {
-          by rewrite -UNZIP; apply/mapP; exists (i,R).
-        }
         unfold interference_bound_generic; simpl.
         rewrite leq_min; apply/andP; split.
         {
-          apply leq_trans with (n := W_jitter task_cost task_period task_jitter i R x1);
-            first by apply geq_minl.
-          exploit (VALID i); first by rewrite mem_rcons in_cons; apply/orP; right.
-          by ins; des; apply W_monotonic; try (by ins); apply GE_COST.
-        } 
+          apply leq_trans with (n := W_jitter task_cost task_period task_jitter i R x1); first by apply geq_minl.
+          apply W_monotonic; try (by done).
+          have INts: i \in ts_hp by rewrite -UNZIP; apply/mapP; exists (i, R).
+          by exploit (VALID i);
+            [by rewrite mem_rcons in_cons INts orbT | by ins; des].
+        }
         {
           apply leq_trans with (n := x1 - task_cost tsk + 1); first by apply geq_minr.
           by rewrite leq_add2r leq_sub2r //.
@@ -520,29 +472,22 @@ Module ResponseTimeIterationFP.
       (* Consider a task set ts. *)
       Variable ts: taskset_of sporadic_task.
       
-      (* Assume that higher_eq_priority is a total strict order (<).
-         TODO: it doesn't have to be total over the entire domain, but
-         only within the task set.
-         But to weaken the hypothesis, we need to re-prove some lemmas
-         from ssreflect. *)
-      Hypothesis H_irreflexive: irreflexive higher_priority.
-      Hypothesis H_transitive: transitive higher_priority.
-      Hypothesis H_unique_priorities: antisymmetric higher_priority.
-      Hypothesis H_total: total higher_priority.
-
-      (* Assume the task set has no duplicates, ... *)
-      Hypothesis H_ts_is_a_set: uniq ts.
-
-      (* ...all tasks have valid parameters, ... *)
+      (* Assume that all tasks have valid parameters, ... *)
       Hypothesis H_valid_task_parameters:
         valid_sporadic_taskset task_cost task_period task_deadline ts.
 
-      (* ...constrained deadlines, ...*)
+      (* ...and constrained deadlines.*)
       Hypothesis H_constrained_deadlines:
         forall tsk, tsk \in ts -> task_deadline tsk <= task_period tsk.
 
-      (* ...and tasks are ordered by increasing priorities. *)
-      Hypothesis H_sorted_ts: sorted higher_priority ts.
+       (* Assume that the task set is totally ordered by unique priorities,
+          and that the priority order is transitive. *)
+      Hypothesis H_task_set_is_sorted: sorted higher_priority ts.
+      Hypothesis H_task_set_has_unique_priorities:
+        FP_is_antisymmetric_over_task_set higher_priority ts.
+      Hypothesis H_priority_is_total:
+        FP_is_total_over_task_set higher_priority ts.
+      Hypothesis H_priority_transitive: FP_is_transitive higher_priority.
 
       (* Next, consider any arrival sequence such that...*)
       Context {arr_seq: arrival_sequence Job}.
@@ -563,8 +508,7 @@ Module ResponseTimeIterationFP.
       
       (* Then, consider any platform with at least one CPU such that...*)
       Variable sched: schedule num_cpus arr_seq.
-      Hypothesis H_at_least_one_cpu :
-        num_cpus > 0.
+      Hypothesis H_at_least_one_cpu: num_cpus > 0.
 
       (* ...jobs only execute after jitter and no longer than their execution costs. *)
       Hypothesis H_jobs_must_arrive_to_execute:
@@ -602,8 +546,7 @@ Module ResponseTimeIterationFP.
       Proof.
         rename H_valid_job_parameters into JOBPARAMS, H_valid_task_parameters into TASKPARAMS,
                H_constrained_deadlines into RESTR, H_completed_jobs_dont_execute into COMP,
-               H_jobs_must_arrive_to_execute into MUSTARRIVE, H_sorted_ts into SORT,
-               H_unique_priorities into UNIQ, 
+               H_jobs_must_arrive_to_execute into MUSTARRIVE,
                H_all_jobs_from_taskset into ALLJOBS.
         intros tsk R MATCH.
         assert (SOME: exists hp_bounds, fp_claimed_bounds ts = Some hp_bounds /\
@@ -668,11 +611,37 @@ Module ResponseTimeIterationFP.
         {
           cut (NTH idx \in hp_bounds = true);
             [intros IN | by apply mem_nth].
-          by rewrite -UNZIP; apply/mapP; exists (TASK idx, RESP idx); rewrite PAIR.
+          by rewrite set_mem -UNZIP; apply/mapP; exists (TASK idx, RESP idx); rewrite PAIR.
         }
         {
-          unfold unzip1 in *; rewrite map_take UNZIP SUBST //.
-          by apply fp_claimed_bounds_interf with (hp_bounds := hp_bounds); rewrite -?SIZE.
+          intros hp_tsk IN INTERF.
+          exists (RESP (index hp_tsk ts)).
+          move: (IN) => INDEX; apply nth_index with (x0 := tsk) in INDEX.
+          rewrite -{1}[hp_tsk]INDEX -SUBST; last by rewrite SIZE index_mem.
+          assert (UNIQ: uniq hp_bounds).
+          {
+            apply map_uniq with (f := fst); unfold unzip1 in *; rewrite UNZIP.
+            by destruct ts.
+          }
+          rewrite -filter_idx_lt_take //.
+          {
+            rewrite PAIR mem_filter; apply/andP; split;
+              last by apply mem_nth; rewrite SIZE index_mem.
+            {
+              rewrite /NTH index_uniq; [| by rewrite SIZE index_mem | by done ].
+              {
+                move: INTERF => /andP [HP NEQ].
+                apply fp_claimed_bounds_hp_tasks_have_smaller_index with
+                  (ts := ts) (elem := tsk) (hp_bounds := hp_bounds);
+                  try (by done);
+                  [by rewrite index_mem | by rewrite -SIZE | | by rewrite INDEX -SUBST].
+                apply/eqP; intro BUG; subst idx.
+                rewrite SUBST -{1}INDEX in NEQ;
+                  first by rewrite eq_refl in NEQ.
+                by rewrite SIZE index_mem INDEX.
+              }
+            }
+          }
         }
         {
           intros hp_tsk R_hp IN; apply mem_take in IN.
@@ -717,7 +686,7 @@ Module ResponseTimeIterationFP.
         feed (UNZIP rt_bounds); first by done.
         assert (EX: exists R, (tsk, R) \in rt_bounds).
         {
-          rewrite -UNZIP in INtsk; move: INtsk => /mapP EX.
+          rewrite set_mem -UNZIP in INtsk; move: INtsk => /mapP EX.
           by destruct EX as [p]; destruct p as [tsk' R]; simpl in *; subst tsk'; exists R.
         } des.
         exploit (RLIST tsk R); [by ins | by apply JOBtsk | intro COMPLETED].
diff --git a/analysis/jitter/bertogna_fp_theory.v b/analysis/jitter/bertogna_fp_theory.v
index 1f78d2a488f176aad2d8e6be18c1ed6f8e3a1666..c4abdd9bc1d3909530b85f9cd3a8c881f90c7bb0 100644
--- a/analysis/jitter/bertogna_fp_theory.v
+++ b/analysis/jitter/bertogna_fp_theory.v
@@ -1,6 +1,6 @@
 Require Import rt.util.all.
 Require Import rt.model.jitter.task rt.model.jitter.job rt.model.jitter.task_arrival
-               rt.model.jitter.schedule rt.model.jitter.platform rt.model.jitter.platform_fp
+               rt.model.jitter.schedule rt.model.jitter.platform rt.model.jitter.constrained_deadlines
                rt.model.jitter.workload rt.model.jitter.schedulability rt.model.jitter.priority
                rt.model.jitter.response_time rt.model.jitter.interference.
 Require Import rt.analysis.jitter.workload_bound rt.analysis.jitter.interference_bound_fp.
@@ -8,10 +8,16 @@ From mathcomp Require Import ssreflect ssrbool eqtype ssrnat seq fintype bigop d
 
 Module ResponseTimeAnalysisFP.
 
-  Export JobWithJitter SporadicTaskset ScheduleOfSporadicTaskWithJitter Workload Interference
-         Platform PlatformFP Schedulability ResponseTime Priority SporadicTaskArrival
-         WorkloadBoundJitter Interference InterferenceBoundFP.
+  Export JobWithJitter SporadicTaskset ScheduleOfSporadicTaskWithJitter
+         Workload Interference Platform ConstrainedDeadlines Schedulability
+         ResponseTime Priority SporadicTaskArrival WorkloadBoundJitter
+         Interference InterferenceBoundFP.
     
+  (* In this section, we prove that any fixed point in Bertogna and
+     Cirinei's RTA for FP scheduling modified to account for jitter
+     yields a safe response-time bound. This is an extension of the
+     analysis found in Chapter 18.2 of Baruah et al.'s book
+     Multiprocessor Scheduling for Real-time Systems. *)
   Section ResponseTimeBound.
 
     Context {sporadic_task: eqType}.
@@ -37,76 +43,82 @@ Module ResponseTimeAnalysisFP.
         valid_sporadic_job_with_jitter task_cost task_deadline task_jitter job_cost
                                        job_deadline job_task job_jitter j.
 
-    (* Consider any schedule such that...*)
+    (* Assume that we have a task set where all tasks have valid
+       parameters and constrained deadlines, ... *)
+    Variable ts: taskset_of sporadic_task.
+    Hypothesis H_valid_task_parameters:
+      valid_sporadic_taskset task_cost task_period task_deadline ts.
+    Hypothesis H_constrained_deadlines:
+      forall tsk, tsk \in ts -> task_deadline tsk <= task_period tsk.
+
+    (* ... and that all jobs in the arrival sequence come from the task set. *)
+    Hypothesis H_all_jobs_from_taskset:
+      forall (j: JobIn arr_seq), job_task j \in ts.
+
+    (* Next, consider any schedule such that...*)
     Variable num_cpus: nat.
     Variable sched: schedule num_cpus arr_seq.
 
-    (* ...jobs do not execute before their arrival times nor longer
-       than their execution costs. *)
+    (* ...jobs are sequential, do not execute before the jitter
+       has passed and nor longer than their execution costs. *)
+    Hypothesis H_sequential_jobs: sequential_jobs sched.
     Hypothesis H_jobs_execute_after_jitter:
       jobs_execute_after_jitter job_jitter sched.
     Hypothesis H_completed_jobs_dont_execute:
       completed_jobs_dont_execute job_cost sched.
 
-    (* Also assume that jobs are sequential and that there exists
-       at least one processor. *)
-    Hypothesis H_sequential_jobs: sequential_jobs sched.
+    (* Assume that there exists at least one processor. *)
     Hypothesis H_at_least_one_cpu: num_cpus > 0.
 
-    (* Assume that we have a task set (with no duplicates) where all jobs
-       come from the task set and all tasks have valid parameters and constrained deadlines. *)
-    Variable ts: taskset_of sporadic_task.
-    Hypothesis H_ts_is_a_set: uniq ts.
-    Hypothesis H_all_jobs_from_taskset:
-      forall (j: JobIn arr_seq), job_task j \in ts.
-    Hypothesis H_valid_task_parameters:
-      valid_sporadic_taskset task_cost task_period task_deadline ts.
-    Hypothesis H_constrained_deadlines:
-      forall tsk, tsk \in ts -> task_deadline tsk <= task_period tsk.
+    (* Consider a given FP policy, ... *)
+    Variable higher_eq_priority: FP_policy sporadic_task.
 
-    (* Next, consider a task tsk that is to be analyzed. *)
-    Variable tsk: sporadic_task.
-    Hypothesis task_in_ts: tsk \in ts.
+    (* ...and assume that the schedule is work-conserving and enforces this policy. *)
+    Hypothesis H_work_conserving: work_conserving job_cost job_jitter sched.
+    Hypothesis H_enforces_priority:
+      enforces_FP_policy job_cost job_task job_jitter sched higher_eq_priority.
 
+    (* Let's define some local names to avoid passing many parameters. *)
     Let no_deadline_is_missed_by_tsk (tsk: sporadic_task) :=
       task_misses_no_deadline job_cost job_deadline job_task sched tsk.
     Let response_time_bounded_by (tsk: sporadic_task) :=
       is_response_time_bound_of_task job_cost job_task tsk sched.
 
-    (* Assume a known response-time bound for any interfering task *)
-    Let task_with_response_time := (sporadic_task * time)%type.
-    Variable hp_bounds: seq task_with_response_time.
-    
-    (* For FP scheduling, assume there exists a fixed task priority. *)
-    Variable higher_eq_priority: FP_policy sporadic_task.
+    (* Next, we consider the response-time recurrence.
+       Let tsk be a task in ts that is to be analyzed. *)
+    Variable tsk: sporadic_task.
+    Hypothesis task_in_ts: tsk \in ts.
 
-    Let can_interfere_with_tsk := fp_can_interfere_with higher_eq_priority tsk.
-    
-    (* Assume that hp_bounds has exactly the tasks that interfere with tsk,... *)
-    Hypothesis H_hp_bounds_has_interfering_tasks:
-      [seq tsk_hp <- ts | can_interfere_with_tsk tsk_hp] = unzip1 hp_bounds.
+    (* Let is_hp_task denote whether a task is a higher-priority task
+       (with respect to tsk). *)
+    Let is_hp_task := higher_priority_task higher_eq_priority tsk.
 
-    (* ...and that all values in the pairs contain valid response-time bounds *)
+    (* Assume a response-time bound is known... *)
+    Let task_with_response_time := (sporadic_task * time)%type.
+    Variable hp_bounds: seq task_with_response_time.
     Hypothesis H_response_time_of_interfering_tasks_is_known:
       forall hp_tsk R,
         (hp_tsk, R) \in hp_bounds ->
         response_time_bounded_by hp_tsk (task_jitter hp_tsk + R).
+    
+    (* ... for every higher-priority task. *)
+    Hypothesis H_hp_bounds_has_interfering_tasks:
+      forall hp_tsk,
+        hp_tsk \in ts ->
+        is_hp_task hp_tsk ->
+        exists R,
+          (hp_tsk, R) \in hp_bounds.
 
     (* Assume that the response-time bounds are larger than task costs. *)
     Hypothesis H_response_time_bounds_ge_cost:
       forall hp_tsk R,
         (hp_tsk, R) \in hp_bounds -> R >= task_cost hp_tsk.
     
-    (* Assume that no deadline is missed by any interfering task, i.e.,
-       response-time bound R_other <= deadline. *)
+    (* Assume that no deadline is missed by any higher-priority task. *)
     Hypothesis H_interfering_tasks_miss_no_deadlines:
       forall hp_tsk R,
-        (hp_tsk, R) \in hp_bounds -> task_jitter hp_tsk + R <= task_deadline hp_tsk.
-
-    (* Assume that the scheduler is work-conserving and enforces priorities. *)
-    Hypothesis H_work_conserving: work_conserving job_cost job_jitter sched.
-    Hypothesis H_enforces_priority:
-      enforces_FP_policy job_cost job_task job_jitter sched higher_eq_priority.
+        (hp_tsk, R) \in hp_bounds ->
+        task_jitter hp_tsk + R <= task_deadline hp_tsk.
     
     (* Let R be the fixed point of Bertogna and Cirinei's recurrence, ...*)
     Variable R: time.
@@ -114,14 +126,14 @@ Module ResponseTimeAnalysisFP.
       R = task_cost tsk +
           div_floor
             (total_interference_bound_fp task_cost task_period task_jitter
-                                         tsk hp_bounds R higher_eq_priority)
+                                         tsk hp_bounds R)
             num_cpus.
 
     (* ... and assume that jitter + R is no larger than the deadline of tsk.*)
     Hypothesis H_response_time_no_larger_than_deadline:
       task_jitter tsk + R <= task_deadline tsk.
 
-    (* In order to prove that R is a response-time bound, we first present some lemmas. *)
+    (* In order to prove that R is a response-time bound, we first provide some lemmas. *)
     Section Lemmas.
 
       (* Consider any job j of tsk. *)
@@ -143,17 +155,18 @@ Module ResponseTimeAnalysisFP.
       Let x (tsk_other: sporadic_task) :=
         task_interference job_cost job_task job_jitter sched j tsk_other t1 (t1 + R).
 
-      (* and X the total interference incurred by job j due to any task. *)
+      (* ...and X the total interference incurred by job j due to any task. *)
       Let X := total_interference job_cost job_jitter sched j t1 (t1 + R).
 
       (* Recall Bertogna and Cirinei's workload bound. *)
       Let workload_bound (tsk_other: sporadic_task) (R_other: time) :=
         W_jitter task_cost task_period task_jitter tsk_other R_other R.
 
-      (* Also, let ts_interf be the subset of tasks that interfere with tsk. *)
-      Let ts_interf := [seq tsk_other <- ts | can_interfere_with_tsk tsk_other].
+      (* Let hp_tasks denote the set of higher-priority tasks. *)
+      Let hp_tasks := [seq tsk_other <- ts | is_hp_task tsk_other].
 
-      Section LemmasAboutInterferingTasks.
+      (* Now we establish results the higher-priority tasks. *)
+      Section LemmasAboutHPTasks.
         
         (* Let (tsk_other, R_other) be any pair of higher-priority task and
            response-time bound computed in previous iterations. *)
@@ -161,30 +174,6 @@ Module ResponseTimeAnalysisFP.
         Variable R_other: time.
         Hypothesis H_response_time_of_tsk_other: (tsk_other, R_other) \in hp_bounds.
 
-        (* Note that tsk_other is in task set ts ...*)
-        Lemma bertogna_fp_tsk_other_in_ts: tsk_other \in ts.
-          Proof.
-            rename H_hp_bounds_has_interfering_tasks into UNZIP,
-                   H_response_time_of_tsk_other into INbounds.
-            move: UNZIP => UNZIP.
-            cut (tsk_other \in ts_interf = true);
-              first by rewrite mem_filter; move => /andP [_ IN].
-            unfold ts_interf; rewrite UNZIP.
-            by apply/mapP; exists (tsk_other, R_other).
-        Qed.
-
-        (*... and interferes with task tsk. *)
-        Lemma bertogna_fp_tsk_other_interferes: can_interfere_with_tsk tsk_other.
-          Proof.
-            rename H_hp_bounds_has_interfering_tasks into UNZIP,
-                   H_response_time_of_tsk_other into INbounds.
-            move: UNZIP => UNZIP.
-            cut (tsk_other \in ts_interf = true);
-              first by rewrite mem_filter; move => /andP [INTERF _].
-            unfold ts_interf; rewrite UNZIP.
-            by apply/mapP; exists (tsk_other, R_other).
-        Qed.
-
         (* Since tsk_other cannot interfere more than it executes, we show that
            the interference caused by tsk_other is no larger than workload bound W. *)
         Lemma bertogna_fp_workload_bounds_interference :
@@ -193,15 +182,37 @@ Module ResponseTimeAnalysisFP.
           unfold response_time_bounded_by, is_response_time_bound_of_task,
                  completed, completed_jobs_dont_execute, valid_sporadic_job in *.
           rename H_valid_job_parameters into PARAMS,
+                 H_all_jobs_from_taskset into FROMTS,
                  H_valid_task_parameters into TASK_PARAMS,
                  H_constrained_deadlines into RESTR,
                  H_response_time_of_interfering_tasks_is_known into RESP,
                  H_interfering_tasks_miss_no_deadlines into NOMISS,
                  H_response_time_bounds_ge_cost into GE_COST.
           unfold x, workload_bound.
-          have INts := bertogna_fp_tsk_other_in_ts.
+          destruct ([exists t: 'I_(t1 + R),
+                       task_is_scheduled job_task sched tsk_other t]) eqn: SCHED;
+            last first.
+          {
+            apply negbT in SCHED; rewrite negb_exists in SCHED.
+            move: SCHED => /forallP SCHED.
+            apply leq_trans with (n := 0); last by done.
+            apply leq_trans with (n := \sum_(t1 <= t < t1 + R) 0);
+              last by rewrite big1.
+            apply leq_sum_nat; move => t /andP [_ LTt] _.
+            unfold t1 in LTt.
+            specialize (SCHED (Ordinal LTt)).
+            rewrite negb_exists in SCHED; move: SCHED => /forallP SCHED.
+            rewrite big1 //; intros cpu _.
+            specialize (SCHED cpu); apply negbTE in SCHED.
+            by rewrite SCHED andbF.
+          }
+          move: SCHED => /existsP [t /existsP [cpu SCHED]].
+          unfold task_scheduled_on in SCHED.
+          destruct (sched cpu t) as [j0 |]; last by done.
+          assert (INts: tsk_other \in ts).
+            by move: SCHED => /eqP <-; rewrite FROMTS.
           apply leq_trans with (n := workload job_task sched tsk_other t1 (t1 + R));
-            first by apply task_interference_le_workload.
+              first by apply task_interference_le_workload.
           by apply workload_bounded_by_W with (task_deadline0 := task_deadline)
               (job_jitter0 := job_jitter) (job_cost0 := job_cost) (job_deadline0 := job_deadline);
             try (by ins); last 2 first;
@@ -212,7 +223,7 @@ Module ResponseTimeAnalysisFP.
               | by ins; apply GE_COST]. 
         Qed.
 
-      End LemmasAboutInterferingTasks.
+      End LemmasAboutHPTasks.
 
       (* Next we prove some lemmas that help to derive a contradiction.*)
       Section DerivingContradiction.
@@ -263,66 +274,11 @@ Module ResponseTimeAnalysisFP.
           by apply completion_monotonic with (t := i); try (by done); apply ltnW.
         Qed.
 
-        Let scheduled_task_other_than_tsk (t: time) (tsk_other: sporadic_task) :=
-          task_is_scheduled job_task sched tsk_other t &&
-          can_interfere_with_tsk tsk_other.
-
-        (* 1) At all times that j is backlogged, all processors are busy with other tasks. *)
-        Lemma bertogna_fp_scheduling_invariant:
-          forall t,
-            t1 <= t < t1 + R ->
-            backlogged job_cost job_jitter sched j t ->
-            count (scheduled_task_other_than_tsk t) ts = num_cpus.
-        Proof.
-          rename H_valid_task_parameters into PARAMS,
-                 H_job_of_tsk into JOBtsk,
-                 H_all_jobs_from_taskset into FROMTS,
-                 H_sporadic_tasks into SPO,
-                 H_valid_job_parameters into JOBPARAMS,
-                 H_constrained_deadlines into RESTR,
-                 H_hp_bounds_has_interfering_tasks into UNZIP,
-                 H_interfering_tasks_miss_no_deadlines into NOMISS,
-                 H_response_time_of_interfering_tasks_is_known into PREV,
-                 H_previous_jobs_of_tsk_completed into PREVtsk.
-          unfold sporadic_task_model, is_response_time_bound_of_task,
-                 valid_sporadic_job_with_jitter in *.
-          move => t /andP [LEt LTt] BACK.
-
-          apply platform_fp_cpus_busy_with_interfering_tasks with (task_cost0 := task_cost)
-          (task_period0 := task_period) (task_deadline0 := task_deadline) (job_task0 := job_task)
-          (ts0 := ts) (tsk0 := tsk) (higher_eq_priority0 := higher_eq_priority) in BACK;
-            try (by done); first by apply PARAMS; rewrite -JOBtsk FROMTS.
-          {
-            apply leq_trans with (n := job_arrival j + job_jitter j + R); first by done.
-            rewrite -addnA leq_add2l.
-            apply leq_trans with (n := task_deadline tsk); last by apply RESTR.
-            apply leq_trans with (n := task_jitter tsk + R); last by done.
-            by rewrite leq_add2r -JOBtsk; specialize (JOBPARAMS j); des.
-          }
-          {
-            intros j_other tsk_other JOBother INTERF.
-            move: UNZIP => UNZIP.
-            cut (tsk_other \in unzip1 hp_bounds = true); last first.
-            {
-              rewrite -UNZIP mem_filter; apply/andP; split; first by done.
-              by rewrite -JOBother; apply FROMTS.
-            } intro IN.
-            move: IN => /mapP [p IN EQ]; destruct p as [tsk' R']; simpl in *; subst tsk'.
-            apply completion_monotonic with (t0 := job_arrival j_other + task_jitter tsk_other + R');
-              try (by done); last by rewrite -addnA; apply PREV.
-            by rewrite -addnA leq_add2l; apply leq_trans with (n := task_deadline tsk_other);
-              [by apply NOMISS | by apply RESTR; rewrite -JOBother].
-          }
-          {
-            ins; apply completion_monotonic with (t0 := job_arrival j0 + task_jitter tsk + R);
-              try (by done); last by apply PREVtsk.
-            rewrite -addnA leq_add2l.
-            by apply leq_trans with (n := task_deadline tsk); [by done | by apply RESTR].
-          }
-        Qed.
-
-        (* 2) Prove that during the scheduling window of j, any job that is scheduled while j is
-           backlogged comes from a different task. *)
+        (* 1) Next, we prove that during the scheduling window of j, any job that is
+              scheduled while j is backlogged comes from a different task.
+              This follows from the fact that j is the first job not to complete
+              by its response-time bound, so previous jobs of j's task must have
+              completed by their periods and cannot be pending. *)
         Lemma bertogna_fp_interference_by_different_tasks :
           forall t j_other,
             t1 <= t < t1 + R ->
@@ -387,11 +343,68 @@ Module ResponseTimeAnalysisFP.
           }
         Qed.
 
-        (* 3) Next, we prove that the sum of the interference of each task is equal
-              to the total interference multiplied by the number of processors. This
-              holds because interference only occurs when all processors are busy. *)
-        Lemma bertogna_fp_all_cpus_busy :
-          \sum_(tsk_k <- ts_interf) x tsk_k = X * num_cpus.
+        (* Let's define a predicate to identify the other tasks that are scheduled. *)
+        Let other_scheduled_task (t: time) (tsk_other: sporadic_task) :=
+          task_is_scheduled job_task sched tsk_other t &&
+          is_hp_task tsk_other.
+      
+        (* 2) Now we prove that, at all times that j is backlogged, the number
+              of tasks other than tsk that are scheduled is exactly the number
+              of processors in the system. This is required to prove lemma (3). *)
+        Lemma bertogna_fp_all_cpus_are_busy:
+          forall t,
+            t1 <= t < t1 + R ->
+            backlogged job_cost job_jitter sched j t ->
+            count (other_scheduled_task t) ts = num_cpus.
+        Proof.
+          rename H_valid_task_parameters into PARAMS,
+                 H_job_of_tsk into JOBtsk,
+                 H_all_jobs_from_taskset into FROMTS,
+                 H_sporadic_tasks into SPO,
+                 H_valid_job_parameters into JOBPARAMS,
+                 H_constrained_deadlines into RESTR,
+                 H_hp_bounds_has_interfering_tasks into HAS,
+                 H_interfering_tasks_miss_no_deadlines into NOMISS,
+                 H_response_time_of_interfering_tasks_is_known into PREV,
+                 H_previous_jobs_of_tsk_completed into PREVtsk.
+          unfold sporadic_task_model, is_response_time_bound_of_task,
+                 valid_sporadic_job_with_jitter in *.
+          move => t /andP [LEt LTt] BACK.
+
+          apply platform_fp_cpus_busy_with_interfering_tasks with (task_cost0 := task_cost)
+          (task_period0 := task_period) (task_deadline0 := task_deadline) (job_task0 := job_task)
+          (ts0 := ts) (tsk0 := tsk) (higher_eq_priority0 := higher_eq_priority) in BACK;
+            try (by done); first by apply PARAMS; rewrite -JOBtsk FROMTS.
+          {
+            apply leq_trans with (n := job_arrival j + job_jitter j + R); first by done.
+            rewrite -addnA leq_add2l.
+            apply leq_trans with (n := task_deadline tsk); last by apply RESTR.
+            apply leq_trans with (n := task_jitter tsk + R); last by done.
+            by rewrite leq_add2r -JOBtsk; specialize (JOBPARAMS j); des.
+          }
+          {
+            intros j_other tsk_other JOBother INTERF.
+            feed (HAS tsk_other); first by rewrite -JOBother FROMTS.
+            move: (HAS INTERF) => [R' IN].
+            apply completion_monotonic with (t0 := job_arrival j_other + task_jitter tsk_other + R');
+              try (by done); last by rewrite -addnA; apply PREV.
+            by rewrite -addnA leq_add2l; apply leq_trans with (n := task_deadline tsk_other);
+              [by apply NOMISS | by apply RESTR; rewrite -JOBother].
+          }
+          {
+            ins; apply completion_monotonic with (t0 := job_arrival j0 + task_jitter tsk + R);
+              try (by done); last by apply PREVtsk.
+            rewrite -addnA leq_add2l.
+            by apply leq_trans with (n := task_deadline tsk); [by done | by apply RESTR].
+          }
+        Qed.
+
+
+        (* 3) Now we prove that, at all times that j is backlogged, the number
+              of tasks other than tsk that are scheduled is exactly the number
+              of processors in the system. This is required to prove lemma (4). *)
+        Lemma bertogna_fp_interference_on_all_cpus:
+          \sum_(tsk_k <- hp_tasks) x tsk_k = X * num_cpus.
         Proof.
           have DIFFTASK := bertogna_fp_interference_by_different_tasks.
           rename H_all_jobs_from_taskset into FROMTS,
@@ -414,7 +427,7 @@ Module ResponseTimeAnalysisFP.
           apply eq_trans with (y := \sum_(cpu < num_cpus) 1); last by simpl_sum_const.
           apply eq_bigr; intros cpu _.
           move: (WORK j t BACK cpu) => [j_other /eqP SCHED]; unfold scheduled_on in *.
-          rewrite (bigD1_seq (job_task j_other)) /=; last by rewrite filter_uniq.
+          rewrite (bigD1_seq (job_task j_other)) /=; last by rewrite filter_uniq; destruct ts.
           {
             rewrite (eq_bigr (fun i => 0));
               last by intros i DIFF; rewrite /task_scheduled_on SCHED;apply/eqP;rewrite eqb0 eq_sym.
@@ -422,7 +435,7 @@ Module ResponseTimeAnalysisFP.
             by unfold task_scheduled_on; rewrite SCHED.
           }
           rewrite mem_filter; apply/andP; split; last by apply FROMTS.
-          unfold can_interfere_with_tsk, fp_can_interfere_with; apply/andP; split.
+          unfold is_hp_task, higher_priority_task; apply/andP; split.
           {
             rewrite -JOBtsk; apply FP with (t := t); try by done.
             by apply/existsP; exists cpu; apply/eqP.
@@ -431,19 +444,20 @@ Module ResponseTimeAnalysisFP.
           by apply/existsP; exists cpu; apply/eqP.
         Qed.
 
-        (* Let (cardGE delta) be the number of interfering tasks whose interference
-           is larger than delta. *)
-        Let cardGE (delta: time) := count (fun i => x i >= delta) ts_interf.
+        (* Before stating the next lemma, let (num_tasks_exceeding delta) be the
+           number of interfering tasks whose interference x is larger than delta. *)
+        Let num_tasks_exceeding delta := count (fun i => x i >= delta) (hp_tasks).
 
-        (* 4) If there is at least one of such tasks (e.g., cardGE > 0), then the
-           cumulative interference caused by the complementary set of interfering
-           tasks fills at least (num_cpus - cardGE) processors. *)
-        Lemma bertogna_fp_helper_lemma :
+        (* 4) Now we prove that, for any delta, if (num_task_exceeding delta > 0), then the
+              cumulative interference caused by the complementary set of interfering tasks fills
+              the remaining, not-completely-full (num_cpus - num_tasks_exceeding delta)
+              processors. *)
+        Lemma bertogna_fp_interference_in_non_full_processors :
           forall delta,
-            0 < cardGE delta < num_cpus ->
-            \sum_(i <- ts_interf | x i < delta) x i >= delta * (num_cpus - cardGE delta).
+            0 < num_tasks_exceeding delta < num_cpus ->
+            \sum_(i <- hp_tasks | x i < delta) x i >= delta * (num_cpus - num_tasks_exceeding delta).
         Proof.
-          have INV := bertogna_fp_scheduling_invariant.
+          have INV := bertogna_fp_all_cpus_are_busy.
           rename H_all_jobs_from_taskset into FROMTS,
                  H_valid_task_parameters into PARAMS,
                  H_valid_job_parameters into JOBPARAMS,
@@ -454,7 +468,7 @@ Module ResponseTimeAnalysisFP.
                  H_constrained_deadlines into CONSTR,
                  H_sequential_jobs into SEQ,
                  H_enforces_priority into FP,
-                 H_hp_bounds_has_interfering_tasks into UNZIP,
+                 H_hp_bounds_has_interfering_tasks into HASHP,
                  H_interfering_tasks_miss_no_deadlines into NOMISSHP.
           unfold sporadic_task_model, valid_sporadic_job_with_jitter, valid_sporadic_job in *.
           move => delta /andP [HAS LT]. 
@@ -463,14 +477,14 @@ Module ResponseTimeAnalysisFP.
           set some_interference_A := fun t =>
             has (fun tsk_k => backlogged job_cost job_jitter sched j t &&
                               (x tsk_k >= delta) &&
-                              task_is_scheduled job_task sched tsk_k t) ts_interf.
+                              task_is_scheduled job_task sched tsk_k t) hp_tasks.
           set total_interference_B := fun t =>
               backlogged job_cost job_jitter sched j t *
               count (fun tsk_k => (x tsk_k < delta) &&
-                    task_is_scheduled job_task sched tsk_k t) ts_interf.
+                    task_is_scheduled job_task sched tsk_k t) hp_tasks.
 
           apply leq_trans with ((\sum_(t1 <= t < t1 + R)
-                                some_interference_A t) * (num_cpus - cardGE delta)).
+                                some_interference_A t) * (num_cpus - num_tasks_exceeding delta)).
           {
             rewrite leq_mul2r; apply/orP; right.
             move: HAS => /hasP HAS; destruct HAS as [tsk_a INa LEa].
@@ -540,7 +554,7 @@ Module ResponseTimeAnalysisFP.
                 by rewrite -SAMEjob; apply/existsP; exists cpu; apply/eqP.
               }
               {
-                assert (INTERF: fp_can_interfere_with higher_eq_priority tsk (job_task j1)).
+                assert (INTERF: is_hp_task (job_task j1)).
                 {
                   apply/andP; split; last by rewrite SAMEtsk.
                   rewrite -JOBtsk; apply FP with (t := t); first by done.
@@ -553,9 +567,8 @@ Module ResponseTimeAnalysisFP.
                   rewrite ?JOBtsk ?SAMEtsk //.
                 {
                   intros j0 tsk0 JOB0 INTERF0.
-                  assert (IN: tsk0 \in (unzip1 hp_bounds)).
-                    by rewrite -UNZIP mem_filter; apply/andP; split; last by rewrite -JOB0 FROMTS.
-                  move: IN => /mapP [p IN EQ]; destruct p as [tsk0' R0]; simpl in *; subst tsk0'.
+                  feed (HASHP tsk0); first by rewrite -JOB0 FROMTS.
+                  move: (HASHP INTERF0) => [R0 IN0].
                   apply completion_monotonic with (t0 := job_arrival j0 + task_jitter tsk0 + R0);
                     try (by done).
                   {
@@ -564,7 +577,7 @@ Module ResponseTimeAnalysisFP.
                       [by apply NOMISSHP | by apply CONSTR; rewrite -JOB0 FROMTS].
                   }
                   rewrite -addnA.
-                  by eapply H_response_time_of_interfering_tasks_is_known; first by apply IN.
+                  by eapply H_response_time_of_interfering_tasks_is_known; first by apply IN0.
                 }
               }
             }
@@ -581,13 +594,13 @@ Module ResponseTimeAnalysisFP.
               [rewrite mul1n /= | by rewrite has_pred0 //].
 
             destruct (has (fun tsk_k : sporadic_task => (delta <= x tsk_k) &&
-                       task_is_scheduled job_task sched tsk_k t) ts_interf) eqn:HAS';
+                       task_is_scheduled job_task sched tsk_k t) hp_tasks) eqn:HAS';
               last by done.
             rewrite mul1n; move: HAS => /hasP [tsk_k INk LEk].
-            unfold cardGE.
+            unfold num_tasks_exceeding.
             apply leq_trans with (n := num_cpus -
                          count (fun i => (x i >= delta) &&
-                            task_is_scheduled job_task sched i t) ts_interf).
+                            task_is_scheduled job_task sched i t) hp_tasks).
             {
               apply leq_sub2l.
               rewrite -2!sum1_count big_mkcond /=.
@@ -596,11 +609,11 @@ Module ResponseTimeAnalysisFP.
               by destruct (task_is_scheduled job_task sched i t);
                 [by rewrite andbT | by rewrite andbF].
             }
-            rewrite -count_filter -[count _ ts_interf]count_filter.
+            rewrite -count_filter -[count _ hp_tasks]count_filter.
             eapply leq_trans with (n := count (predC (fun tsk => delta <= x tsk)) _);
               last by apply eq_leq, eq_in_count; red; ins; rewrite ltnNge.
             rewrite leq_subLR count_predC size_filter.
-            by apply leq_trans with (n := count (scheduled_task_other_than_tsk t) ts);
+            by apply leq_trans with (n := count (other_scheduled_task t) ts);
               [by rewrite INV | by rewrite count_filter].            
           }
           {
@@ -608,7 +621,7 @@ Module ResponseTimeAnalysisFP.
             rewrite exchange_big /=; apply leq_sum; intros t _.
             destruct (backlogged job_cost job_jitter sched j t) eqn:BACK; last by ins.
             rewrite mul1n -sum1_count.
-            rewrite big_mkcond [\sum_(i <- ts_interf | _ < _) _]big_mkcond /=.
+            rewrite big_mkcond [\sum_(i <- hp_tasks | _ < _) _]big_mkcond /=.
             apply leq_sum_seq; move => tsk_k IN _.
             destruct (x tsk_k < delta); [rewrite andTb | by rewrite andFb].
             destruct (task_is_scheduled job_task sched tsk_k t) eqn:SCHED; last by done.
@@ -617,13 +630,13 @@ Module ResponseTimeAnalysisFP.
           }
         Qed.
 
-        (* 5) Next, we prove that, for any interval delta, if the sum of per-task
-              interference exceeds delta * num_cpus, the same applies for the
-              sum of the minimum between the interference and delta. *)
+        (* 5) Based on lemma (4), we prove that, for any interval delta, if the sum of per-task
+              interference exceeds (delta * num_cpus), the same applies for the
+              sum of the minimum of the interference and delta. *)
         Lemma bertogna_fp_minimum_exceeds_interference :
           forall delta,
-            \sum_(tsk_k <- ts_interf) x tsk_k >= delta * num_cpus ->
-               \sum_(tsk_k <- ts_interf) minn (x tsk_k) delta >=
+            \sum_(tsk_k <- hp_tasks) x tsk_k >= delta * num_cpus ->
+               \sum_(tsk_k <- hp_tasks) minn (x tsk_k) delta >=
                delta * num_cpus.
         Proof.
           intros delta SUMLESS.
@@ -637,59 +650,63 @@ Module ResponseTimeAnalysisFP.
             [| by red; ins; rewrite ltnNge
              | by intros i COND; rewrite -ltnNge in COND; rewrite COND].
 
-          (* Case 1: cardGE = 0 *)
-          destruct (~~ has (fun i => delta <= x i) ts_interf) eqn:HASa.
+          (* Case 1: num_tasks_exceeding = 0 *)
+          destruct (~~ has (fun i => delta <= x i) hp_tasks) eqn:HASa.
           {
             rewrite [\sum_(_ <- _ | _ <= _) _]big_hasC; last by apply HASa.
             rewrite big_seq_cond; move: HASa => /hasPn HASa.
-            rewrite add0n (eq_bigl (fun i => (i \in ts_interf) && true));
-              last by red; intros tsk_k; destruct (tsk_k \in ts_interf) eqn:INk;
+            rewrite add0n (eq_bigl (fun i => (i \in hp_tasks) && true));
+              last by red; intros tsk_k; destruct (tsk_k \in hp_tasks) eqn:INk;
                 [by rewrite andTb ltnNge; apply HASa | by rewrite andFb].
             by rewrite -big_seq_cond.
           } apply negbFE in HASa.
 
-          (* Case 2: cardGE >= num_cpus *)
-          destruct (cardGE delta >= num_cpus) eqn:CARD.
+          (* Case 2: num_tasks_exceeding >= num_cpus *)
+          destruct (num_tasks_exceeding delta >= num_cpus) eqn:CARD.
           {
-            apply leq_trans with (delta * cardGE delta);
+            apply leq_trans with (delta * num_tasks_exceeding delta);
               first by rewrite leq_mul2l; apply/orP; right.
-            unfold cardGE; rewrite -sum1_count big_distrr /=.
+            unfold num_tasks_exceeding; rewrite -sum1_count big_distrr /=.
             rewrite -[\sum_(_ <- _ | _) _]addn0.
             by apply leq_add; [by apply leq_sum; ins; rewrite muln1|by ins].
           } apply negbT in CARD; rewrite -ltnNge in CARD.
 
-          (* Case 3: cardGE < num_cpus *)
-          rewrite big_const_seq iter_addn addn0; fold cardGE.
-          apply leq_trans with (n := delta * cardGE delta +
-                                     delta * (num_cpus - cardGE delta));
+          (* Case 3: num_tasks_exceeding < num_cpus *)
+          rewrite big_const_seq iter_addn addn0; fold num_tasks_exceeding.
+          apply leq_trans with (n := delta * num_tasks_exceeding delta +
+                                     delta * (num_cpus - num_tasks_exceeding delta));
             first by rewrite -mulnDr subnKC //; apply ltnW.
-          rewrite leq_add2l; apply bertogna_fp_helper_lemma.
+          rewrite leq_add2l; apply bertogna_fp_interference_in_non_full_processors.
           by apply/andP; split; first by rewrite -has_count.
         Qed.
 
-        (* 6) Now, we prove that the Bertogna's interference bound
-              is not enough to cover the sum of the "minimum" term over
-              all tasks (artifact of the proof by contradiction). *)
+        (* 6) Next, using lemmas (0), (3) and (5) we prove that the reduction-based
+              interference bound is not enough to cover the sum of the minima over all tasks
+              (artifact of the proof by contradiction). *)
         Lemma bertogna_fp_sum_exceeds_total_interference:
           \sum_((tsk_k, R_k) <- hp_bounds)
             minn (x tsk_k) (R - task_cost tsk + 1) >
-          total_interference_bound_fp task_cost task_period task_jitter tsk hp_bounds
-                                      R higher_eq_priority.
+          total_interference_bound_fp task_cost task_period task_jitter tsk hp_bounds R.
         Proof.
           have EXCEEDS := bertogna_fp_minimum_exceeds_interference.
-          have ALLBUSY := bertogna_fp_all_cpus_busy.
+          have ALLBUSY := bertogna_fp_interference_on_all_cpus.
           have TOOMUCH := bertogna_fp_too_much_interference.
-          rename H_hp_bounds_has_interfering_tasks into UNZIP,
+          rename H_hp_bounds_has_interfering_tasks into HAS,
                  H_response_time_recurrence_holds into REC.
-          apply leq_trans with (n := \sum_(tsk_k <- ts_interf) minn (x tsk_k) (R - task_cost tsk + 1));
+          apply leq_trans with (n := \sum_(tsk_k <- hp_tasks) minn (x tsk_k) (R - task_cost tsk + 1));
             last first.
           {
             rewrite (eq_bigr (fun i => minn (x (fst i)) (R - task_cost tsk + 1)));
               last by ins; destruct i.
-            rewrite big_filter.
-            have MAP := big_map (fun x => fst x) (fun i => true) (fun i => minn (x i)
-                                                                      (R - task_cost tsk + 1)).
-            by unfold unzip1 in *; rewrite -MAP -UNZIP -big_filter.
+            have MAP := @big_map _ 0 addn _ _ (fun x => fst x) hp_bounds (fun x => true) (fun y => minn (x y) (R - task_cost tsk + 1)).
+            rewrite -MAP.
+            apply leq_sum_sub_uniq; first by apply filter_uniq; destruct ts.
+            red; move => tsk0 IN0.
+            rewrite mem_filter in IN0; move: IN0 => /andP [INTERF0 IN0].
+            apply/mapP.
+            feed (HAS tsk0); first by done.
+            move: (HAS INTERF0) => [R0 IN].
+            by exists (tsk0, R0).
           }
           apply ltn_div_trunc with (d := num_cpus);
             first by apply H_at_least_one_cpu.
@@ -703,9 +720,11 @@ Module ResponseTimeAnalysisFP.
           by rewrite leq_mul2r; apply/orP; right; apply TOOMUCH.
         Qed.
 
-        (* 7) After concluding that the sum of the minimum exceeds (R' - e_i + 1),
-              we prove that there exists a tuple (tsk_k, R_k) such that
-              min (x_k, R' - e_i + 1) > min (W_k, R' - e_i + 1). *)
+        (* 7) After concluding that the sum of the minima exceeds (R - e_i + 1),
+              we prove that there exists a tuple (tsk_k, R_k) that satisfies
+              min (x_k, R - e_i + 1) > min (W_k', R - e_i + 1).
+              This implies that x_k > W_k', which is of course a contradiction,
+              since W_k is a valid task interference bound. *)
         Lemma bertogna_fp_exists_task_that_exceeds_bound :
           exists tsk_k R_k,
             (tsk_k, R_k) \in hp_bounds /\
@@ -713,27 +732,25 @@ Module ResponseTimeAnalysisFP.
               minn (workload_bound tsk_k R_k) (R - task_cost tsk + 1)).
         Proof.
           have SUM := bertogna_fp_sum_exceeds_total_interference.
-          have INTERFk := bertogna_fp_tsk_other_interferes.
-          rename H_hp_bounds_has_interfering_tasks into UNZIP.
+          rename H_hp_bounds_has_interfering_tasks into HASHP.
           assert (HAS: has (fun tup : task_with_response_time =>
                              let (tsk_k, R_k) := tup in
                                (minn (x tsk_k) (R - task_cost tsk + 1) >
                                 minn (workload_bound tsk_k R_k)(R - task_cost tsk + 1)))
                             hp_bounds).
           {
-            apply/negP; unfold not; intro NOTHAS.
-            move: NOTHAS => /negP /hasPn ALL.
-            rewrite -[_ < _]negbK in SUM.
-            move: SUM => /negP SUM; apply SUM; rewrite -leqNgt.
-            unfold total_interference_bound_fp.
-            rewrite [\sum_(i <- _ | let '(tsk_other, _) := i in _)_]big_mkcond.
-            rewrite big_seq_cond [\sum_(i <- _ | true) _]big_seq_cond.
-            apply leq_sum; move => tsk_k /andP [HPk _]; destruct tsk_k as [tsk_k R_k].
-            specialize (ALL (tsk_k, R_k) HPk).
-            rewrite -leqNgt in ALL.
-            specialize (INTERFk tsk_k R_k HPk).
-            fold (can_interfere_with_tsk); rewrite INTERFk.
-            by apply ALL.
+              apply/negP; unfold not; intro NOTHAS.
+              move: NOTHAS => /negP /hasPn ALL.
+              rewrite -[_ < _]negbK in SUM.
+              move: SUM => /negP SUM; apply SUM; rewrite -leqNgt.
+              rewrite (eq_bigr (fun i => minn (x (fst i)) (R - task_cost tsk + 1)));
+                last by ins; destruct i.
+              unfold total_interference_bound_fp.
+              rewrite big_seq_cond.
+              rewrite [\sum_(_ <- _ | true)_]big_seq_cond.
+              apply leq_sum.
+              intros p; rewrite andbT; intros IN.
+              by specialize (ALL p IN); destruct p; rewrite leqNgt.
           }
           move: HAS => /hasP HAS; destruct HAS as [[tsk_k R_k] HPk MINk]; exists tsk_k, R_k.
           by repeat split.
diff --git a/analysis/jitter/interference_bound.v b/analysis/jitter/interference_bound.v
index 41df7e005d041cffd16bcd3742acf153e3005d6a..27b9de55059802e7fd10eea81bf09e074bcffe88 100644
--- a/analysis/jitter/interference_bound.v
+++ b/analysis/jitter/interference_bound.v
@@ -36,25 +36,11 @@ Module InterferenceBoundJitter.
       (* Based on the workload bound, Bertogna and Cirinei define the
          following interference bound for a task. *)
       Definition interference_bound_generic :=
-        minn (W_jitter task_cost task_period task_jitter tsk_other R_other delta) (delta - (task_cost tsk) + 1).
+        minn (W_jitter task_cost task_period task_jitter tsk_other R_other delta)
+             (delta - task_cost tsk + 1).
 
     End PerTask.
 
-    Section AllTasks.
-      
-      (* Assume an FP policy. *)
-      Variable higher_eq_priority: FP_policy sporadic_task.
-
-      Let can_interfere_with_tsk := fp_can_interfere_with higher_eq_priority tsk.
-      
-      (* The total interference incurred by tsk is bounded by the sum
-         of individual task interferences. *)
-      Definition total_interference_bound_fp :=
-        \sum_((tsk_other, R_other) <- R_prev | can_interfere_with_tsk tsk_other)
-           interference_bound_generic (tsk_other, R_other).
-      
-    End AllTasks.
-
   End Definitions.
 
 End InterferenceBoundJitter.
\ No newline at end of file
diff --git a/analysis/jitter/interference_bound_edf.v b/analysis/jitter/interference_bound_edf.v
index 124063581b81453cde8ae327821e3b29d4fa4dca..48d2fad71370a668c0d5f934aabd1de925dc3eb7 100644
--- a/analysis/jitter/interference_bound_edf.v
+++ b/analysis/jitter/interference_bound_edf.v
@@ -66,7 +66,7 @@ Module InterferenceBoundEDFJitter.
     (* ... and an interval length delta. *)
     Variable delta: time.
 
-    Section PerTask.
+    Section RecallInterferenceBounds.
 
       Variable tsk_R: task_with_response_time.
       Let tsk_other := fst tsk_R.
@@ -84,19 +84,21 @@ Module InterferenceBoundEDFJitter.
       Definition interference_bound_edf :=
         minn basic_interference_bound edf_specific_bound.
 
-    End PerTask.
+    End RecallInterferenceBounds.
 
-    Section AllTasks.
+    (* Next we define the computation of the total interference for APA scheduling. *)
+    Section TotalInterference.
 
-      Let can_interfere_with_tsk := jldp_can_interfere_with tsk.
+      (* Recall the definition of a different task (with respect to tsk). *)
+      Let other_task := different_task tsk.
       
       (* The total interference incurred by tsk is bounded by the sum
-         of individual task interferences. *)
+         of individual task interferences of the other tasks. *)
       Definition total_interference_bound_edf :=
-        \sum_((tsk_other, R_other) <- R_prev | can_interfere_with_tsk tsk_other)
+        \sum_((tsk_other, R_other) <- R_prev | other_task tsk_other)
            interference_bound_edf (tsk_other, R_other).
 
-    End AllTasks.
+    End TotalInterference.
 
   End TotalInterferenceBoundEDF.
   
diff --git a/analysis/jitter/interference_bound_fp.v b/analysis/jitter/interference_bound_fp.v
index b60d89c3d6651a5d73b827d6ce42ff9690fa8a21..4bc8c267e84e228ea1d77d49a915c0f3f39fea19 100644
--- a/analysis/jitter/interference_bound_fp.v
+++ b/analysis/jitter/interference_bound_fp.v
@@ -31,13 +31,13 @@ Module InterferenceBoundFP.
     (* Assume an FP policy. *)
     Variable higher_eq_priority: FP_policy sporadic_task.
 
-    Let can_interfere_with_tsk := fp_can_interfere_with higher_eq_priority tsk.  
+    (* Recall the interference bound. *)
     Let total_interference_bound := interference_bound_generic task_cost task_period task_jitter tsk delta.
     
     (* The total interference incurred by tsk is bounded by the sum
        of individual task interferences. *)
     Definition total_interference_bound_fp :=
-      \sum_((tsk_other, R_other) <- R_prev | can_interfere_with_tsk tsk_other)
+      \sum_((tsk_other, R_other) <- R_prev)
          total_interference_bound (tsk_other, R_other).
       
   End Definitions.
diff --git a/analysis/parallel/bertogna_edf_comp.v b/analysis/parallel/bertogna_edf_comp.v
index a0b40e4ecee8823be52b2558f08ff24a75e90bd7..5ec5b9b7093024e9150231ac0ed5cb1ef6532e0b 100755
--- a/analysis/parallel/bertogna_edf_comp.v
+++ b/analysis/parallel/bertogna_edf_comp.v
@@ -7,7 +7,7 @@ Module ResponseTimeIterationEDF.
   Import ResponseTimeAnalysisEDF.
 
   (* In this section, we define the algorithm for Bertogna and Cirinei's
-     response-time analysis for EDF scheduling. *)
+     response-time analysis for EDF scheduling with parallel jobs. *)
   Section Analysis.
     
     Context {sporadic_task: eqType}.
@@ -27,7 +27,7 @@ Module ResponseTimeIterationEDF.
     (* Consider a platform with num_cpus processors. *)  
     Variable num_cpus: nat.
 
-    (* First, recall the interference bound under EDF, ... *)
+    (* First, recall the jitter-aware interference bound for EDF, ... *)
     Let I (rt_bounds: seq task_with_response_time)
           (tsk: sporadic_task) (delta: time) :=
       total_interference_bound_edf task_cost task_period task_deadline tsk rt_bounds delta.
@@ -57,7 +57,7 @@ Module ResponseTimeIterationEDF.
     (* To compute the response-time bounds of the entire task set,
        We start the iteration with a sequence of tasks and costs:
        <(task1, cost1), (task2, cost2), ...>. *)
-    Let initial_state (ts: taskset_of sporadic_task) :=
+    Let initial_state (ts: seq sporadic_task) :=
       map (fun t => (t, task_cost t)) ts.
 
     (* Then, we successively update the the response-time bounds based
@@ -68,14 +68,14 @@ Module ResponseTimeIterationEDF.
     (* To ensure that the procedure converges, we run the iteration a
        "sufficient" number of times: task_deadline tsk - task_cost tsk + 1.
        This corresponds to the time complexity of the procedure. *)
-    Let max_steps (ts: taskset_of sporadic_task) :=
+    Let max_steps (ts: seq sporadic_task) :=
       \sum_(tsk <- ts) (task_deadline tsk - task_cost tsk) + 1.
 
     (* This yields the following definition for the RTA. At the end of
        the iteration, we check if all computed response-time bounds
        are less than or equal to the deadline, in which case they are
        valid. *)
-    Definition edf_claimed_bounds (ts: taskset_of sporadic_task) :=
+    Definition edf_claimed_bounds (ts: seq sporadic_task) :=
       let R_values := iter (max_steps ts) edf_rta_iteration (initial_state ts) in
         if (all R_le_deadline R_values) then
           Some R_values
@@ -83,7 +83,7 @@ Module ResponseTimeIterationEDF.
 
     (* The schedulability test simply checks if we got a list of
        response-time bounds (i.e., if the computation did not fail). *)
-    Definition edf_schedulable (ts: taskset_of sporadic_task) :=
+    Definition edf_schedulable (ts: seq sporadic_task) :=
       edf_claimed_bounds ts != None.
 
     (* In the following section, we prove several helper lemmas about the
@@ -205,8 +205,8 @@ Module ResponseTimeIterationEDF.
        of the iteration at (max_steps ts) is equal to the value at (max_steps ts) + 1. *)
     Section Convergence.
 
-      (* Consider any valid task set. *)
-      Variable ts: taskset_of sporadic_task.
+      (* Consider any sequence of tasks with valid parameters. *)
+      Variable ts: seq sporadic_task.
       Hypothesis H_valid_task_parameters:
         valid_sporadic_taskset task_cost task_period task_deadline ts.
       
@@ -395,11 +395,11 @@ Module ResponseTimeIterationEDF.
 
           assert (SUBST: forall l delta,
                     \sum_(j <- l | let '(tsk_other, _) := j in
-                      jldp_can_interfere_with tsk_i tsk_other)
+                      different_task tsk_i tsk_other)
                         (let '(tsk_other, R_other) := j in
                           interference_bound_edf task_cost task_period task_deadline tsk_i delta
                             (tsk_other, R_other)) =
-                    \sum_(j <- l | jldp_can_interfere_with tsk_i (fst j))
+                    \sum_(j <- l | different_task tsk_i (fst j))
                       interference_bound_edf task_cost task_period task_deadline tsk_i delta j).
           {
             intros l x; clear -l.
@@ -444,13 +444,13 @@ Module ResponseTimeIterationEDF.
           }
           move: GE_COST => /allP GE_COST.
 
-          assert (LESUM: \sum_(j <- x1' | jldp_can_interfere_with tsk_i (fst j))
-                        interference_bound_edf task_cost task_period task_deadline tsk_i delta j <=                                  \sum_(j <- x2' | jldp_can_interfere_with tsk_i (fst j))
+          assert (LESUM: \sum_(j <- x1' | different_task tsk_i (fst j))
+                        interference_bound_edf task_cost task_period task_deadline tsk_i delta j <=                                  \sum_(j <- x2' | different_task tsk_i (fst j))
                         interference_bound_edf task_cost task_period task_deadline tsk_i delta' j).
           {
             set elem := (tsk0, R0); rewrite 2!(big_nth elem).
             rewrite -SIZE.
-            rewrite big_mkcond [\sum_(_ <- _ | jldp_can_interfere_with _ _)_]big_mkcond.
+            rewrite big_mkcond [\sum_(_ <- _ | different_task _ _)_]big_mkcond.
             rewrite big_seq_cond [\sum_(_ <- _ | true) _]big_seq_cond.
             apply leq_sum; intros j; rewrite andbT; intros INj.
             rewrite mem_iota add0n subn0 in INj; move: INj => /andP [_ INj].
@@ -459,7 +459,7 @@ Module ResponseTimeIterationEDF.
               have MAP := @nth_map _ elem _ tsk0 (fun x => fst x).
               by rewrite -2?MAP -?SIZE //; f_equal.
             } rewrite -FSTeq.
-            destruct (jldp_can_interfere_with tsk_i (fst (nth elem x1' j))) eqn:INTERF;
+            destruct (different_task tsk_i (fst (nth elem x1' j))) eqn:INTERF;
               last by done.
             {
               exploit (LE elem); [by rewrite /= SIZE | | intro LEj].
@@ -485,7 +485,7 @@ Module ResponseTimeIterationEDF.
               by apply interference_bound_edf_monotonic.
             }
           }
-          destruct (jldp_can_interfere_with tsk_i tsk0) eqn:INTERFtsk0; last by done.
+          destruct (different_task tsk_i tsk0) eqn:INTERFtsk0; last by done.
           apply leq_add; last by done.
           {             
             exploit (LE (tsk0, R0)); [by rewrite /= SIZE | | intro LEj];
@@ -816,15 +816,13 @@ Module ResponseTimeIterationEDF.
       Qed.
 
       (* Therefore, with regard to the response-time bound recurrence, ...*)
-      Let rt_recurrence (tsk: sporadic_task) (rt_bounds: seq task_with_response_time) (R: time) :=
-        task_cost tsk + div_floor (I rt_bounds tsk R) num_cpus.
       
       (* ..., the individual response-time bounds (elements of the list) are also fixed points. *)
       Theorem edf_claimed_bounds_finds_fixed_point_for_each_bound :
         forall tsk R rt_bounds,
           edf_claimed_bounds ts = Some rt_bounds ->
           (tsk, R) \in rt_bounds ->
-          R = rt_recurrence tsk rt_bounds R.
+          R = edf_response_time_bound rt_bounds tsk R.
       Proof.
         intros tsk R rt_bounds SOME IN.
         have CONV := edf_claimed_bounds_finds_fixed_point_of_list rt_bounds.
@@ -854,17 +852,14 @@ Module ResponseTimeIterationEDF.
 
     Section MainProof.
 
-      (* Consider a task set ts. *)
+      (* Consider a task set ts where... *)
       Variable ts: taskset_of sporadic_task.
       
-      (* Assume the task set has no duplicates, ... *)
-      Hypothesis H_ts_is_a_set: uniq ts.
-
-      (* ...all tasks have valid parameters, ... *)
+      (* ...all tasks have valid parameters ... *)
       Hypothesis H_valid_task_parameters:
         valid_sporadic_taskset task_cost task_period task_deadline ts.
 
-      (* ...constrained deadlines, ...*)
+      (* ...and constrained deadlines.*)
       Hypothesis H_constrained_deadlines:
         forall tsk, tsk \in ts -> task_deadline tsk <= task_period tsk.
 
diff --git a/analysis/parallel/bertogna_edf_theory.v b/analysis/parallel/bertogna_edf_theory.v
index 8448eaf546f939352f1c84c1c972f12e63e2a21c..3a7fafdff6f5523cc9b2b722d580ea1579bfb7e1 100644
--- a/analysis/parallel/bertogna_edf_theory.v
+++ b/analysis/parallel/bertogna_edf_theory.v
@@ -12,8 +12,11 @@ Module ResponseTimeAnalysisEDF.
          Priority SporadicTaskArrival WorkloadBound InterferenceBoundEDF
          Interference Platform.
 
-  (* In this section, we prove that Bertogna and Cirinei's RTA yields
-     safe response-time bounds. *)
+  (* In this section, we prove that any fixed point in Bertogna and
+     Cirinei's RTA for EDF scheduling modified to consider (potentially)
+     parallel jobs yields a safe response-time bound. This is an extension
+     of the analysis found in Chapter 17.1.2 of Baruah et al.'s book
+     Multiprocessor Scheduling for Real-time Systems. *)
   Section ResponseTimeBound.
 
     Context {sporadic_task: eqType}.
@@ -36,7 +39,19 @@ Module ResponseTimeAnalysisEDF.
       forall (j: JobIn arr_seq),
         valid_sporadic_job task_cost task_deadline job_cost job_deadline job_task j.
 
-    (* Consider any schedule such that...*)
+    (* Assume that we have a task set where all tasks have valid
+       parameters and constrained deadlines, ... *)
+    Variable ts: taskset_of sporadic_task.
+    Hypothesis H_valid_task_parameters:
+      valid_sporadic_taskset task_cost task_period task_deadline ts.
+    Hypothesis H_constrained_deadlines:
+      forall tsk, tsk \in ts -> task_deadline tsk <= task_period tsk.
+
+    (* ... and that all jobs in the arrival sequence come from the task set. *)
+    Hypothesis H_all_jobs_from_taskset:
+      forall (j: JobIn arr_seq), job_task j \in ts.
+
+    (* Next, consider any schedule such that...*)
     Variable num_cpus: nat.
     Variable sched: schedule num_cpus arr_seq.
 
@@ -48,33 +63,27 @@ Module ResponseTimeAnalysisEDF.
       completed_jobs_dont_execute job_cost sched.
 
     (* Assume that there exists at least one processor. *)
-    Hypothesis H_at_least_one_cpu :
-      num_cpus > 0.
-
-    (* Assume that we have a task set ts such that all jobs come from
-       the task set, and all tasks have valid parameters and
-       constrained deadlines. *)
-    Variable ts: taskset_of sporadic_task.
-    Hypothesis H_all_jobs_from_taskset:
-      forall (j: JobIn arr_seq), job_task j \in ts.
-    Hypothesis H_valid_task_parameters:
-      valid_sporadic_taskset task_cost task_period task_deadline ts.
-    Hypothesis H_constrained_deadlines:
-      forall tsk, tsk \in ts -> task_deadline tsk <= task_period tsk.
+    Hypothesis H_at_least_one_cpu: num_cpus > 0.
 
+    (* Assume that the schedule is a work-conserving EDF schedule. *)
+    Hypothesis H_work_conserving: work_conserving job_cost sched.
+    Hypothesis H_edf_policy: enforces_JLDP_policy job_cost sched (EDF job_deadline).
+    
+    (* Let's define some local names to avoid passing many parameters. *)
     Let no_deadline_is_missed_by_tsk (tsk: sporadic_task) :=
       task_misses_no_deadline job_cost job_deadline job_task sched tsk.
     Let response_time_bounded_by (tsk: sporadic_task) :=
       is_response_time_bound_of_task job_cost job_task tsk sched.
 
-    (* Assume a known response-time bound R is known...  *)
+    (* Next we consider the response-time recurrence.
+       Assume that a response-time bound R is known...  *)
     Let task_with_response_time := (sporadic_task * time)%type.
     Variable rt_bounds: seq task_with_response_time.
 
-    (* ...for any task in the task set. *)
+    (* ...for any task in the task set, ... *)
     Hypothesis H_rt_bounds_contains_all_tasks: unzip1 rt_bounds = ts.
 
-    (* Also, assume that R is a fixed-point of the response-time recurrence, ... *)
+    (* ...where R is a fixed-point of the response-time recurrence, ... *)
     Let I (tsk: sporadic_task) (delta: time) :=
       total_interference_bound_edf task_cost task_period task_deadline tsk rt_bounds delta.
     Hypothesis H_response_time_is_fixed_point :
@@ -87,16 +96,7 @@ Module ResponseTimeAnalysisEDF.
       forall tsk_other R,
         (tsk_other, R) \in rt_bounds -> R <= task_deadline tsk_other.
 
-    (* Assume that we have a work-conserving EDF scheduler. *)
-    Hypothesis H_work_conserving: work_conserving job_cost sched.
-    Hypothesis H_edf_policy: enforces_JLDP_policy job_cost sched (EDF job_deadline).
-    
-    (* Assume that the task set has no duplicates. This is required to
-       avoid problems when counting tasks (for example, when stating
-       that the number of interfering tasks is at most num_cpus). *)
-    Hypothesis H_ts_is_a_set : uniq ts.
-
-    (* In order to prove that R is a response-time bound, we first present some lemmas. *)
+    (* In order to prove that R is a response-time bound, we first provide some lemmas. *)
     Section Lemmas.
 
       (* Let (tsk, R) be any task to be analyzed, with its response-time bound R. *)
@@ -111,7 +111,7 @@ Module ResponseTimeAnalysisEDF.
       (* Assume that job j did not complete on time, ... *)
       Hypothesis H_j_not_completed: ~~ completed job_cost sched j (job_arrival j + R).
 
-      (* and that it is the first job not to satisfy its response-time bound. *)
+      (* ...and that it is the first job not to satisfy its response-time bound. *)
       Hypothesis H_all_previous_jobs_completed_on_time :
         forall (j_other: JobIn arr_seq) tsk_other R_other,
           job_task j_other = tsk_other ->
@@ -124,7 +124,7 @@ Module ResponseTimeAnalysisEDF.
         task_interference job_cost job_task sched j
                           tsk_other (job_arrival j) (job_arrival j + R).
 
-      (* and X the total interference incurred by job j due to any task. *)
+      (* ...and X the total interference incurred by job j due to any task. *)
       Let X := total_interference job_cost sched j (job_arrival j) (job_arrival j + R).
 
       (* Recall Bertogna and Cirinei's workload bound ... *)
@@ -139,9 +139,14 @@ Module ResponseTimeAnalysisEDF.
       Let interference_bound (tsk_other: sporadic_task) (R_other: time) :=
         interference_bound_edf task_cost task_period task_deadline tsk R (tsk_other, R_other). 
       
-      (* Also, let ts_interf be the subset of tasks that interfere with tsk. *)
-      Let ts_interf := [seq tsk_other <- ts | jldp_can_interfere_with tsk tsk_other].
+      (* Based on the definition of a different task, ... *)
+      Let other_task := different_task tsk.
+
+      (* ...let other_tasks denote the set of tasks that are different from tsk. *)
+      Let other_tasks :=
+        [seq tsk_other <- ts | other_task tsk_other].
 
+      (* Now we establish results the interfering tasks. *)
       Section LemmasAboutInterferingTasks.
         
         (* Let (tsk_other, R_other) be any pair of higher-priority task and
@@ -153,7 +158,7 @@ Module ResponseTimeAnalysisEDF.
         (* Note that tsk_other is in task set ts ...*)
         Lemma bertogna_edf_tsk_other_in_ts: tsk_other \in ts.
         Proof.
-          by rewrite -H_rt_bounds_contains_all_tasks; apply/mapP; exists (tsk_other, R_other).
+          by rewrite set_mem -H_rt_bounds_contains_all_tasks; apply/mapP; exists (tsk_other, R_other).
         Qed.
 
         (* Also, R_other is larger than the cost of tsk_other. *)
@@ -253,9 +258,11 @@ Module ResponseTimeAnalysisEDF.
 
       (* 1) Next, we prove that the sum of the interference of each task is equal
             to the total interference multiplied by the number of processors. This
-            holds because interference only occurs when all processors are busy. *)
-      Lemma bertogna_edf_all_cpus_busy :
-        \sum_(tsk_k <- ts_interf) x tsk_k = X * num_cpus.
+            holds because interference only occurs when all processors are busy.
+            With this lemma we can relate per-task interference with the total
+            interference incurred by j (backlogged time). *)
+      Lemma bertogna_edf_interference_on_all_cpus :
+        \sum_(tsk_k <- other_tasks) x tsk_k = X * num_cpus.
       Proof.
         rename H_all_jobs_from_taskset into FROMTS,
                H_valid_task_parameters into PARAMS,
@@ -286,7 +293,7 @@ Module ResponseTimeAnalysisEDF.
         apply eq_bigr; intros cpu _.
         specialize (WORK j t BACK cpu); des.
         move: WORK => /eqP SCHED.
-        rewrite (bigD1_seq (job_task j_other)) /=; last by rewrite filter_uniq.
+        rewrite (bigD1_seq (job_task j_other)) /=; last by rewrite filter_uniq; destruct ts.
         {
           rewrite (eq_bigr (fun i => 0));
             last by intros i DIFF; rewrite /task_scheduled_on SCHED;apply/eqP;rewrite eqb0 eq_sym.
@@ -294,7 +301,7 @@ Module ResponseTimeAnalysisEDF.
           by unfold task_scheduled_on; rewrite SCHED.
         }
         rewrite mem_filter; apply/andP; split; last by apply FROMTS.
-        unfold jldp_can_interfere_with.
+        unfold different_task.
         apply/eqP; red; intro SAMEtsk.
         assert (SCHED': scheduled sched j_other t).
         {
@@ -332,31 +339,30 @@ apply scheduled_implies_pending with (job_cost0 := job_cost) in PENDING; try (by
         }
       Qed.
 
-      (* 2) Now, we prove that the Bertogna's interference bound
-            is not enough to cover the sum of the "minimum" term over
-            all tasks (artifact of the proof by contradiction). *)
+      (* 2) Next, using lemmas (0) and (1) we prove that the reduction-based
+            interference bound is not enough to cover the sum of the minima over all tasks
+            (artifact of the proof by contradiction). *)
       Lemma bertogna_edf_sum_exceeds_total_interference:
-        \sum_((tsk_other, R_other) <- rt_bounds | jldp_can_interfere_with tsk tsk_other)
+        \sum_((tsk_other, R_other) <- rt_bounds | other_task tsk_other)
           x tsk_other > I tsk R.
       Proof.
         have GE_COST := bertogna_edf_R_other_ge_cost.
-        have ALLBUSY := bertogna_edf_all_cpus_busy.
+        have ALLBUSY := bertogna_edf_interference_on_all_cpus.
         have TOOMUCH := bertogna_edf_too_much_interference.
         rename H_rt_bounds_contains_all_tasks into UNZIP,
                H_response_time_is_fixed_point into REC.
-        apply leq_trans with (n := \sum_(tsk_other <- ts_interf) x tsk_other);
+        apply leq_trans with (n := \sum_(tsk_other <- other_tasks) x tsk_other);
           last first.
         {
           rewrite (eq_bigr (fun i => x (fst i))); last by ins; destruct i.
-          assert (FILTER: filter (jldp_can_interfere_with tsk) (unzip1 rt_bounds) =
-                          filter (jldp_can_interfere_with tsk) ts).
+          assert (FILTER: filter (different_task tsk) (unzip1 rt_bounds) =
+                          filter (different_task tsk) ts).
             by f_equal.
-          unfold ts_interf; rewrite -FILTER; clear FILTER.
+          unfold other_tasks; rewrite -FILTER; clear FILTER.
           rewrite -[\sum_(_ <- rt_bounds | _)_]big_filter.
           assert (SUBST: [seq i <- rt_bounds
-                 | let '(tsk_other, _) := i in
-                        jldp_can_interfere_with tsk tsk_other] = [seq i <- rt_bounds
-                           | jldp_can_interfere_with tsk (fst i)]).
+                           | let '(tsk_other, _) := i in other_task tsk_other] =
+                         [seq i <- rt_bounds| other_task (fst i)]).
           {
             by apply eq_filter; red; intro i; destruct i.
           } rewrite SUBST; clear SUBST.         
@@ -373,9 +379,11 @@ apply scheduled_implies_pending with (job_cost0 := job_cost) in PENDING; try (by
         by rewrite leq_mul2r; apply/orP; right; apply TOOMUCH.
       Qed.
 
-      (* 3) After concluding that the sum of the minimum exceeds (R - e_i + 1),
-            we prove that there exists a tuple (tsk_k, R_k) such that
-            min (x_k, R - e_i + 1) > min (W_k, edf_bound, R - e_i + 1). *)
+      (* 3) After concluding that the sum of the minima exceeds (R - e_i + 1),
+            we prove that there exists a tuple (tsk_k, R_k) that satisfies
+            min (x_k, R - e_i + 1) > min (W_k, I_edf, R - e_i + 1).
+            This implies that either x_k > W_k or x_k > I_edf, which is a contradiction,
+            since both W_k and I_edf are valid task interference bounds. *)
       Lemma bertogna_edf_exists_task_that_exceeds_bound :
         exists tsk_other R_other,
           (tsk_other, R_other) \in rt_bounds /\
@@ -387,7 +395,7 @@ apply scheduled_implies_pending with (job_cost0 := job_cost) in PENDING; try (by
         rename H_rt_bounds_contains_all_tasks into UNZIP.
         assert (HAS: has (fun tup : task_with_response_time =>
                        let (tsk_other, R_other) := tup in
-                         (tsk_other \in ts) && jldp_can_interfere_with tsk tsk_other &&
+                         (tsk_other \in ts) && other_task tsk_other &&
                         (x tsk_other  > interference_bound tsk_other R_other))
                          rt_bounds).
         {
diff --git a/analysis/parallel/bertogna_fp_comp.v b/analysis/parallel/bertogna_fp_comp.v
index 58beffe3dfbbd92ffbdc246df921df30255d25a6..0656ea907532feef14f5728cf3742e3670f9dfec 100644
--- a/analysis/parallel/bertogna_fp_comp.v
+++ b/analysis/parallel/bertogna_fp_comp.v
@@ -7,7 +7,7 @@ Module ResponseTimeIterationFP.
   Import ResponseTimeAnalysisFP.
 
   (* In this section, we define the algorithm of Bertogna and Cirinei's
-     response-time analysis for FP scheduling. *)
+     response-time analysis for FP scheduling with parallel jobs. *)
   Section Analysis.
     
     Context {sporadic_task: eqType}.
@@ -47,8 +47,7 @@ Module ResponseTimeIterationFP.
       iter step
         (fun t => task_cost tsk +
                   div_floor
-                    (total_interference_bound_fp task_cost task_period tsk
-                                                R_prev t higher_priority)
+                    (total_interference_bound_fp task_cost task_period R_prev t)
                     num_cpus)
         (task_cost tsk).
 
@@ -75,12 +74,12 @@ Module ResponseTimeIterationFP.
     (* The response-time analysis for a given task set is defined
        as a left-fold (reduce) based on the function above.
        This either returns a list of task and response-time bounds, or None. *)
-    Definition fp_claimed_bounds (ts: taskset_of sporadic_task) :=
+    Definition fp_claimed_bounds (ts: seq sporadic_task) :=
       foldl fp_bound_of_task (Some [::]) ts.
 
     (* The schedulability test simply checks if we got a list of
        response-time bounds (i.e., if the computation did not fail). *)
-    Definition fp_schedulable (ts: taskset_of sporadic_task) :=
+    Definition fp_schedulable (ts: seq sporadic_task) :=
       fp_claimed_bounds ts != None.
     
     (* In the following section, we prove several helper lemmas about the
@@ -258,8 +257,8 @@ Module ResponseTimeIterationFP.
       Lemma per_task_rta_fold :
         forall tsk rt_bounds,
           task_cost tsk +
-           div_floor (total_interference_bound_fp task_cost task_period tsk rt_bounds
-                     (per_task_rta tsk rt_bounds (max_steps tsk)) higher_priority) num_cpus
+           div_floor (total_interference_bound_fp task_cost task_period rt_bounds
+                     (per_task_rta tsk rt_bounds (max_steps tsk))) num_cpus
           = per_task_rta tsk rt_bounds (max_steps tsk).+1.
       Proof.
           by done.
@@ -274,14 +273,13 @@ Module ResponseTimeIterationFP.
       (* Consider a list of previous tasks and a task tsk to be analyzed. *)
       Variable ts: taskset_of sporadic_task.
 
-      (* Assume that the task set doesn't contain duplicates and is sorted by priority, ... *)
-      Hypothesis H_task_set_is_a_set: uniq ts.
+      (* Assume that the task set is sorted by unique priorities, ... *)
       Hypothesis H_task_set_is_sorted: sorted higher_priority ts.
+      Hypothesis H_task_set_has_unique_priorities:
+        FP_is_antisymmetric_over_task_set higher_priority ts.
 
-      (* ...the priority order is strict (<), ...*)
-      Hypothesis H_priority_irreflexive: irreflexive higher_priority.
-      Hypothesis H_priority_transitive: transitive higher_priority.
-      Hypothesis H_priority_antissymetric: antisymmetric higher_priority.
+      (* ...the priority order is transitive, ...*)
+      Hypothesis H_priority_transitive: FP_is_transitive higher_priority.
       
       (* ... and that the response-time analysis succeeds. *)
       Variable hp_bounds: seq task_with_response_time.
@@ -292,61 +290,20 @@ Module ResponseTimeIterationFP.
       Variable elem: sporadic_task.
       Let TASK := nth elem ts.
                     
-      (* Then, the tasks in the prefix of fp_claimed_bounds are exactly interfering tasks
-         under FP scheduling.*)
-      Lemma fp_claimed_bounds_interf:
-        forall idx,
+      (* We prove that higher-priority tasks have smaller index. *)
+      Lemma fp_claimed_bounds_hp_tasks_have_smaller_index :
+        forall hp_idx idx,
+          hp_idx < size ts ->
           idx < size ts ->
-          [seq tsk_hp <- ts | fp_can_interfere_with higher_priority (TASK idx) tsk_hp] = take idx ts.
+          hp_idx != idx ->
+          higher_priority (TASK hp_idx) (TASK idx) ->
+          hp_idx < idx.
       Proof.
-        rename H_task_set_is_sorted into SORT,
-               H_task_set_is_a_set into UNIQ,
-               H_priority_antissymetric into ANTI,
-               H_priority_irreflexive into IRR.
-        induction idx.
-        {
-          intros LT.
-          destruct ts as [| tsk0 ts']; [by done | simpl in SORT].
-          unfold fp_can_interfere_with; rewrite /= eq_refl andbF.
-          apply eq_trans with (y := filter pred0 ts');
-            last by apply filter_pred0.
-          apply eq_in_filter; red; intros x INx; rewrite /TASK /=.
-          destruct (x != tsk0) eqn:SAME; rewrite ?andbT ?andbF //.
-          apply negbTE; apply/negP; unfold not; intro HP.
-          move: SAME => /eqP SAME; apply SAME; clear SAME.
-          apply ANTI; apply/andP; split; first by done.
-          apply order_path_min in SORT; last by done.
-          by move: SORT => /allP SORT; apply SORT.
-        }
-        {
-          intros LT.
-          generalize LT; intro LT'; apply ltSnm in LT.
-          feed IHidx; first by done.
-          rewrite -filter_idx_le_takeS //.
-          apply eq_in_filter; red; intros x INx.
-          unfold fp_can_interfere_with.
-          generalize INx; intro SUBST; apply nth_index with (x0 := elem) in SUBST.
-          rewrite -SUBST; clear SUBST.
-          rewrite index_uniq; [ | by rewrite index_mem | by done].
-          apply/idP/idP.
-          {
-            move => /andP [HP DIFF].
-            unfold TASK in *.
-            apply sorted_uniq_rel_implies_le_idx in HP; try (by done);
-              last by rewrite index_mem.
-            by rewrite leq_eqVlt in HP; move: HP => /orP [/eqP SAME | LESS];
-                first by rewrite SAME eq_refl in DIFF.
-          }
-          {
-            intros LEidx; apply/andP; split;
-              first by apply sorted_lt_idx_implies_rel.
-            apply/eqP; red; intro BUG.
-            eapply f_equal with (f := fun x => index x ts) in BUG.
-            rewrite nth_index in BUG; last by done.
-            rewrite BUG in LEidx.
-            by rewrite index_uniq // ltnn in LEidx.
-          }
-        }
+        unfold TASK; clear TASK.
+        rename ts into ts'; destruct ts' as [ts UNIQ]; simpl in *.
+        intros hp_idx idx LThp LT NEQ HP.
+        rewrite ltn_neqAle; apply/andP; split; first by done.
+        by apply sorted_rel_implies_le_idx with (leT := higher_priority) (s := ts) (x0 := elem).
       Qed.
       
     End HighPriorityTasks.
@@ -355,7 +312,7 @@ Module ResponseTimeIterationFP.
     Section Convergence.
 
       (* Consider any set of higher-priority tasks. *)
-      Variable ts_hp: taskset_of sporadic_task.
+      Variable ts_hp: seq sporadic_task.
 
       (* Assume that the response-time analysis succeeds for the higher-priority tasks. *)
       Variable rt_bounds: seq task_with_response_time.
@@ -384,22 +341,19 @@ Module ResponseTimeIterationFP.
         intros x1 x2 LEx; unfold f, per_task_rta.
         apply fun_mon_iter_mon; [by ins | by ins; apply leq_addr |].
         clear LEx x1 x2; intros x1 x2 LEx.
-        unfold div_floor, total_interference_bound_fp.
-        rewrite big_seq_cond [\sum_(i <- _ | let '(tsk_other, _) := i in
-                                 _ && (tsk_other != tsk))_]big_seq_cond.
-        rewrite leq_add2l leq_div2r // leq_sum //.
-
-        intros i; destruct (i \in rt_bounds) eqn:HP; last by rewrite andFb.
-        destruct i as [i R]; intros _.
-        have GE_COST := fp_claimed_bounds_ge_cost ts_hp rt_bounds i R SOME.
+        rewrite leq_add2l leq_div2r //.
+        unfold total_interference_bound_fp.
+        rewrite big_seq_cond.
+        rewrite [\sum_(_ <- _ | true) _]big_seq_cond.
+        apply leq_sum; move => i /andP [IN _].
+        destruct i as [i R].
+        have GE_COST := fp_claimed_bounds_ge_cost ts_hp rt_bounds i R SOME IN.
         have UNZIP := fp_claimed_bounds_unzip ts_hp rt_bounds SOME.
-        assert (IN: i \in ts_hp).
-        {
-          by rewrite -UNZIP; apply/mapP; exists (i,R).
-        }
-        unfold interference_bound_generic; simpl.
-        exploit (VALID i); [by rewrite mem_rcons in_cons IN orbT | ins; des].
-        by apply W_monotonic; try (by ins); apply leqnn.
+unfold interference_bound_generic; simpl.
+        apply W_monotonic; try (by done).
+        have INts: i \in ts_hp by rewrite -UNZIP; apply/mapP; exists (i, R).
+        by exploit (VALID i);
+          [by rewrite mem_rcons in_cons INts orbT | by ins; des].
       Qed.
 
       (* If the iteration converged at an earlier step, then it remains stable. *)
@@ -507,29 +461,22 @@ Module ResponseTimeIterationFP.
       (* Consider a task set ts. *)
       Variable ts: taskset_of sporadic_task.
       
-      (* Assume that higher_priority is a total strict order (<).
-         TODO: it doesn't have to be total over the entire domain, but
-         only within the task set.
-         But to weaken the hypothesis, we need to re-prove some lemmas
-         from ssreflect. *)
-      Hypothesis H_irreflexive: irreflexive higher_priority.
-      Hypothesis H_transitive: transitive higher_priority.
-      Hypothesis H_unique_priorities: antisymmetric higher_priority.
-      Hypothesis H_total: total higher_priority.
-
-      (* Assume the task set has no duplicates, ... *)
-      Hypothesis H_ts_is_a_set: uniq ts.
-
-      (* ...all tasks have valid parameters, ... *)
+      (* Assume that all tasks have valid parameters, ... *)
       Hypothesis H_valid_task_parameters:
         valid_sporadic_taskset task_cost task_period task_deadline ts.
 
-      (* ...constrained deadlines, ...*)
+      (* ...and constrained deadlines.*)
       Hypothesis H_constrained_deadlines:
         forall tsk, tsk \in ts -> task_deadline tsk <= task_period tsk.
 
-      (* ...and tasks are ordered by increasing priorities. *)
-      Hypothesis H_sorted_ts: sorted higher_priority ts.
+       (* Assume that the task set is totally ordered by unique priorities,
+          and that the priority order is transitive. *)
+      Hypothesis H_task_set_is_sorted: sorted higher_priority ts.
+      Hypothesis H_task_set_has_unique_priorities:
+        FP_is_antisymmetric_over_task_set higher_priority ts.
+      Hypothesis H_priority_is_total:
+        FP_is_total_over_task_set higher_priority ts.
+      Hypothesis H_priority_transitive: FP_is_transitive higher_priority.
 
       (* Next, consider any arrival sequence such that...*)
       Context {arr_seq: arrival_sequence Job}.
@@ -549,8 +496,7 @@ Module ResponseTimeIterationFP.
       
       (* Then, consider any platform with at least one CPU such that...*)
       Variable sched: schedule num_cpus arr_seq.
-      Hypothesis H_at_least_one_cpu :
-        num_cpus > 0.
+      Hypothesis H_at_least_one_cpu: num_cpus > 0.
 
       (* ...jobs only execute after they arrived and no longer
          than their execution costs,... *)
@@ -650,11 +596,37 @@ Module ResponseTimeIterationFP.
         {
           cut (NTH idx \in hp_bounds = true);
             [intros IN | by apply mem_nth].
-          by rewrite -UNZIP; apply/mapP; exists (TASK idx, RESP idx); rewrite PAIR.
+          by rewrite set_mem -UNZIP; apply/mapP; exists (TASK idx, RESP idx); rewrite PAIR.
         }
         {
-          unfold unzip1 in *; rewrite map_take UNZIP SUBST //.
-          by apply fp_claimed_bounds_interf with (hp_bounds := hp_bounds); rewrite -?SIZE.
+          intros hp_tsk IN INTERF.
+          exists (RESP (index hp_tsk ts)).
+          move: (IN) => INDEX; apply nth_index with (x0 := tsk) in INDEX.
+          rewrite -{1}[hp_tsk]INDEX -SUBST; last by rewrite SIZE index_mem.
+          assert (UNIQ: uniq hp_bounds).
+          {
+            apply map_uniq with (f := fst); unfold unzip1 in *; rewrite UNZIP.
+            by destruct ts.
+          }
+          rewrite -filter_idx_lt_take //.
+          {
+            rewrite PAIR mem_filter; apply/andP; split;
+              last by apply mem_nth; rewrite SIZE index_mem.
+            {
+              rewrite /NTH index_uniq; [| by rewrite SIZE index_mem | by done ].
+              {
+                move: INTERF => /andP [HP NEQ].
+                apply fp_claimed_bounds_hp_tasks_have_smaller_index with
+                  (ts := ts) (elem := tsk) (hp_bounds := hp_bounds);
+                  try (by done);
+                  [by rewrite index_mem | by rewrite -SIZE | | by rewrite INDEX -SUBST].
+                apply/eqP; intro BUG; subst idx.
+                rewrite SUBST -{1}INDEX in NEQ;
+                  first by rewrite eq_refl in NEQ.
+                by rewrite SIZE index_mem INDEX.
+              }
+            }
+          }
         }
         {
           rewrite REC per_task_rta_fold.
@@ -685,7 +657,7 @@ Module ResponseTimeIterationFP.
         feed (UNZIP rt_bounds); first by done.
         assert (EX: exists R, (tsk, R) \in rt_bounds).
         {
-          rewrite -UNZIP in INtsk; move: INtsk => /mapP EX.
+          rewrite set_mem -UNZIP in INtsk; move: INtsk => /mapP EX.
           by destruct EX as [p]; destruct p as [tsk' R]; simpl in *; subst tsk'; exists R.
         } des.
         exploit (RLIST tsk R); [by ins | by apply JOBtsk | intro COMPLETED].
diff --git a/analysis/parallel/bertogna_fp_theory.v b/analysis/parallel/bertogna_fp_theory.v
index 243cb9b33168511d13ef067c1cfe90ff06a2ffe9..64db0dab1d0555c677e850f6d24fce68472312ea 100644
--- a/analysis/parallel/bertogna_fp_theory.v
+++ b/analysis/parallel/bertogna_fp_theory.v
@@ -1,16 +1,24 @@
 Require Import rt.util.all.
 Require Import rt.model.basic.task rt.model.basic.job rt.model.basic.task_arrival
-               rt.model.basic.schedule rt.model.basic.platform rt.model.basic.platform_fp
-               rt.model.basic.workload rt.model.basic.schedulability rt.model.basic.priority
-               rt.model.basic.response_time rt.model.basic.interference.
+               rt.model.basic.schedule rt.model.basic.platform
+               rt.model.basic.constrained_deadlines
+               rt.model.basic.workload rt.model.basic.schedulability
+               rt.model.basic.priority rt.model.basic.response_time
+               rt.model.basic.interference.
 Require Import rt.analysis.parallel.workload_bound rt.analysis.parallel.interference_bound_fp.
 From mathcomp Require Import ssreflect ssrbool eqtype ssrnat seq fintype bigop div path.
 
 Module ResponseTimeAnalysisFP.
 
-  Export Job SporadicTaskset ScheduleOfSporadicTask Workload Interference InterferenceBoundFP
-         Platform PlatformFP Schedulability ResponseTime Priority SporadicTaskArrival WorkloadBound.
+  Export Job SporadicTaskset ScheduleOfSporadicTask Workload Interference
+         InterferenceBoundFP Platform Schedulability ResponseTime
+         Priority SporadicTaskArrival WorkloadBound ConstrainedDeadlines.
     
+  (* In this section, we prove that any fixed point in Bertogna and
+     Cirinei's RTA for FP scheduling modified to consider (potentially)
+     parallel jobs yields a safe response-time bound. This is an extension
+     of the analysis found in Chapter 18.2 of Baruah et al.'s book
+     Multiprocessor Scheduling for Real-time Systems. *)
   Section ResponseTimeBound.
 
     Context {sporadic_task: eqType}.
@@ -33,7 +41,19 @@ Module ResponseTimeAnalysisFP.
       forall (j: JobIn arr_seq),
         valid_sporadic_job task_cost task_deadline job_cost job_deadline job_task j.
 
-    (* Consider any schedule such that...*)
+    (* Assume that we have a task set where all tasks have valid
+       parameters and constrained deadlines, ... *)
+    Variable ts: taskset_of sporadic_task.
+    Hypothesis H_valid_task_parameters:
+      valid_sporadic_taskset task_cost task_period task_deadline ts.
+    Hypothesis H_constrained_deadlines:
+      forall tsk, tsk \in ts -> task_deadline tsk <= task_period tsk.
+
+    (* ... and that all jobs in the arrival sequence come from the task set. *)
+    Hypothesis H_all_jobs_from_taskset:
+      forall (j: JobIn arr_seq), job_task j \in ts.
+
+    (* Next, consider any schedule such that...*)
     Variable num_cpus: nat.
     Variable sched: schedule num_cpus arr_seq.
 
@@ -44,67 +64,61 @@ Module ResponseTimeAnalysisFP.
     Hypothesis H_completed_jobs_dont_execute:
       completed_jobs_dont_execute job_cost sched.
 
+    (* Consider a given FP policy, ... *)
+    Variable higher_eq_priority: FP_policy sporadic_task.
+    
+    (* ... and assume that the schedule is an APA work-conserving
+       schedule that enforces this policy. *)
+    Hypothesis H_work_conserving: work_conserving job_cost sched.
+    Hypothesis H_enforces_FP_policy:
+      enforces_FP_policy job_cost job_task sched higher_eq_priority.
+    
     (* Assume that there exists at least one processor. *)
-    Hypothesis H_at_least_one_cpu :
-      num_cpus > 0.
-
-    (* Assume that we have a task set (with no duplicates) where all jobs
-       come from the task set and all tasks have valid parameters and constrained deadlines. *)
-    Variable ts: taskset_of sporadic_task.
-    Hypothesis H_ts_is_a_set: uniq ts.
-    Hypothesis H_all_jobs_from_taskset:
-      forall (j: JobIn arr_seq), job_task j \in ts.
-    Hypothesis H_valid_task_parameters:
-      valid_sporadic_taskset task_cost task_period task_deadline ts.
-    Hypothesis H_constrained_deadlines:
-      forall tsk, tsk \in ts -> task_deadline tsk <= task_period tsk.
-
-    (* Next, consider a task tsk that is to be analyzed. *)
-    Variable tsk: sporadic_task.
-    Hypothesis task_in_ts: tsk \in ts.
+    Hypothesis H_at_least_one_cpu: num_cpus > 0.
 
+    (* Let's define some local names to avoid passing many parameters. *)   
     Let no_deadline_is_missed_by_tsk (tsk: sporadic_task) :=
       task_misses_no_deadline job_cost job_deadline job_task sched tsk.
     Let response_time_bounded_by (tsk: sporadic_task) :=
       is_response_time_bound_of_task job_cost job_task tsk sched.
 
-    (* Assume a known response-time bound for any interfering task *)
-    Let task_with_response_time := (sporadic_task * time)%type.
-    Variable hp_bounds: seq task_with_response_time.
-    
-    (* For FP scheduling, assume there exists a fixed task priority. *)
-    Variable higher_eq_priority: FP_policy sporadic_task.
+    (* Next, we consider the response-time recurrence.
+       Let tsk be a task in ts that is to be analyzed. *)
+    Variable tsk: sporadic_task.
+    Hypothesis task_in_ts: tsk \in ts.
 
-    Let can_interfere_with_tsk := fp_can_interfere_with higher_eq_priority tsk.
-    
-    (* Assume that hp_bounds has exactly the tasks that interfere with tsk,... *)
-    Hypothesis H_hp_bounds_has_interfering_tasks:
-      [seq tsk_hp <- ts | can_interfere_with_tsk tsk_hp] = unzip1 hp_bounds.
+    (* Let is_hp_task denote whether a task is a higher-priority task
+       (with respect to tsk). *)
+    Let is_hp_task := higher_priority_task higher_eq_priority tsk.
 
-    (* ...and that all values in the pairs contain valid response-time bounds *)
+    (* Assume a response-time bound is known... *)
+    Let task_with_response_time := (sporadic_task * time)%type.
+    Variable hp_bounds: seq task_with_response_time.
     Hypothesis H_response_time_of_interfering_tasks_is_known:
       forall hp_tsk R,
         (hp_tsk, R) \in hp_bounds ->
         response_time_bounded_by hp_tsk R.
-
-    (* Assume that the scheduler is work-conserving and enforces the FP policy. *)
-    Hypothesis H_work_conserving: work_conserving job_cost sched.
-    Hypothesis H_enforces_FP_policy:
-      enforces_FP_policy job_cost job_task sched higher_eq_priority.
     
+    (* ... for every higher-priority task. *)
+    Hypothesis H_hp_bounds_has_interfering_tasks:
+      forall hp_tsk,
+        hp_tsk \in ts ->
+        is_hp_task hp_tsk ->
+          exists R, (hp_tsk, R) \in hp_bounds.
+
     (* Let R be the fixed point of Bertogna and Cirinei's recurrence, ...*)
     Variable R: time.
     Hypothesis H_response_time_recurrence_holds :
       R = task_cost tsk +
           div_floor
-            (total_interference_bound_fp task_cost task_period tsk hp_bounds R higher_eq_priority)
+            (total_interference_bound_fp task_cost task_period hp_bounds R)
             num_cpus.
 
     (* ... and assume that R is no larger than the deadline of tsk.*)
     Hypothesis H_response_time_no_larger_than_deadline:
       R <= task_deadline tsk.
 
-    (* In order to prove that R is a response-time bound, we first present some lemmas. *)
+    (* In order to prove that R is a response-time bound, we first provide some lemmas. *)
     Section Lemmas.
 
       (* Consider any job j of tsk. *)
@@ -124,17 +138,18 @@ Module ResponseTimeAnalysisFP.
         task_interference job_cost job_task sched j
                           tsk_other (job_arrival j) (job_arrival j + R).
 
-      (* and X the total interference incurred by job j due to any task. *)
+      (* ...and X the total interference incurred by job j due to any task. *)
       Let X := total_interference job_cost sched j (job_arrival j) (job_arrival j + R).
 
       (* Recall Bertogna and Cirinei's workload bound. *)
       Let workload_bound (tsk_other: sporadic_task) (R_other: time) :=
         W task_cost task_period tsk_other R_other R.
 
-      (* Also, let ts_interf be the subset of tasks that interfere with tsk. *)
-      Let ts_interf := [seq tsk_other <- ts | can_interfere_with_tsk tsk_other].
+      (* Let hp_tasks denote the set of higher-priority tasks. *)
+      Let hp_tasks := [seq tsk_other <- ts | is_hp_task tsk_other].
 
-      Section LemmasAboutInterferingTasks.
+      (* Now we establish results about the higher-priority tasks. *)
+      Section LemmasAboutHPTasks.
         
         (* Let (tsk_other, R_other) be any pair of higher-priority task and
            response-time bound computed in previous iterations. *)
@@ -142,30 +157,6 @@ Module ResponseTimeAnalysisFP.
         Variable R_other: time.
         Hypothesis H_response_time_of_tsk_other: (tsk_other, R_other) \in hp_bounds.
 
-        (* Note that tsk_other is in task set ts ...*)
-        Lemma bertogna_fp_tsk_other_in_ts: tsk_other \in ts.
-          Proof.
-            rename H_hp_bounds_has_interfering_tasks into UNZIP,
-                   H_response_time_of_tsk_other into INbounds.
-            move: UNZIP => UNZIP.
-            cut (tsk_other \in ts_interf = true);
-              first by rewrite mem_filter; move => /andP [_ IN].
-            unfold ts_interf; rewrite UNZIP.
-            by apply/mapP; exists (tsk_other, R_other).
-        Qed.
-
-        (*... and interferes with task tsk. *)
-        Lemma bertogna_fp_tsk_other_interferes: can_interfere_with_tsk tsk_other.
-          Proof.
-            rename H_hp_bounds_has_interfering_tasks into UNZIP,
-                   H_response_time_of_tsk_other into INbounds.
-            move: UNZIP => UNZIP.
-            cut (tsk_other \in ts_interf = true);
-              first by rewrite mem_filter; move => /andP [INTERF _].
-            unfold ts_interf; rewrite UNZIP.
-            by apply/mapP; exists (tsk_other, R_other).
-        Qed.
-
         (* Since tsk_other cannot interfere more than it executes, we show that
            the interference caused by tsk_other is no larger than workload bound W. *)
         Lemma bertogna_fp_workload_bounds_interference :
@@ -174,9 +165,30 @@ Module ResponseTimeAnalysisFP.
           unfold response_time_bounded_by, is_response_time_bound_of_task,
                  completed, completed_jobs_dont_execute, valid_sporadic_job in *.
           rename H_valid_task_parameters into TASK_PARAMS,
+                 H_all_jobs_from_taskset into FROMTS,
                  H_response_time_of_interfering_tasks_is_known into RESP.
           unfold x, workload_bound.
-          have INts := bertogna_fp_tsk_other_in_ts.
+          destruct ([exists t: 'I_(job_arrival j + R),
+                       task_is_scheduled job_task sched tsk_other t]) eqn: SCHED;
+            last first.
+          {
+            apply negbT in SCHED; rewrite negb_exists in SCHED.
+            move: SCHED => /forallP SCHED.
+            apply leq_trans with (n := 0); last by done.
+            apply leq_trans with (n := \sum_(job_arrival j <= t < job_arrival j + R) 0);
+              last by rewrite big1.
+            apply leq_sum_nat; move => i /andP [_ LTi] _.
+            specialize (SCHED (Ordinal LTi)).
+            rewrite negb_exists in SCHED; move: SCHED => /forallP SCHED.
+            rewrite big1 //; intros cpu _.
+            specialize (SCHED cpu); apply negbTE in SCHED.
+            by rewrite SCHED andbF.
+          }
+          move: SCHED => /existsP [t /existsP [cpu SCHED]].
+          unfold task_scheduled_on in SCHED.
+          destruct (sched cpu t) as [j0 |]; last by done.
+          assert (INts: tsk_other \in ts).
+            by move: SCHED => /eqP <-; rewrite FROMTS.
           apply leq_trans with (n := workload job_task sched tsk_other
                                               (job_arrival j) (job_arrival j + R));
             first by apply task_interference_le_workload.
@@ -187,7 +199,7 @@ Module ResponseTimeAnalysisFP.
               | by ins; apply RESP with (hp_tsk := tsk_other)].
         Qed.
 
-      End LemmasAboutInterferingTasks.
+      End LemmasAboutHPTasks.
 
       (* Next we prove some lemmas that help to derive a contradiction.*)
       Section DerivingContradiction.
@@ -238,15 +250,16 @@ Module ResponseTimeAnalysisFP.
           by apply completion_monotonic with (t := i); try (by done); apply ltnW.
         Qed.
 
-        Let scheduled_task_other_than_tsk (t: time) (tsk_other: sporadic_task) :=
+        (* Let's define a predicate to identify the other tasks that are scheduled. *)
+        Let other_scheduled_task (t: time) (tsk_other: sporadic_task) :=
           task_is_scheduled job_task sched tsk_other t &&
-          can_interfere_with_tsk tsk_other.
-        
-        (* 1) Next, we prove that the sum of the interference of each task is equal
-              to the total interference multiplied by the number of processors. This
-              holds because interference only occurs when all processors are busy. *)
-        Lemma bertogna_fp_all_cpus_busy :
-          \sum_(tsk_k <- ts_interf) x tsk_k = X * num_cpus.
+          is_hp_task tsk_other.
+      
+        (* 1) Now we prove that, at all times that j is backlogged, the number
+              of tasks other than tsk that are scheduled is exactly the number
+              of processors in the system. This is required to prove lemma (2). *)
+        Lemma bertogna_fp_all_cpus_are_busy:
+          \sum_(tsk_k <- hp_tasks) x tsk_k = X * num_cpus.
         Proof.
           rename H_all_jobs_from_taskset into FROMTS,
                  H_valid_task_parameters into PARAMS,
@@ -277,7 +290,7 @@ Module ResponseTimeAnalysisFP.
           apply eq_bigr; intros cpu _.
           specialize (WORK j t BACK cpu); des.
           move: WORK => /eqP SCHED.
-          rewrite (bigD1_seq (job_task j_other)) /=; last by rewrite filter_uniq.
+          rewrite (bigD1_seq (job_task j_other)) /=; last by rewrite filter_uniq; destruct ts.
           {
             rewrite (eq_bigr (fun i => 0));
               last by intros i DIFF; rewrite /task_scheduled_on SCHED;apply/eqP;rewrite eqb0 eq_sym.
@@ -285,7 +298,6 @@ Module ResponseTimeAnalysisFP.
             by unfold task_scheduled_on; rewrite SCHED.
           }
           rewrite mem_filter; apply/andP; split; last by apply FROMTS.
-          unfold can_interfere_with_tsk, fp_can_interfere_with.
           apply/andP; split.
           {
             rewrite -JOBtsk; apply FP with (t := t); first by done.
@@ -328,25 +340,30 @@ Module ResponseTimeAnalysisFP.
           }
         Qed.
 
-        (* 2) Now, we prove that the Bertogna's interference bound
-              is not enough to cover the sum of the "minimum" term over
-              all tasks (artifact of the proof by contradiction). *)
+        (* 2) Next, using lemmas (0) and (1) we prove that the reduction-based
+              interference bound is not enough to cover the sum of the minima over all tasks
+              (artifact of the proof by contradiction). *)
         Lemma bertogna_fp_sum_exceeds_total_interference:
           \sum_((tsk_k, R_k) <- hp_bounds)
-           x tsk_k > total_interference_bound_fp task_cost task_period tsk
-                                            hp_bounds R higher_eq_priority.
+           x tsk_k > total_interference_bound_fp task_cost task_period hp_bounds R.
         Proof.
           have TOOMUCH := bertogna_fp_too_much_interference.
-          rename H_hp_bounds_has_interfering_tasks into UNZIP,
+          have ALLBUSY := bertogna_fp_all_cpus_are_busy.
+          rename H_hp_bounds_has_interfering_tasks into HAS,
                  H_response_time_recurrence_holds into REC.
-          apply leq_trans with (n := \sum_(tsk_k <- ts_interf) x tsk_k);
+          apply leq_trans with (n := \sum_(tsk_k <- hp_tasks) x tsk_k);
               last first.
           {
-            rewrite (eq_bigr (fun i => x (fst i)));
-              last by ins; destruct i.
-            rewrite big_filter.
-            have MAP := big_map (fun x => fst x) (fun i => true) (fun i => x i).
-            by unfold unzip1 in *; rewrite -MAP -UNZIP -big_filter.
+            rewrite (eq_bigr (fun i => x (fst i))); last by ins; destruct i.
+            have MAP := @big_map _ 0 addn _ _ (fun x => fst x) hp_bounds (fun x => true) (fun y => (x y)).
+            rewrite -MAP.
+            apply leq_sum_sub_uniq; first by apply filter_uniq; destruct ts.
+            red; move => tsk0 IN0.
+            rewrite mem_filter in IN0; move: IN0 => /andP [INTERF0 IN0].
+            apply/mapP.
+            feed (HAS tsk0); first by done.
+            move: (HAS INTERF0) => [R0 IN].
+            by exists (tsk0, R0).
           }
           apply ltn_div_trunc with (d := num_cpus);
             first by apply H_at_least_one_cpu.
@@ -354,20 +371,22 @@ Module ResponseTimeAnalysisFP.
           rewrite -addn1 -leq_subLR.
           rewrite -[R + 1 - _]subh1; last by rewrite REC; apply leq_addr.
           rewrite leq_divRL; last by apply H_at_least_one_cpu.
-          rewrite bertogna_fp_all_cpus_busy.
+          rewrite ALLBUSY.
           by rewrite leq_mul2r; apply/orP; right; apply TOOMUCH.
         Qed.
 
-        (* 3) After concluding that the sum of the minimum exceeds (R - e_i + 1),
-              we prove that there exists a tuple (tsk_k, R_k) such that x_k > W_k. *)
+        (* 3) After concluding that the sum of the minima exceeds (R - e_i + 1),
+              we prove that there exists a tuple (tsk_k, R_k) that satisfies
+              min (x_k, R - e_i + 1) > min (W_k, R - e_i + 1).
+              This implies that x_k > W_k, which is of course a contradiction,
+              since W_k is a valid task interference bound. *)
         Lemma bertogna_fp_exists_task_that_exceeds_bound :
           exists tsk_k R_k,
             (tsk_k, R_k) \in hp_bounds /\
             x tsk_k > workload_bound tsk_k R_k.
         Proof.
-          have INTERFk := bertogna_fp_tsk_other_interferes.
           have SUM := bertogna_fp_sum_exceeds_total_interference.
-          rename H_hp_bounds_has_interfering_tasks into UNZIP.
+          rename H_hp_bounds_has_interfering_tasks into HASHP.
           assert (HAS: has (fun tup : task_with_response_time =>
                              let (tsk_k, R_k) := tup in
                                x tsk_k > workload_bound tsk_k R_k)
@@ -377,15 +396,13 @@ Module ResponseTimeAnalysisFP.
             move: NOTHAS => /negP /hasPn ALL.
             rewrite -[_ < _]negbK in SUM.
             move: SUM => /negP SUM; apply SUM; rewrite -leqNgt.
+            rewrite (eq_bigr (fun i => x (fst i))); last by ins; destruct i.
             unfold total_interference_bound_fp.
-            rewrite [\sum_(i <- _ | let '(tsk_other, _) := i in _)_]big_mkcond.
-            rewrite big_seq_cond [\sum_(i <- _ | true) _]big_seq_cond.
-            apply leq_sum; move => tsk_k /andP [HPk _]; destruct tsk_k as [tsk_k R_k].
-            specialize (ALL (tsk_k, R_k) HPk).
-            rewrite -leqNgt in ALL.
-            specialize (INTERFk tsk_k R_k HPk).
-            fold (can_interfere_with_tsk); rewrite INTERFk.
-            unfold interference_bound_generic. by apply ALL.
+            rewrite big_seq_cond.
+            rewrite [\sum_(_ <- _ | true)_]big_seq_cond.
+            apply leq_sum.
+            intros p; rewrite andbT; intros IN.
+            by specialize (ALL p IN); destruct p; rewrite leqNgt.
           }
           move: HAS => /hasP HAS; destruct HAS as [[tsk_k R_k] HPk MINk]; exists tsk_k, R_k.
           by repeat split.
@@ -403,7 +420,7 @@ Module ResponseTimeAnalysisFP.
       have BOUND := bertogna_fp_workload_bounds_interference.
       rename H_response_time_recurrence_holds into REC,
              H_response_time_of_interfering_tasks_is_known into RESP,
-             H_hp_bounds_has_interfering_tasks into UNZIP,
+             H_hp_bounds_has_interfering_tasks into HAS,
              H_response_time_no_larger_than_deadline into LEdl.
       intros j JOBtsk.
        
diff --git a/analysis/parallel/interference_bound_edf.v b/analysis/parallel/interference_bound_edf.v
index a06f5532dcb1a53c0bb5badbda1b264da8b81820..f0f2c6a33ef959c913e9e2efde66b0d6385869f1 100644
--- a/analysis/parallel/interference_bound_edf.v
+++ b/analysis/parallel/interference_bound_edf.v
@@ -63,7 +63,7 @@ Module InterferenceBoundEDF.
     (* ... and an interval length delta. *)
     Variable delta: time.
 
-    Section PerTask.
+    Section RecallInterferenceBounds.
 
       Variable tsk_R: task_with_response_time.
       Let tsk_other := fst tsk_R.
@@ -81,19 +81,21 @@ Module InterferenceBoundEDF.
       Definition interference_bound_edf :=
         minn basic_interference_bound edf_specific_bound.
 
-    End PerTask.
+    End RecallInterferenceBounds.
 
-    Section AllTasks.
+    (* Next we define the computation of the total interference for APA scheduling. *)
+    Section TotalInterference.
 
-      Let interferes_with_tsk := jldp_can_interfere_with tsk.
+      (* Let other_task denote tasks different from tsk. *)
+      Let other_task := different_task tsk.
       
       (* The total interference incurred by tsk is bounded by the sum
-         of individual task interferences. *)
+         of individual task interferences of the other tasks. *)
       Definition total_interference_bound_edf :=
-        \sum_((tsk_other, R_other) <- R_prev | interferes_with_tsk tsk_other)
+        \sum_((tsk_other, R_other) <- R_prev | other_task tsk_other)
            interference_bound_edf (tsk_other, R_other).
 
-    End AllTasks.
+    End TotalInterference.
 
   End TotalInterferenceBoundEDF.
   
@@ -134,8 +136,7 @@ Module InterferenceBoundEDF.
       completed_jobs_dont_execute job_cost sched.
 
     (* Assume there exists at least one processor. *)
-    Hypothesis H_at_least_one_cpu :
-      num_cpus > 0.
+    Hypothesis H_at_least_one_cpu: num_cpus > 0.
 
     (* Assume that we have a task set where all tasks have valid
        parameters and constrained deadlines. *)
diff --git a/analysis/parallel/interference_bound_fp.v b/analysis/parallel/interference_bound_fp.v
index 49ed78f3decee2a4f91708322b586af34c0b854f..e6aa91d6f47fb2a8697b2f49c1ef12103d4509ac 100644
--- a/analysis/parallel/interference_bound_fp.v
+++ b/analysis/parallel/interference_bound_fp.v
@@ -30,13 +30,13 @@ Module InterferenceBoundFP.
     (* Assume an FP policy. *)
     Variable higher_eq_priority: FP_policy sporadic_task.
 
-    Let can_interfere_with_tsk := fp_can_interfere_with higher_eq_priority tsk.  
+    (* Recall the interference bound. *)
     Let total_interference_bound := interference_bound_generic task_cost task_period delta.
     
     (* The total interference incurred by tsk is bounded by the sum
        of individual task interferences. *)
     Definition total_interference_bound_fp :=
-      \sum_((tsk_other, R_other) <- R_prev | can_interfere_with_tsk tsk_other)
+      \sum_((tsk_other, R_other) <- R_prev)
          total_interference_bound (tsk_other, R_other).
       
   End Definitions.
diff --git a/implementation/apa/arrival_sequence.v b/implementation/apa/arrival_sequence.v
new file mode 100644
index 0000000000000000000000000000000000000000..21adb133c02d24c6355c75e79bf0f1899d8f11e8
--- /dev/null
+++ b/implementation/apa/arrival_sequence.v
@@ -0,0 +1,114 @@
+(* We can reuse the.apa definition of periodic arrival sequence. *)
+Require Import rt.util.all.
+Require Import rt.model.apa.arrival_sequence rt.model.apa.job
+               rt.model.apa.task rt.model.apa.task_arrival.
+Require Import rt.implementation.apa.task rt.implementation.apa.job.
+From mathcomp Require Import ssreflect ssrbool ssrfun ssrnat eqtype seq div.
+
+Module ConcreteArrivalSequence.
+
+  Import Job ArrivalSequence ConcreteTask ConcreteJob SporadicTaskset SporadicTaskArrival.
+
+  Section PeriodicArrivals.
+
+    Context {num_cpus: nat}.
+    Variable ts: concrete_taskset num_cpus.
+
+    (* At any time t, we release Some job of tsk if t is a multiple of the period,
+       otherwise we release None. *)
+    Definition add_job (t: time) (tsk: @concrete_task num_cpus) : option (@concrete_job _) :=
+      if task_period tsk %| t  then
+        Some (Build_concrete_job (t %/ task_period tsk) (task_cost tsk) (task_deadline tsk) tsk)
+      else
+        None.
+
+    (* The arrival sequence at any time t is simply the partial map of add_job. *)
+    Definition periodic_arrival_sequence (t: time) := pmap (add_job t) ts.
+
+  End PeriodicArrivals.
+
+  Section Proofs.
+
+    Context {num_cpus: nat}.
+    
+    (* Let ts be any concrete task set with valid parameters. *)
+    Variable ts: concrete_taskset num_cpus.
+    Hypothesis H_valid_task_parameters:
+      valid_sporadic_taskset task_cost task_period task_deadline ts.
+    
+    (* Regarding the periodic arrival sequence built from ts, we prove that...*)
+    Let arr_seq := periodic_arrival_sequence ts.
+
+    (* ... every job comes from the task set, ... *)
+    Theorem periodic_arrivals_all_jobs_from_taskset:
+      forall (j: JobIn arr_seq),
+        job_task (_job_in arr_seq j) \in ts. (* TODO: fix coercion. *)
+    Proof.
+      intros j.
+      destruct j as [j arr ARRj]; simpl.
+      unfold arr_seq, arrives_at, periodic_arrival_sequence in *.
+      rewrite mem_pmap in ARRj.
+      move: ARRj => /mapP ARRj; destruct ARRj as [tsk IN SOME].
+      by unfold add_job in *; desf.
+    Qed.
+
+    (* ..., jobs have valid parameters, ... *)
+    Theorem periodic_arrivals_valid_job_parameters:
+      forall (j: JobIn arr_seq),
+        valid_sporadic_job task_cost task_deadline job_cost job_deadline job_task j.
+    Proof.
+      rename H_valid_task_parameters into PARAMS.
+      unfold valid_sporadic_taskset, is_valid_sporadic_task in *.
+      intros j; destruct j as [j arr ARRj]; simpl.
+      unfold arrives_at, arr_seq, periodic_arrival_sequence in ARRj.
+      rewrite mem_pmap in ARRj; move: ARRj => /mapP [tsk IN SOME].
+      unfold add_job in SOME; desf.
+      specialize (PARAMS tsk IN); des.
+      unfold valid_sporadic_job, valid_realtime_job, job_cost_positive.
+      by repeat split; try (by done); apply leqnn.
+    Qed.
+
+    (* ... job arrivals satisfy the sporadic task model, ... *)
+    Theorem periodic_arrivals_are_sporadic:
+      sporadic_task_model task_period arr_seq job_task.
+    Proof.
+      unfold sporadic_task_model; move => j j' /eqP DIFF SAMEtsk LE.
+      destruct j as [j arr ARR], j' as [j' arr' ARR']; simpl in *.
+      rewrite eqE /= /jobin_eqdef negb_and /= in DIFF.
+      unfold arrives_at, arr_seq, periodic_arrival_sequence in *.
+      rewrite 2!mem_pmap in ARR ARR'.
+      move: ARR ARR' => /mapP [tsk_j INj SOMEj] /mapP [tsk_j' INj' SOMEj'].
+      unfold add_job in SOMEj, SOMEj'; desf; simpl in *;
+      move: Heq0 Heq => /dvdnP [k DIV] /dvdnP [k' DIV'].
+      {
+        rewrite DIV DIV' -mulSnr.
+        rewrite leq_eqVlt in LE; move: LE => /orP [/eqP EQ | LESS].
+        { 
+          exfalso; move: DIFF => /negP DIFF; apply DIFF.
+          by subst; rewrite EQ.
+        }
+        subst; rewrite leq_mul2r; apply/orP; right.
+        by rewrite ltn_mul2r in LESS; move: LESS => /andP [_ LT].
+      }
+      {
+        assert (LT: arr < arr'). by rewrite ltn_neqAle; apply/andP.
+        clear LE DIFF; subst tsk_j' arr arr'.
+        rewrite ltn_mul2r in LT; move: LT => /andP [_ LT].
+        by apply leq_trans with (n := k.+1 * task_period tsk_j);
+          [by rewrite mulSnr | by rewrite leq_mul2r; apply/orP; right].
+      }
+    Qed.
+
+    (* ... and the arrival sequence has no duplicate jobs. *)
+    Theorem periodic_arrivals_is_a_set:
+      arrival_sequence_is_a_set arr_seq.
+    Proof.
+      intros t.
+      unfold arr_seq, periodic_arrival_sequence.
+      apply (pmap_uniq) with (g := job_task); last by destruct ts.
+      by unfold add_job, ocancel; intro tsk; desf.
+    Qed.
+      
+  End Proofs.
+  
+End ConcreteArrivalSequence.
\ No newline at end of file
diff --git a/implementation/apa/bertogna_edf_example.v b/implementation/apa/bertogna_edf_example.v
new file mode 100644
index 0000000000000000000000000000000000000000..d8fdf6fe5eb33a539cd6ec21dd43ce93acfa312a
--- /dev/null
+++ b/implementation/apa/bertogna_edf_example.v
@@ -0,0 +1,207 @@
+Require Import rt.util.all.
+Require Import rt.model.apa.job rt.model.apa.task rt.model.apa.affinity
+               rt.model.apa.schedule rt.model.apa.interference
+               rt.model.apa.schedulability
+               rt.model.apa.priority rt.model.apa.platform.
+Require Import rt.analysis.apa.workload_bound
+               rt.analysis.apa.interference_bound_edf
+               rt.analysis.apa.bertogna_edf_comp.
+Require Import rt.implementation.apa.job
+               rt.implementation.apa.task
+               rt.implementation.apa.schedule
+               rt.implementation.apa.arrival_sequence.
+From mathcomp Require Import ssreflect ssrbool ssrnat eqtype seq fintype bigop div.
+
+Module ResponseTimeAnalysisEDF.
+
+  Import Job Schedule SporadicTaskset Priority Schedulability
+         Affinity Platform InterferenceBoundEDF WorkloadBound
+         Interference ResponseTimeIterationEDF.
+  Import ConcreteJob ConcreteTask ConcreteArrivalSequence ConcreteScheduler.
+
+  (* In this section, we instantiate a simple example to show that the theorems
+     contain no contradictory assumptions. *)
+  Section ExampleRTA.
+
+    (* Assume there are two processors. *)
+    Let num_cpus := 2.
+
+    (* Let (cpu j) denote the j-th processor *)
+    Let cpu j := @Ordinal num_cpus j.
+
+    (* Define alpha1 := {cpu 0, cpu 1} with the two processors. *)
+    Program Let alpha1 : affinity num_cpus :=
+      (Build_set [:: cpu 0 _; cpu 1 _] _).
+
+    (* Define the singleton affinity alpha2 := {cpu 0}. *)
+    Program Let alpha2 : affinity num_cpus :=
+      (Build_set [:: cpu 0 _] _).
+
+    (* Define the singleton affinity alpha3 := {cpu 1}. *)
+    Program Let alpha3 : affinity num_cpus :=
+      (Build_set [:: cpu 1 _] _).
+
+    (* Now we create three tasks using the affinities above ... *)
+    Let tsk1 := {| task_id := 1; task_cost := 3; task_period := 5;
+                   task_deadline := 3; task_affinity := alpha1|}.
+    Let tsk2 := {| task_id := 2; task_cost := 2; task_period := 6;
+                   task_deadline := 5; task_affinity  := alpha2|}.
+    Let tsk3 := {| task_id := 3; task_cost := 2; task_period := 12;
+                   task_deadline := 11; task_affinity := alpha3|}.
+
+    (* ... and group these tasks into task set ts. *)
+    Program Let ts := Build_set [:: tsk1; tsk2; tsk3] _.
+
+    (* In this section, we let Coq compute a few properties about ts. *)
+    Section FactsAboutTaskset.
+
+      (* There are no empty affinities. *)
+      Fact ts_non_empty_affinities:
+        forall tsk, 
+          tsk \in ts ->
+          #|task_affinity tsk| > 0.
+      Proof.
+        intros tsk IN.
+        by repeat (move: IN => /orP [/eqP EQ | IN]; subst; rewrite ?set_card; compute).
+      Qed.
+
+      (* The tasks have valid parameters (e.g., cost > 0). *)
+      Fact ts_has_valid_parameters:
+        valid_sporadic_taskset task_cost task_period task_deadline ts.
+      Proof.
+        intros tsk IN.
+        by repeat (move: IN => /orP [/eqP EQ | IN]; subst; compute).
+      Qed.
+
+      (* The task set has constrained deadlines. *)
+      Fact ts_has_constrained_deadlines:
+        forall tsk,
+          tsk \in ts ->
+          task_deadline tsk <= task_period tsk.
+      Proof.
+        intros tsk IN.
+        by repeat (move: IN => /orP [/eqP EQ | IN]; subst; compute).
+      Qed.
+
+    End FactsAboutTaskset.
+
+    (* Next, recall the EDF RTA schedulability test for APA scheduling.
+       Note that the task functions (from implementation/apa/task.v)
+       require num_cpus as a parameter, so we leave a blank space so that
+       can be inferred automatically. *)
+    Let schedulability_test :=
+      edf_schedulable (@task_cost _) (@task_period _)
+                      (@task_deadline _) num_cpus task_affinity
+                      task_affinity. (* For simplicity, we use subaffinity alpha' = alpha. *)
+
+    (* Now, we guide Coq to compute the schedulability test function
+       and show it returns true. *)
+    Fact schedulability_test_succeeds :
+      schedulability_test ts = true.
+    Proof.
+      unfold schedulability_test, edf_schedulable, edf_claimed_bounds; desf.
+      apply negbT in Heq; move: Heq => /negP ALL.
+      exfalso; apply ALL; clear ALL.
+      assert (STEPS: \sum_(tsk <- ts) (task_deadline tsk - task_cost tsk) + 1 = 13).
+      {
+        by rewrite unlock; compute.
+      } rewrite STEPS; clear STEPS.
+
+      have cpu0P: 0 < 2 by done.
+      have cpu1P: 1 < 2 by done.
+
+      have cpu_compute :
+        forall (P: processor num_cpus -> bool),
+          [exists x, P x] =
+            if (P (cpu 0 cpu0P)) then true else
+              if P (cpu 1 cpu1P) then true else false.
+      {
+        unfold num_cpus in *; intros P.
+        destruct (P (cpu 0 cpu0P)) eqn:P0;
+          first by apply/existsP; exists (cpu 0 cpu0P).
+        destruct (P (cpu 1 cpu1P)) eqn:P1;
+          first by apply/existsP; exists (cpu 1 cpu1P).
+        apply negbTE; rewrite negb_exists; apply/forallP.
+        intros x; destruct x as [x LE]; apply negbT.
+        have GE0 := leq0n x.
+        rewrite leq_eqVlt in GE0; move: GE0 => /orP [/eqP EQ0 | GE1];
+          first by rewrite -P0; f_equal; apply ord_inj.
+        rewrite leq_eqVlt in GE1; move: GE1 => /orP [/eqP EQ1 | GE2];
+          first by rewrite -P1; f_equal; apply ord_inj.
+        have LE' := LE.
+        apply leq_ltn_trans with (m := 2) in LE'; last by done.
+        by rewrite ltnn in LE'.
+      }
+
+      Ltac f :=
+        unfold edf_rta_iteration; simpl;
+        unfold edf_response_time_bound, div_floor, total_interference_bound_edf, interference_bound_edf, interference_bound_generic, W, edf_specific_interference_bound, different_task_in, affinity_intersects; simpl;
+        rewrite !addnE !set_card !big_cons ?big_nil /=.
+
+      
+      rewrite [edf_rta_iteration]lock; simpl.
+      unfold locked at 13; destruct master_key; f; rewrite !cpu_compute /=.
+      unfold locked at 12; destruct master_key; f; rewrite !cpu_compute /=.
+      unfold locked at 11; destruct master_key; f; rewrite !cpu_compute /=.
+      unfold locked at 10; destruct master_key; f; rewrite !cpu_compute /=.
+      unfold locked at 9; destruct master_key; f; rewrite !cpu_compute /=.
+      unfold locked at 8; destruct master_key; f; rewrite !cpu_compute /=.
+      unfold locked at 7; destruct master_key; f; rewrite !cpu_compute /=.
+      unfold locked at 6; destruct master_key; f; rewrite !cpu_compute /=.
+      unfold locked at 5; destruct master_key; f; rewrite !cpu_compute /=.
+      unfold locked at 4; destruct master_key; f; rewrite !cpu_compute /=.
+      unfold locked at 3; destruct master_key; f; rewrite !cpu_compute /=.
+      unfold locked at 2; destruct master_key; f; rewrite !cpu_compute /=.
+      by unfold locked at 1; destruct master_key; f; rewrite !cpu_compute /=.
+    Qed.
+
+    (* Let arr_seq be the periodic arrival sequence from ts. *)
+    Let arr_seq := periodic_arrival_sequence ts.
+
+    (* Let sched be the weak APA EDF scheduler. *)
+    Let sched := scheduler job_cost job_task num_cpus arr_seq task_affinity (EDF job_deadline).
+
+    (* Recall the definition of deadline miss. *)
+    Let no_deadline_missed_by :=
+      task_misses_no_deadline job_cost job_deadline job_task sched.
+
+    (* To show that the RTA works, we infer the schedulability of the task
+       set from the result of the RTA procedure. *)
+    Corollary ts_is_schedulable:
+      forall tsk,
+        tsk \in ts ->
+        no_deadline_missed_by tsk.
+    Proof.
+      intros tsk IN.
+      have VALID := periodic_arrivals_valid_job_parameters ts ts_has_valid_parameters.
+      have VALIDTS := ts_has_valid_parameters.
+      unfold valid_sporadic_job, valid_realtime_job in *; des.
+      apply taskset_schedulable_by_edf_rta with (task_cost := task_cost) (task_period := task_period) (task_deadline := task_deadline) (alpha := task_affinity) (alpha' := task_affinity) (ts0 := ts).
+      - by apply ts_has_valid_parameters. 
+      - by apply ts_has_constrained_deadlines.
+      - by apply ts_non_empty_affinities.
+      - by ins.
+      - by apply periodic_arrivals_all_jobs_from_taskset.
+      - by apply periodic_arrivals_valid_job_parameters, ts_has_valid_parameters.
+      - by apply periodic_arrivals_are_sporadic.
+      - by apply scheduler_jobs_must_arrive_to_execute.
+      - apply scheduler_completed_jobs_dont_execute; intro j'.
+        -- by specialize (VALID j'); des.
+        -- by apply periodic_arrivals_is_a_set.
+      - by apply scheduler_sequential_jobs, periodic_arrivals_is_a_set.
+      - by apply scheduler_respects_affinity.
+      - apply scheduler_apa_work_conserving; try (by done).
+        -- by apply periodic_arrivals_is_a_set.
+        -- by apply EDF_is_transitive.
+        -- by apply EDF_is_total.
+      - apply scheduler_enforces_policy.
+        -- by apply periodic_arrivals_is_a_set.
+        -- by apply EDF_is_transitive.
+        -- by apply EDF_is_total.
+      - by apply schedulability_test_succeeds.
+      - by apply IN.
+    Qed.
+
+  End ExampleRTA.
+
+End ResponseTimeAnalysisEDF.
\ No newline at end of file
diff --git a/implementation/apa/bertogna_fp_example.v b/implementation/apa/bertogna_fp_example.v
new file mode 100644
index 0000000000000000000000000000000000000000..1a1452a6785f15ebd94939ca2a94c106fb6df8fe
--- /dev/null
+++ b/implementation/apa/bertogna_fp_example.v
@@ -0,0 +1,297 @@
+Require Import rt.util.all.
+Require Import rt.model.apa.job rt.model.apa.task rt.model.apa.affinity
+               rt.model.apa.schedule rt.model.apa.schedulability
+               rt.model.apa.priority rt.model.apa.platform
+               rt.model.apa.interference.
+Require Import rt.analysis.apa.workload_bound
+               rt.analysis.apa.interference_bound_fp
+               rt.analysis.apa.bertogna_fp_comp.
+Require Import rt.implementation.apa.job
+               rt.implementation.apa.task
+               rt.implementation.apa.schedule
+               rt.implementation.apa.arrival_sequence.
+From mathcomp Require Import ssreflect ssrbool ssrnat eqtype fintype seq bigop div.
+
+Module ResponseTimeAnalysisFP.
+
+  Import Job Schedule SporadicTaskset Priority Schedulability Platform
+         Interference InterferenceBoundFP WorkloadBound
+         ResponseTimeIterationFP Affinity.
+  Import ConcreteJob ConcreteTask ConcreteArrivalSequence ConcreteScheduler.
+
+  (* In this section, we instantiate a simple example to show that the theorems
+     contain no contradictory assumptions. *)  
+  Section ExampleRTA.
+
+    (* Assume there are two processors. *)
+    Let num_cpus := 2.
+
+    (* Let (cpu j) denote the j-th processor *)
+    Let cpu j := @Ordinal num_cpus j.
+
+    (* Define alpha1 := {cpu 0, cpu 1} with the two processors. *)
+    Program Let alpha1 : affinity num_cpus :=
+      (Build_set [:: cpu 0 _; cpu 1 _] _).
+
+    (* Define the singleton affinity alpha2 := {cpu 0}. *)
+    Program Let alpha2 : affinity num_cpus :=
+      (Build_set [:: cpu 0 _] _).
+
+    (* Define the singleton affinity alpha3 := {cpu 1}. *)
+    Program Let alpha3 : affinity num_cpus :=
+      (Build_set [:: cpu 1 _] _).
+
+    (* Now we create three tasks using the affinities above ... *)
+    Let tsk1 := {| task_id := 1; task_cost := 3; task_period := 5;
+                   task_deadline := 3; task_affinity := alpha1|}.
+    Let tsk2 := {| task_id := 2; task_cost := 2; task_period := 6;
+                   task_deadline := 5; task_affinity  := alpha2|}.
+    Let tsk3 := {| task_id := 3; task_cost := 2; task_period := 12;
+                   task_deadline := 11; task_affinity := alpha3|}.
+
+    (* ... and group these tasks into task set ts. *)
+    Program Let ts := Build_set [:: tsk1; tsk2; tsk3] _.
+
+    (* In this section, we let Coq compute a few properties about ts. *)
+    Section FactsAboutTaskset.
+
+      (* There are no empty affinities. *)
+      Fact ts_non_empty_affinities:
+        forall tsk, 
+          tsk \in ts -> #|task_affinity tsk| > 0.
+      Proof.
+        intros tsk IN.
+        by repeat (move: IN => /orP [/eqP EQ | IN]; subst; rewrite ?set_card; compute).
+      Qed.
+
+      (* The tasks have valid parameters (e.g., cost > 0). *)
+      Fact ts_has_valid_parameters:
+        valid_sporadic_taskset task_cost task_period task_deadline ts.
+      Proof.
+        intros tsk IN.
+        by repeat (move: IN => /orP [/eqP EQ | IN]; subst; compute).
+      Qed.
+
+      (* The task set has constrained deadlines. *)
+      Fact ts_has_constrained_deadlines:
+        forall tsk,
+          tsk \in ts ->
+          task_deadline tsk <= task_period tsk.
+      Proof.
+        intros tsk IN.
+        by repeat (move: IN => /orP [/eqP EQ | IN]; subst; compute).
+      Qed.
+
+    End FactsAboutTaskset.
+
+    (* Then, let arr_seq be the periodic arrival sequence from ts. *)
+    Let arr_seq := periodic_arrival_sequence ts.
+
+    (* Assume rate-monotonic priorities. *)
+    Let higher_priority : JLDP_policy arr_seq :=
+      (FP_to_JLDP job_task (RM task_period)).
+
+    Section FactsAboutPriorityOrder.
+
+      Lemma ts_has_unique_priorities :
+        FP_is_antisymmetric_over_task_set (RM task_period) ts.
+      Proof.
+        unfold RM; intros tsk tsk' IN IN' HP HP'.
+        have EQ: task_period tsk = task_period tsk' by apply/eqP; rewrite eqn_leq HP HP'.
+        clear HP HP'.
+        rewrite !in_cons 2!in_nil 2!orbF in IN IN'; des; rewrite IN IN'; try (by done);
+        subst tsk tsk'; simpl in *; by done.
+      Qed.
+
+      Lemma priority_is_total :
+        FP_is_total_over_task_set (RM task_period) ts.
+      Proof.
+        unfold RM; intros tsk tsk' IN IN'.
+        destruct (leqP (task_period tsk) (task_period tsk'));
+          [by left | by right; apply ltnW].
+      Qed.
+      
+    End FactsAboutPriorityOrder.      
+    
+    (* Next, recall the FP RTA schedulability test for APA scheduling.
+       Note that the task functions (from implementation/apa/task.v)
+       require num_cpus as a parameter, so we leave a blank space so that
+       can be inferred automatically. *)
+    Let schedulability_test :=
+      fp_schedulable (@task_cost _) (@task_period _) (@task_deadline _)
+                     num_cpus (RM task_period) task_affinity
+                     task_affinity. (* For simplicity, we use subaffinity alpha' = alpha. *)
+
+    (* Now we show that the schedulability test returns true. *)
+    Fact schedulability_test_succeeds :
+      schedulability_test ts = true.
+    Proof.
+      unfold schedulability_test, fp_schedulable, fp_claimed_bounds; simpl.
+      unfold total_interference_bound_fp, div_floor.
+      rewrite big_nil div0n addn0 /=.
+      unfold div_floor; rewrite !set_card /=.   
+
+      have cpu0P: 0 < 2 by done.
+      have cpu1P: 1 < 2 by done.
+
+      have cpu_compute :
+        forall (P: processor num_cpus -> bool),
+          [exists x, P x] =
+            if (P (cpu 0 cpu0P)) then true else
+              if P (cpu 1 cpu1P) then true else false.
+      {
+        unfold num_cpus in *; intros P.
+        destruct (P (cpu 0 cpu0P)) eqn:P0;
+          first by apply/existsP; exists (cpu 0 cpu0P).
+        destruct (P (cpu 1 cpu1P)) eqn:P1;
+          first by apply/existsP; exists (cpu 1 cpu1P).
+        apply negbTE; rewrite negb_exists; apply/forallP.
+        intros x; destruct x as [x LE]; apply negbT.
+        have GE0 := leq0n x.
+        rewrite leq_eqVlt in GE0; move: GE0 => /orP [/eqP EQ0 | GE1];
+          first by rewrite -P0; f_equal; apply ord_inj.
+        rewrite leq_eqVlt in GE1; move: GE1 => /orP [/eqP EQ1 | GE2];
+          first by rewrite -P1; f_equal; apply ord_inj.
+        have LE' := LE.
+        apply leq_ltn_trans with (m := 2) in LE'; last by done.
+        by rewrite ltnn in LE'.
+      }
+
+      set I2 := total_interference_bound_fp task_cost task_period
+                        task_affinity tsk2 alpha2  [:: (tsk1, 3)].
+
+      assert (H1: I2 2 (RM task_period) = 1).
+      {
+        unfold I2, total_interference_bound_fp; rewrite big_cons big_nil.
+        unfold higher_priority_task_in; simpl.
+        by rewrite /affinity_intersects cpu_compute /= addn0; compute.
+      }
+      rewrite H1 !divn1 !addn1; clear H1.
+      assert (H1: I2 3 (RM task_period) = 2).
+      {
+        unfold I2, total_interference_bound_fp; rewrite big_cons big_nil.
+        unfold higher_priority_task_in; simpl.
+        by rewrite /affinity_intersects cpu_compute /= addn0; compute.
+      }
+      rewrite H1 !addn2; clear H1.
+      assert (H1: I2 4 (RM task_period) = 3).
+      {
+        unfold I2, total_interference_bound_fp; rewrite big_cons big_nil.
+        unfold higher_priority_task_in; simpl.
+        by rewrite /affinity_intersects cpu_compute /= addn0; compute.
+      }
+      rewrite H1 !addn3; clear H1.
+      assert (H1: I2 5 (RM task_period) = 3).
+      {
+        unfold I2, total_interference_bound_fp; rewrite big_cons big_nil.
+        unfold higher_priority_task_in; simpl.
+        by rewrite /affinity_intersects cpu_compute /= addn0; compute.
+      }
+      rewrite H1 !addn3; clear H1.
+      have H2: 4 < 5 by compute.
+      rewrite H2; clear H2 I2.
+      unfold fp_bound_of_task.
+
+      Ltac f := unfold div_floor;
+                rewrite !big_cons big_nil /= /higher_priority_task_in /=
+                        /affinity_intersects !addn0 /= ?set_card ?divn1 ?addn0;
+                unfold interference_bound_generic, W, max_jobs, div_floor;
+                rewrite addn1 ?addn0.
+
+      have H4: per_task_rta task_cost task_period num_cpus (RM task_period) task_affinity task_affinity tsk3 [:: (tsk1, 3); (tsk2, 5)]
+                            (max_steps task_cost task_deadline tsk3) = 5.
+      {
+        rewrite /per_task_rta iterSr /div_floor set_card.
+        Ltac g :=
+            rewrite /total_interference_bound_fp /interference_bound_generic
+                    /W /max_jobs /div_floor !big_cons big_nil /= /higher_priority_task_in /=
+                    /affinity_intersects.
+        set x9 := total_interference_bound_fp _ _ _ _ _ _ _ _.
+        have I9: x9 = 1 by unfold x9; g; rewrite ?cpu_compute /=; compute.
+        rewrite I9 iterSr.
+        set x8 := total_interference_bound_fp _ _ _ _ _ _ _ _.
+        have I8: x8 = 2 by unfold x8; g; rewrite ?cpu_compute /=; compute.
+        rewrite I8 iterSr.
+        set x7 := total_interference_bound_fp _ _ _ _ _ _ _ _.
+        have I7: x7 = 3 by unfold x7; g; rewrite ?cpu_compute /=; compute.
+        rewrite I7 iterSr.
+        set x6 := total_interference_bound_fp _ _ _ _ _ _ _ _.
+        have I6: x6 = 3 by unfold x6; g; rewrite ?cpu_compute /=; compute.
+        rewrite I6 iterSr.
+        set x5 := total_interference_bound_fp _ _ _ _ _ _ _ _.
+        have I5: x5 = 3 by unfold x5; g; rewrite ?cpu_compute /=; compute.
+        rewrite I5 iterSr.
+        set x4 := total_interference_bound_fp _ _ _ _ _ _ _ _.
+        have I4: x4 = 3 by unfold x4; g; rewrite ?cpu_compute /=; compute.
+        rewrite I4 iterSr.
+        set x3 := total_interference_bound_fp _ _ _ _ _ _ _ _.
+        have I3: x3 = 3 by unfold x3; g; rewrite ?cpu_compute /=; compute.
+        rewrite I3 iterSr.
+        set x2 := total_interference_bound_fp _ _ _ _ _ _ _ _.
+        have I2: x2 = 3 by unfold x2; g; rewrite ?cpu_compute /=; compute.
+        rewrite I2 iterSr.
+        set x1 := total_interference_bound_fp _ _ _ _ _ _ _ _.
+        have I1: x1 = 3 by unfold x1; g; rewrite ?cpu_compute /=; compute.
+        rewrite I1 iterSr /=.
+        set x0 := total_interference_bound_fp _ _ _ _ _ _ _ _.
+        have I0: x0 = 3 by unfold x0; g; rewrite ?cpu_compute /=; compute.
+        by rewrite I0; compute.
+      }
+      by rewrite H4.
+    Qed.
+
+    (* Let sched be the work-conserving RM APA scheduler. *)
+    Let sched := scheduler job_cost job_task num_cpus arr_seq task_affinity higher_priority.
+
+    (* Recall the definition of deadline miss. *)
+    Let no_deadline_missed_by :=
+      task_misses_no_deadline job_cost job_deadline job_task sched.
+
+    (* Next, we prove that ts is schedulable with the result of the test. *)
+    Corollary ts_is_schedulable:
+      forall tsk,
+        tsk \in ts ->
+        no_deadline_missed_by tsk.
+    Proof.
+      intros tsk IN.
+      have VALID := periodic_arrivals_valid_job_parameters ts ts_has_valid_parameters.
+      have TSVALID := ts_has_valid_parameters.
+      unfold valid_sporadic_job, valid_realtime_job in *; des.
+      apply taskset_schedulable_by_fp_rta with (task_cost := task_cost)
+       (task_period := task_period) (task_deadline := task_deadline)
+       (ts0 := ts) (higher_priority0 := RM task_period)
+       (alpha := task_affinity) (alpha' := task_affinity); try (by done).
+      - by apply ts_has_constrained_deadlines.
+      - by apply ts_non_empty_affinities.
+      - by ins.
+      - by apply ts_has_unique_priorities.
+      - by apply priority_is_total.
+      - by apply RM_is_transitive.
+      - by apply periodic_arrivals_all_jobs_from_taskset.
+      - by apply periodic_arrivals_are_sporadic.
+      - by apply scheduler_jobs_must_arrive_to_execute.
+      - apply scheduler_completed_jobs_dont_execute; intro j'.
+        -- by specialize (VALID j'); des.
+        -- by apply periodic_arrivals_is_a_set.
+      - by apply scheduler_sequential_jobs, periodic_arrivals_is_a_set.
+      - by apply scheduler_respects_affinity.
+      - apply scheduler_apa_work_conserving.
+        -- by apply periodic_arrivals_is_a_set.
+        -- by ins; apply RM_is_transitive.
+        {
+          unfold FP_to_JLDP; intros t x y; apply/orP.
+          by apply priority_is_total; rewrite periodic_arrivals_all_jobs_from_taskset.
+        }
+      - apply scheduler_enforces_policy.
+        -- by apply periodic_arrivals_is_a_set.
+        -- by ins; apply RM_is_transitive.
+        {
+          unfold FP_to_JLDP; intros t x y; apply/orP.
+          by apply priority_is_total; rewrite periodic_arrivals_all_jobs_from_taskset.
+        }
+      - by apply schedulability_test_succeeds.
+    Qed.
+
+  End ExampleRTA.
+
+End ResponseTimeAnalysisFP.
\ No newline at end of file
diff --git a/implementation/apa/job.v b/implementation/apa/job.v
new file mode 100644
index 0000000000000000000000000000000000000000..bf22a52aca85ee2489c0b591eb14e871252defd6
--- /dev/null
+++ b/implementation/apa/job.v
@@ -0,0 +1,63 @@
+Require Import rt.model.basic.time rt.util.all.
+Require Import rt.implementation.apa.task.
+From mathcomp Require Import ssreflect ssrbool ssrnat eqtype seq.
+
+Module ConcreteJob.
+
+  Import Time.
+  Import ConcreteTask.
+
+  Section Defs.
+
+    Context {num_cpus: nat}.
+    
+    (* Definition of a concrete task. *)
+    Record concrete_job :=
+    {
+      job_id: nat;
+      job_cost: time;
+      job_deadline: time;
+      job_task: @concrete_task num_cpus
+    }.
+
+    (* To make it compatible with ssreflect, we define a decidable
+       equality for concrete jobs. *)
+    Definition job_eqdef (j1 j2: concrete_job) :=
+      (job_id j1 == job_id j2) &&
+      (job_cost j1 == job_cost j2) &&
+      (job_deadline j1 == job_deadline j2) &&
+      (job_task j1 == job_task j2).
+
+    (* Next, we prove that job_eqdef is indeed an equality, ... *)
+    Lemma eqn_job : Equality.axiom job_eqdef.
+    Proof.
+      unfold Equality.axiom; intros x y.
+      destruct (job_eqdef x y) eqn:EQ.
+      {
+        apply ReflectT; unfold job_eqdef in *.
+        move: EQ => /andP [/andP [/andP [/eqP ID /eqP COST] /eqP DL] /eqP TASK].
+        by destruct x, y; simpl in *; subst.
+      }
+      {
+        apply ReflectF.
+        unfold job_eqdef, not in *; intro BUG.
+        apply negbT in EQ; rewrite negb_and in EQ.
+        destruct x, y.
+        rewrite negb_and in EQ.
+        move: EQ => /orP [EQ | /eqP TASK]; last by apply TASK; inversion BUG.
+        move: EQ => /orP [EQ | /eqP DL].
+        rewrite negb_and in EQ.
+        move: EQ => /orP [/eqP ID | /eqP COST].
+        by apply ID; inversion BUG.
+        by apply COST; inversion BUG.
+        by apply DL; inversion BUG.
+      }
+    Qed.
+
+    (* ..., which allows instantiating the canonical structure. *)
+    Canonical concrete_job_eqMixin := EqMixin eqn_job.
+    Canonical concrete_job_eqType := Eval hnf in EqType concrete_job concrete_job_eqMixin.
+
+   End Defs.
+    
+End ConcreteJob.
\ No newline at end of file
diff --git a/implementation/apa/schedule.v b/implementation/apa/schedule.v
new file mode 100644
index 0000000000000000000000000000000000000000..80014bdb26b6f64c3c677a86fcd5256ca7fab092
--- /dev/null
+++ b/implementation/apa/schedule.v
@@ -0,0 +1,748 @@
+Require Import rt.util.all.
+Require Import rt.model.apa.job rt.model.apa.task rt.model.apa.affinity
+               rt.model.apa.arrival_sequence rt.model.apa.schedule
+               rt.model.apa.platform rt.model.apa.priority.
+From mathcomp Require Import ssreflect ssrbool ssrfun eqtype ssrnat fintype bigop seq path.
+
+Module ConcreteScheduler.
+
+  Import Job SporadicTaskset ArrivalSequence Schedule Platform
+         Priority Affinity.
+
+  (* In this section, we implement a concrete weak APA scheduler. *)
+  Section Implementation.
+    
+    Context {Job: eqType}.
+    Context {sporadic_task: eqType}.
+    
+    Variable job_cost: Job -> time.
+    Variable job_task: Job -> sporadic_task.
+
+    (* Let num_cpus denote the number of processors, ...*)
+    Variable num_cpus: nat.
+
+    (* ... and let arr_seq be any arrival sequence.*)
+    Variable arr_seq: arrival_sequence Job.
+
+    (* Let alpha be an affinity associated with each task. *)
+    Variable alpha: task_affinity sporadic_task num_cpus.
+    
+    (* Assume a JLDP policy is given. *)
+    Variable higher_eq_priority: JLDP_policy arr_seq.
+
+    (* Consider the list of pending jobs at time t, ... *)
+    Definition jobs_pending_at (sched: schedule num_cpus arr_seq) (t: time) :=
+      [seq j <- jobs_arrived_up_to arr_seq t | pending job_cost sched j t].
+
+    (* ... which we sort by decreasing priority. *)
+    Definition sorted_pending_jobs (sched: schedule num_cpus arr_seq) (t: time) :=
+      sort (higher_eq_priority t) (jobs_pending_at sched t).
+
+    (* Now we implement the algorithm that generates the APA schedule. *)
+    
+    (* Given a job j at time t, we first define a predicate that states
+       whether j should preempt a mapping (cpu, x), where x is either Some j'
+       that is currently mapped to cpu or None. *)
+      Definition should_be_scheduled (j: JobIn arr_seq) (t: time) p :=
+      let '(cpu, mapped_job) := p in
+        if mapped_job is Some j' then (* If there is a job j', check the priority and affinity. *)
+          (can_execute_on alpha (job_task j) cpu) &&
+          ~~ (higher_eq_priority t j' j)
+        else (* Else, if cpu is idle, check only the affinity. *)
+          (can_execute_on alpha (job_task j) cpu).
+    
+    (* Next, using the "should_be_scheduled" predicate, we define a function
+       that tries to schedule job j by updating a list of mappings.
+       It does so by replacing the first pair (cpu, x) where j can be
+       scheduled (if it exists). *)
+    Definition update_available_cpu t allocation j :=
+      replace_first (should_be_scheduled j t) (* search for processors that j can preempt *)
+                    (set_pair_2nd (Some j)) (* replace the mapping in that processor with j *)
+                    allocation. (* list of mappings *)
+
+    (* Using the fuction "update_available_cpu", we now define an iteration
+       that tries to successively schedule each job in a list job_list.
+       Starting with an empty mapping,
+       <(cpu0, None), (cpu1, None), (cpu2, None), ...>,
+       it tries to schedule each job in job_list and yields some updated list: 
+       <(cpu0, None), (cpu1, Some j5), (cpu2, j9), ...>. *)
+    Definition try_to_schedule_every_job job_list t :=
+      foldl (update_available_cpu t)
+            (zip (enum (processor num_cpus)) (nseq num_cpus None)) (* empty mapping*)
+            job_list.
+
+    (* The iteration we just defined is then applied to the list of pending jobs
+       at any time t. *)
+    Definition schedule_every_pending_job prev_sched t :=
+      try_to_schedule_every_job (sorted_pending_jobs prev_sched t) t.
+
+    (* The schedule can now be constructed iteratively. Starting from the empty schedule, ... *)
+    Definition empty_schedule : schedule num_cpus arr_seq :=
+      fun cpu t => None.
+
+    (* ..., we update the schedule at time t by calling schedule_every_pending_job with
+       the list of pending jobs at time t, and then converting the result to a function. *)
+    Definition update_schedule (prev_sched: schedule num_cpus arr_seq)
+                               (t_next: time) : schedule num_cpus arr_seq :=
+      fun cpu t =>
+        if t == t_next then
+          pairs_to_function None (schedule_every_pending_job prev_sched t) cpu
+        else prev_sched cpu t.
+    
+    (* This allows us to iteratively construct the schedule up to some time t_max. *)
+    Fixpoint schedule_prefix (t_max: time) : schedule num_cpus arr_seq := 
+      if t_max is t_prev.+1 then
+        (* At time t_prev + 1, schedule jobs that have not completed by time t_prev. *)
+        update_schedule (schedule_prefix t_prev) t_prev.+1
+      else
+        (* At time 0, schedule any jobs that arrive. *)
+        update_schedule empty_schedule 0.
+
+    (* Finally, the prefixes are used to build the complete schedule. *)
+    Definition scheduler (cpu: processor num_cpus) (t: time) := (schedule_prefix t) cpu t.
+
+  End Implementation.
+
+  (* In this section, we prove several properties about the scheduling algorithm we
+     implemented. For example, we show that it is APA work conserving. *)
+  Section Proofs.
+
+    Context {Job: eqType}.
+    Context {sporadic_task: eqType}.
+    
+    Variable job_cost: Job -> time.
+    Variable job_task: Job -> sporadic_task.
+
+    (* Assume a positive number of processors. *)
+    Variable num_cpus: nat.
+    Hypothesis H_at_least_one_cpu: num_cpus > 0.
+
+    (* Let alpha be an affinity associated with each task. *)
+    Variable alpha: task_affinity sporadic_task num_cpus.
+    
+    (* Let arr_seq be any arrival sequence of jobs where ...*)
+    Variable arr_seq: arrival_sequence Job.
+    (* ...jobs have positive cost and...*)
+    Hypothesis H_job_cost_positive:
+      forall (j: JobIn arr_seq), job_cost_positive job_cost j.
+    (* ... at any time, there are no duplicates of the same job. *)
+    Hypothesis H_arrival_sequence_is_a_set :
+      arrival_sequence_is_a_set arr_seq.
+
+    (* Consider any JLDP policy higher_eq_priority that is transitive and total. *)
+    Variable higher_eq_priority: JLDP_policy arr_seq.
+    Hypothesis H_priority_transitive: forall t, transitive (higher_eq_priority t).
+    Hypothesis H_priority_total: forall t, total (higher_eq_priority t).
+    
+    (* Let sched denote our concrete scheduler implementation. *)
+    Let sched := scheduler job_cost job_task num_cpus arr_seq alpha higher_eq_priority.
+    
+    (* Next, we provide some helper lemmas about the scheduler construction. *)
+    Section HelperLemmas.
+
+      (* Let sched_prefix denote the prefix construction of our scheduler. *)
+      Let sched_prefix := schedule_prefix job_cost job_task num_cpus arr_seq alpha higher_eq_priority.
+
+      (* We begin by showing that the scheduler preserves its prefixes. *)
+      Lemma scheduler_same_prefix :
+        forall t t_max cpu,
+          t <= t_max ->
+          sched_prefix t_max cpu t = sched cpu t.
+      Proof.
+        intros t t_max cpu LEt.
+        induction t_max; first by rewrite leqn0 in LEt; move: LEt => /eqP EQ; subst.
+        {
+          rewrite leq_eqVlt in LEt.
+          move: LEt => /orP [/eqP EQ | LESS]; first by subst.
+          {
+            feed IHt_max; first by done.
+            unfold sched_prefix, schedule_prefix, update_schedule at 1.
+            assert (FALSE: t == t_max.+1 = false).
+            {
+              by apply negbTE; rewrite neq_ltn LESS orTb.
+            } rewrite FALSE.
+            by rewrite -IHt_max.
+          }
+        }
+      Qed.
+
+      (* To avoid many parameters, let's also rename the scheduling function.
+         We make a generic version (for any list of jobs l), ... *)
+      Let schedule_jobs t l := try_to_schedule_every_job job_task num_cpus arr_seq alpha higher_eq_priority t l.
+      (* ... and a specific version (for the pending jobs in sched). *)
+      Let schedule_pending_jobs t := schedule_jobs (sorted_pending_jobs job_cost num_cpus arr_seq higher_eq_priority sched t) t.
+
+      (* Next, we show that there are no duplicate cpus in the mapping. *)
+      Lemma scheduler_uniq_cpus :
+        forall t l,
+          uniq (unzip1 (schedule_jobs l t)).
+      Proof.
+        intros t l.
+        induction l as [| l' j_last] using last_ind.
+        {
+          by rewrite unzip1_zip; [rewrite enum_uniq | rewrite size_enum_ord size_nseq].
+        }
+        {
+          rewrite /schedule_jobs /try_to_schedule_every_job -cats1 foldl_cat /=.
+          assert (EQ: forall l j, unzip1 (update_available_cpu job_task num_cpus arr_seq
+                                                alpha higher_eq_priority t l j) = unzip1 l).
+          {
+            intros l j; clear -l j.
+            induction l; first by done.
+            unfold update_available_cpu; simpl.
+            by case SHOULD: should_be_scheduled; simpl; f_equal.
+          }
+          by rewrite EQ.
+        }
+      Qed.
+
+      (* Next, we show that if a job j is in the mapping, then j must come from the list
+         of jobs l used in the construction. *)
+      Lemma scheduler_job_in_mapping :
+        forall l j t cpu,
+          (cpu, Some j) \in schedule_jobs l t -> j \in l.
+      Proof.
+        intros l j t cpu SOME; clear -SOME.
+        unfold schedule_every_pending_job in *.
+        induction l as [| l' j'] using last_ind; simpl in *.
+        {
+          apply mem_zip in SOME; last by rewrite size_enum_ord size_nseq.
+          by move: SOME => [_ /nseqP [SOME _]].
+        }
+        {
+          rewrite /schedule_jobs /try_to_schedule_every_job -cats1 foldl_cat /= in SOME.
+          unfold update_available_cpu in SOME.
+          elim (replace_first_cases SOME) => [IN | [y [NEW IN]]].
+          {
+            by rewrite mem_rcons in_cons; apply/orP; right; apply IHl.
+          }
+          {
+            case: NEW => EQ1 EQ2; subst.
+            by rewrite mem_rcons in_cons eq_refl orTb.
+          }
+        }
+      Qed.
+
+      (* Next, we prove that if a pair (cpu, j) is in the mapping, then
+         cpu must be part of j's affinity. *)
+      Lemma scheduler_mapping_respects_affinity :
+        forall j t cpu,
+          (cpu, Some j) \in schedule_pending_jobs t ->
+          can_execute_on alpha (job_task j) cpu.
+      Proof.
+        intros j t cpu SOME.
+        unfold schedule_pending_jobs, schedule_every_pending_job in SOME.
+        set l := sorted_pending_jobs _ _ _ _ _ _ in SOME.
+        induction l as [| l' j'] using last_ind; simpl in *.
+        {
+          apply mem_zip in SOME; last by rewrite size_enum_ord size_nseq.
+          by move: SOME => [_ /nseqP [BUG _]].
+        }
+        {
+          unfold schedule_jobs, try_to_schedule_every_job in SOME.
+          rewrite -cats1 foldl_cat /= in SOME.
+          elim (replace_first_cases SOME) => [IN | [y [NEW [SHOULD _]]]];
+            first by apply IHl.
+          case: NEW => EQ1 EQ2; subst.
+          by unfold should_be_scheduled in SHOULD; desf.
+        }
+      Qed.
+
+      (* Next, we show that the mapping does not schedule the same job j in two
+         different cpus. *)
+      Lemma scheduler_has_no_duplicate_jobs :
+        forall j t cpu1 cpu2,
+          (cpu1, Some j) \in schedule_pending_jobs t ->
+          (cpu2, Some j) \in schedule_pending_jobs t ->
+          cpu1 = cpu2.
+      Proof.
+        intros j t cpu1 cpu2 SOME1 SOME2.
+        unfold schedule_pending_jobs, schedule_every_pending_job in *.
+        set l := sorted_pending_jobs _ _ _ _ _ _ in SOME1 SOME2.
+        assert (UNIQ: uniq l).
+          by rewrite sort_uniq; apply filter_uniq, JobIn_uniq.
+          
+        induction l as [| l' j'] using last_ind; simpl in *.
+        {
+          apply mem_zip in SOME1; last by rewrite size_enum_ord size_nseq.
+          by move: SOME1 => [_ /nseqP [BUG _]].
+        }
+        {
+          rewrite rcons_uniq in UNIQ; move: UNIQ => /andP [NOTIN UNIQ].
+          rewrite /schedule_jobs /try_to_schedule_every_job -cats1 foldl_cat /= in SOME1 SOME2.
+          destruct (replace_first_cases SOME1) as [SAME1 | [[cpu1' p1] [EQ1 [SHOULD1 PREV1]]]];
+          destruct (replace_first_cases SOME2) as [SAME2 | [[cpu2' p2] [EQ2 [SHOULD2 PREV2]]]].
+          {
+            by apply IHl.
+          }
+          {
+            move: EQ2; case => EQ1 EQ2; subst cpu2' j'.
+            destruct p2 as [j2 |].
+            {
+              apply scheduler_job_in_mapping in SAME1.
+              by rewrite SAME1 in NOTIN.
+            }
+            by apply (replace_first_new _ _ _ (cpu2, Some j)) in SOME1;
+              [by move: SOME1; case => -> | | | by done];
+              apply/negP; intro BUG;
+              apply scheduler_job_in_mapping in BUG;
+              by rewrite BUG in NOTIN.
+          }
+          {
+            move: EQ1; case => EQ1 EQ2; subst cpu1' j'.
+            destruct p1 as [j1 |].
+            {
+              apply scheduler_job_in_mapping in SAME2.
+              by rewrite SAME2 in NOTIN.
+            }
+            by apply (replace_first_new _ _ _ (cpu2, Some j)) in SOME1;
+              [by move: SOME1; case => -> | | | by done];
+              apply/negP; intro BUG;
+              apply scheduler_job_in_mapping in BUG;
+              by rewrite BUG in NOTIN.
+          }
+          {
+            move: EQ1; case => /= EQ1' /= EQ2'; subst cpu1' j'.
+            move: EQ2; case => EQ1'; subst cpu2'.
+            by apply (replace_first_new _ _ _ (cpu2, Some j)) in SOME1;
+              [by move: SOME1; case => -> | | | by done];
+              apply/negP; intro BUG;
+              apply scheduler_job_in_mapping in BUG;
+              by rewrite BUG in NOTIN.
+          }
+        }
+      Qed.
+
+      (* Based on the definition of the schedule, a job j is scheduled on cpu
+         iff (cpu, Some j) is the final mapping. *)
+      Lemma scheduler_scheduled_on :
+        forall j cpu t,
+          scheduled_on sched j cpu t = ((cpu, Some j) \in schedule_pending_jobs t).
+      Proof.
+        unfold schedule_pending_jobs, schedule_jobs in *.
+        intros j cpu t.
+        induction t.
+        {
+          apply/idP/idP.
+          {
+            intros SCHED.
+            unfold scheduled_on, sched, scheduler, schedule_prefix in SCHED.
+            rewrite /update_schedule eq_refl in SCHED; move: SCHED => /eqP SCHED.
+            apply pairs_to_function_neq_default in SCHED; last by done.
+            unfold schedule_every_pending_job in SCHED.
+            set l := sorted_pending_jobs _ _ _ _ _ _ in SCHED.
+            set l' := sorted_pending_jobs _ _ _ _ _ _.
+            assert (EQ: l' = l).
+            {
+              unfold l, l', sorted_pending_jobs; f_equal.
+              unfold jobs_pending_at; apply eq_filter.
+              unfold pending; red; intro j0; do 2 f_equal.
+              unfold completed; f_equal.
+              by unfold service; rewrite big_geq // big_geq //.
+            }
+            by rewrite EQ.
+          }
+          {
+            intros SCHED.
+            have MEM := pairs_to_function_mem None.
+            apply MEM in SCHED; clear MEM; last by apply (scheduler_uniq_cpus 0).
+            unfold scheduled_on, sched, scheduler, schedule_prefix.
+            rewrite /update_schedule eq_refl.
+            rewrite -SCHED; apply/eqP; f_equal.
+            unfold schedule_pending_jobs, schedule_jobs, schedule_every_pending_job; f_equal.
+            unfold sorted_pending_jobs, jobs_pending_at; f_equal.
+            apply eq_filter; intros j0.
+            unfold pending; do 2 f_equal.
+            unfold completed; f_equal.
+            by unfold service; rewrite big_geq // big_geq //.
+          }
+        }
+        {
+          apply/idP/idP.
+          {
+            intros SCHED.
+            unfold scheduled_on, sched, scheduler, schedule_prefix in SCHED.
+            rewrite /update_schedule eq_refl in SCHED; move: SCHED => /eqP SCHED.
+            apply pairs_to_function_neq_default in SCHED; last by done.
+            unfold schedule_every_pending_job in SCHED.
+            set l := try_to_schedule_every_job _ _ _ _ _ _ _ in SCHED.
+            set l' := try_to_schedule_every_job _ _ _ _ _ _ _.
+            assert (EQ: l' = l).
+            {
+              unfold l', l, schedule_every_pending_job; f_equal.
+              unfold sorted_pending_jobs, jobs_pending_at; f_equal.
+              apply eq_filter; intros j0.
+              unfold pending; do 2 f_equal.
+              unfold completed; f_equal.
+              unfold service. rewrite big_nat_recr // big_nat_recr //=.
+              f_equal; apply eq_big_nat; intros t0 LE.
+              unfold service_at; apply eq_bigl; intros cpu0.
+              unfold scheduled_on; f_equal.
+              unfold sched; move: LE => /andP [_ LE].
+              by rewrite <- scheduler_same_prefix with (t_max := t); last by apply ltnW.
+            }
+            by rewrite EQ.
+          }
+          {
+            intros SCHED.
+            have MEM := pairs_to_function_mem None.
+            apply MEM in SCHED; clear MEM; last by apply (scheduler_uniq_cpus t.+1).
+            unfold scheduled_on, sched, scheduler, schedule_prefix.
+            rewrite /update_schedule eq_refl.
+            rewrite -SCHED; apply/eqP; f_equal.
+            unfold schedule_every_pending_job; f_equal.
+            unfold sorted_pending_jobs, jobs_pending_at; f_equal.
+            apply eq_filter; intros j0.
+            unfold pending; do 2 f_equal.
+            unfold completed; f_equal.
+            unfold service. rewrite big_nat_recr // big_nat_recr //=.
+            f_equal; apply eq_big_nat; intros t0 LE.
+            unfold service_at; apply eq_bigl; intros cpu0.
+            unfold scheduled_on; f_equal.
+            unfold sched; move: LE => /andP [_ LE].
+            by rewrite <- scheduler_same_prefix with (t_max := t); last by apply ltnW.
+          }  
+        }
+      Qed.
+
+      (* Now we show that for every cpu, there is always a pair in the mapping. *)
+      Lemma scheduler_has_cpus :
+        forall cpu t l,
+          exists x,
+            (cpu, x) \in schedule_jobs l t. 
+      Proof.
+        intros cpu t l.
+        induction l as [| l' j_last] using last_ind; simpl in *.
+        {
+          by exists None; rewrite mem_zip_nseq_r;
+            [by rewrite mem_enum | by rewrite size_enum_ord].
+        }
+        {
+          rewrite /schedule_jobs /try_to_schedule_every_job -cats1 foldl_cat /=.
+          move: IHl => [x IN].
+          eapply replace_first_previous in IN; des;
+            first by exists x; apply IN.
+          rewrite /set_pair_2nd in IN.
+          by exists (Some j_last); apply IN0.
+        }
+      Qed.
+
+      (* Next, consider a list of jobs l that is sorted by priority and does not have
+         duplicates.
+         We prove that for any job j in l, if j is not scheduled at time t,
+         then every cpu in j's affinity has some job mapped at time t.  *)
+      Lemma scheduler_mapping_is_work_conserving :
+        forall j cpu t l,
+          j \in l ->
+          sorted (higher_eq_priority t) l ->
+          uniq l ->
+          (forall cpu, (cpu, Some j) \notin schedule_jobs l t) ->
+          can_execute_on alpha (job_task j) cpu ->
+          exists j_other,
+            (cpu, Some j_other) \in schedule_jobs l t.
+      Proof.
+        intros j cpu t l IN SORT UNIQ NOTSCHED CAN.
+        
+        generalize dependent cpu.
+        induction l as [| l' j_last] using last_ind; simpl in *;
+          first by rewrite in_nil in IN.
+        {
+          intros cpu CAN.
+          rewrite rcons_uniq in UNIQ; move: UNIQ => /andP [NOTIN UNIQ].
+          rewrite /schedule_jobs /try_to_schedule_every_job -cats1 foldl_cat /= in NOTSCHED *.
+          set prev := foldl (update_available_cpu  _ _ _ _ _ _) (zip _ _) l' in NOTSCHED *.
+          rewrite mem_rcons in_cons in IN.
+          move: IN => /orP [/eqP IN | LAST]; subst.
+          {
+            clear IHl.
+            assert (ALL: forall x, x \in prev -> ~~ (should_be_scheduled job_task num_cpus arr_seq
+                                                            alpha higher_eq_priority j_last t) x).
+            {
+              apply replace_first_failed with (f := set_pair_2nd (Some j_last)).
+              by intros [cpu' j'] IN; apply NOTSCHED.
+            }
+            have [x IN] := scheduler_has_cpus cpu t l'; fold prev in IN.
+            specialize (ALL (cpu, x) IN).
+            simpl in ALL.
+            destruct x as [j' |]; last by rewrite CAN in ALL.
+            eapply replace_first_previous in IN; des;
+              first by exists j'; apply IN.
+            by exists j_last; apply IN0.              
+          }
+          {
+            specialize (IHl LAST).
+            feed_n 2 IHl;
+              [by apply sorted_rcons_prefix in SORT | by done | ].
+            feed IHl.
+            {
+              clear IHl; intros cpu'; specialize (NOTSCHED cpu').
+              apply/negP; intro BUG.
+              apply replace_first_previous with (f := set_pair_2nd (Some j_last))
+                (P := should_be_scheduled job_task num_cpus arr_seq alpha higher_eq_priority j_last t)
+                in BUG; des;
+                first by rewrite BUG in NOTSCHED.
+              move: BUG => /andP [_ /negP HP].
+              by apply HP, order_sorted_rcons with (s := l'); try by done.
+            }
+            move: (IHl cpu CAN) => [j_old IN]; clear IHl LAST.
+            by eapply replace_first_previous in IN; des;
+              [exists j_old; apply IN | exists j_last; apply IN0].
+          }
+        }
+      Qed.
+
+      (* Next, we prove that the mapping enforces priority. *)
+      Lemma scheduler_priority :
+        forall j j_hp cpu t,
+          backlogged job_cost sched j t ->
+          can_execute_on alpha (job_task j) cpu ->
+          scheduled_on sched j_hp cpu t ->
+          higher_eq_priority t j_hp j.
+      Proof.
+        have SCHED_ON := scheduler_scheduled_on.
+        intros j j_hp cpu t BACK CAN SCHED.
+        move: BACK => /andP [PENDING NOTSCHED'].
+        assert (NOTSCHED: forall cpu, (cpu, Some j) \notin schedule_pending_jobs t). 
+        {
+          intros cpu'; rewrite -SCHED_ON.
+          by rewrite negb_exists in NOTSCHED'; move: NOTSCHED' => /forallP ALL; apply ALL.
+        }
+        rewrite SCHED_ON in SCHED.
+        clear NOTSCHED' SCHED_ON.
+
+        unfold schedule_pending_jobs, schedule_jobs, schedule_every_pending_job in *.
+        set l := sorted_pending_jobs _ _ _ _ _ _ in SCHED NOTSCHED.
+        
+        have IN: j \in l.
+        {
+          rewrite mem_sort mem_filter PENDING andTb JobIn_has_arrived.
+          by move: PENDING => /andP [H _].
+        }
+        have INhp: j_hp \in l by apply scheduler_job_in_mapping in SCHED.
+        have SORT : sorted (higher_eq_priority t) l by apply sort_sorted.
+        have UNIQ: uniq l by rewrite sort_uniq filter_uniq // JobIn_uniq //.
+
+        induction l as [| l' j_last] using last_ind;
+          first by rewrite in_nil in IN.
+        {
+          rewrite /try_to_schedule_every_job -cats1 foldl_cat /= in SCHED.
+          rewrite /try_to_schedule_every_job -cats1 foldl_cat /= in NOTSCHED.
+          set prev := foldl (update_available_cpu job_task num_cpus arr_seq alpha higher_eq_priority t) (zip (enum (processor num_cpus)) (nseq num_cpus None)) l' in SCHED NOTSCHED.
+          rewrite rcons_uniq in UNIQ; move: UNIQ => /andP [NOTIN UNIQ].
+          rewrite 2!mem_rcons 2!in_cons in IN INhp.
+          move: IN => /orP [/eqP EQ | IN];
+          move: INhp => /orP [/eqP EQhp | INhp].
+          {
+            subst j_last j_hp.
+            by specialize (NOTSCHED cpu); rewrite SCHED in NOTSCHED.
+          }
+          {
+            subst j_last.
+            by apply order_sorted_rcons with (s := l').
+          }
+          {
+            subst j_last; clear IHl.
+            destruct (replace_first_cases SCHED) as [PREV | [[cpu' y] [EQ [SHOULD IN']]]].
+            {
+              unfold prev in PREV.
+              apply scheduler_job_in_mapping in PREV.
+              by rewrite PREV in NOTIN.
+            }
+            {
+              move: EQ; case; intro EQ1; subst cpu'.
+              destruct y as [j'|].
+              {
+                unfold should_be_scheduled in SHOULD.
+                move: SHOULD => /andP [CAN' /negP HP].
+                unfold prev in IN'.
+                apply scheduler_job_in_mapping in IN'.
+                by exfalso; apply HP, order_sorted_rcons with (s := l').
+              }
+              {
+                destruct [exists cpu, ((cpu, Some j) \in prev)] eqn:EX.
+                {
+                  move: EX => /existsP [cpu' IN''].
+                  unfold prev in IN''.
+                  apply replace_first_previous with (f := set_pair_2nd (Some j_hp)) (P := should_be_scheduled job_task num_cpus arr_seq alpha higher_eq_priority j_hp t) in IN''; des;
+                    first by specialize (NOTSCHED cpu'); rewrite IN'' in NOTSCHED.
+                  move: IN'' => /andP [_ /negP HP].
+                  by exfalso; apply HP, order_sorted_rcons with (s := l').
+                }
+                {
+                  apply negbT in EX; rewrite negb_exists in EX.
+                  move: EX => /forallP EX.
+                  apply sorted_rcons_prefix in SORT.
+                  move: (scheduler_mapping_is_work_conserving j cpu t l' IN SORT UNIQ EX CAN) => [j' BUG].
+                  have MEM := pairs_to_function_mem None.
+                  apply MEM in BUG; last by apply scheduler_uniq_cpus.
+                  have UNIQ' := scheduler_uniq_cpus t l'.
+                  apply MEM in IN'; last by apply UNIQ'.
+                  by rewrite IN' in BUG.
+                }
+              }
+            }
+          }
+          {
+            apply IHl; try (by done); last first.
+            {
+              by apply sorted_rcons_prefix in SORT.
+            }
+            {
+              intros cpu0; apply/negP; intro BUG.
+              unfold update_available_cpu in NOTSCHED.
+              apply replace_first_previous with (f := set_pair_2nd (Some j_last)) (P := should_be_scheduled job_task num_cpus arr_seq alpha higher_eq_priority j_last t) in BUG; des.
+              {
+                by specialize (NOTSCHED cpu0); rewrite BUG in NOTSCHED.
+              }
+              {
+                move: BUG => /andP [_ /negP HP].
+                by apply HP, order_sorted_rcons with (s := l'); try by done.
+              }
+            }
+            {
+              destruct (replace_first_cases SCHED) as [| [[cpu' y][EQ [SHOULD IN']]]]; first by done.
+              move: EQ; case; intros EQ1 EQ2; subst cpu' j_last.
+              by rewrite INhp in NOTIN.
+            }
+          }
+        }
+      Qed.
+
+    End HelperLemmas.
+
+    (* Now, we prove the important properties about the implementation. *)
+    
+    (* Jobs do not execute before they arrive, ...*)
+    Theorem scheduler_jobs_must_arrive_to_execute:
+      jobs_must_arrive_to_execute sched.
+    Proof.
+      unfold jobs_must_arrive_to_execute.
+      intros j t SCHED.
+      move: SCHED => /existsP [cpu /eqP SCHED].
+      unfold sched, scheduler, schedule_prefix in SCHED.
+      destruct t.
+      {
+        rewrite /update_schedule eq_refl in SCHED.
+        apply pairs_to_function_neq_default in SCHED; last by done.
+        unfold schedule_every_pending_job in SCHED.
+        apply scheduler_job_in_mapping in SCHED.
+        rewrite mem_sort mem_filter in SCHED.
+        move: SCHED => /andP [_ ARR].
+        by apply JobIn_has_arrived in ARR.
+      }
+      {
+        unfold update_schedule at 1 in SCHED; rewrite eq_refl /= in SCHED.
+        apply pairs_to_function_neq_default in SCHED; last by done.
+        unfold schedule_every_pending_job in SCHED.
+        apply scheduler_job_in_mapping in SCHED.
+        rewrite mem_sort mem_filter in SCHED.
+        move: SCHED => /andP [_ ARR].
+        by apply JobIn_has_arrived in ARR.
+      }
+    Qed.
+
+    (* ..., jobs are sequential, ... *)
+    Theorem scheduler_sequential_jobs: sequential_jobs sched.
+    Proof.
+      intros j t cpu1 cpu2 SCHED1 SCHED2.
+      by apply scheduler_has_no_duplicate_jobs with (j := j) (t := t);
+        rewrite -scheduler_scheduled_on; apply/eqP.
+    Qed.
+               
+    (* ... and jobs do not execute after completion. *)
+    Theorem scheduler_completed_jobs_dont_execute:
+      completed_jobs_dont_execute job_cost sched.
+    Proof.
+      have SEQ := scheduler_sequential_jobs.
+      rename H_job_cost_positive into GT0.
+      unfold completed_jobs_dont_execute, service.
+      intros j t.
+      induction t; first by rewrite big_geq.
+      {
+        rewrite big_nat_recr // /=.
+        rewrite leq_eqVlt in IHt; move: IHt => /orP [/eqP EQ | LESS]; last first.
+        {
+          destruct (job_cost j); first by rewrite ltn0 in LESS.
+          rewrite -addn1; rewrite ltnS in LESS.
+          by apply leq_add; last by apply service_at_most_one, SEQ. 
+        }
+        rewrite EQ -{2}[job_cost j]addn0; apply leq_add; first by done.
+        destruct t.
+        {
+          rewrite big_geq // in EQ.
+          specialize (GT0 j); unfold job_cost_positive in *.
+          by rewrite -EQ ltn0 in GT0.
+        }
+        {
+          unfold service_at; rewrite big_mkcond.
+          apply leq_trans with (n := \sum_(cpu < num_cpus) 0);
+            last by rewrite big_const_ord iter_addn mul0n addn0.
+          apply leq_sum; intros cpu _; desf.
+          move: Heq => /eqP SCHED.
+          unfold scheduler, schedule_prefix in SCHED.
+          unfold sched, scheduler, schedule_prefix, update_schedule at 1 in SCHED.
+          rewrite eq_refl in SCHED.
+          apply pairs_to_function_neq_default in SCHED; last by done.
+          unfold schedule_every_pending_job in SCHED.
+          apply scheduler_job_in_mapping in SCHED.
+          rewrite mem_sort mem_filter in SCHED.
+          move: SCHED => /andP [/andP [_ /negP NOTCOMP] _].
+          exfalso; apply NOTCOMP; clear NOTCOMP.
+          unfold completed; apply/eqP.
+          unfold service; rewrite -EQ.
+          rewrite big_nat_cond [\sum_(_ <= _ < _ | true)_]big_nat_cond.
+          apply eq_bigr; move => i /andP [/andP [_ LT] _].
+          apply eq_bigl; red; ins.
+          unfold scheduled_on; f_equal.
+          fold (schedule_prefix job_cost job_task num_cpus arr_seq alpha higher_eq_priority).
+          by rewrite scheduler_same_prefix.
+        }
+      }
+    Qed.
+
+    (* In addition, the scheduler is APA work conserving, ... *)
+    Theorem scheduler_apa_work_conserving:
+      apa_work_conserving job_cost job_task sched alpha.
+    Proof.
+      intros j t BACK cpu CAN.
+      set l := (sorted_pending_jobs job_cost num_cpus arr_seq higher_eq_priority sched t).
+      have SORT : sorted (higher_eq_priority t) l  by apply sort_sorted.
+      have UNIQ: uniq l by rewrite sort_uniq filter_uniq // JobIn_uniq //.
+      move: BACK => /andP [PENDING NOTSCHED].
+      have IN: j \in l.
+      {
+        rewrite mem_sort mem_filter PENDING andTb JobIn_has_arrived.
+        by move: PENDING => /andP [H _].
+      }
+      have WORK := scheduler_mapping_is_work_conserving j cpu t l IN SORT UNIQ.
+      exploit WORK; try by done.
+      {
+        rewrite negb_exists in NOTSCHED; move: NOTSCHED => /forallP NOTSCHED.
+        by intros cpu0; specialize (NOTSCHED cpu0); rewrite -scheduler_scheduled_on.
+      }
+      by move => [j_other IN']; exists j_other; rewrite scheduler_scheduled_on.
+    Qed.  
+
+    (* ..., respects affinities, ... *)
+    Theorem scheduler_respects_affinity:
+      respects_affinity job_task sched alpha.
+    Proof.
+      unfold respects_affinity; intros j cpu t SCHED.
+      apply scheduler_mapping_respects_affinity with (t := t).
+      by rewrite -scheduler_scheduled_on.
+    Qed.
+    
+    (* ... and enforces the JLDP policy under weak APA scheduling. *)
+    Theorem scheduler_enforces_policy :
+      enforces_JLDP_policy_under_weak_APA job_cost job_task sched alpha higher_eq_priority.
+    Proof.
+      unfold enforces_JLDP_policy_under_weak_APA.
+      intros j j_hp cpu t BACK SCHED ALPHA.
+      rewrite scheduler_scheduled_on in SCHED.
+      apply scheduler_priority with (cpu := cpu); try by done.
+      by rewrite scheduler_scheduled_on.
+    Qed.
+
+  End Proofs.
+    
+End ConcreteScheduler.
\ No newline at end of file
diff --git a/implementation/apa/task.v b/implementation/apa/task.v
new file mode 100644
index 0000000000000000000000000000000000000000..6281667a82afba5ce8fc63344a14a821cdf349eb
--- /dev/null
+++ b/implementation/apa/task.v
@@ -0,0 +1,76 @@
+Require Import rt.model.apa.time rt.util.all.
+Require Import rt.model.apa.task rt.model.apa.affinity.
+From mathcomp Require Import ssreflect ssrbool ssrnat eqtype seq.
+
+Module ConcreteTask.
+
+  Import Time SporadicTaskset Affinity.
+  
+  Section Defs.
+
+    (* Let num_cpus be the number of processors. *)
+    Context {num_cpus: nat}.
+    
+    (* Definition of a concrete task. *)
+    Record concrete_task :=
+    {
+      task_id: nat; (* for uniqueness *)  
+      task_cost: time;
+      task_period: time;
+      task_deadline: time;
+      task_affinity: affinity num_cpus
+    }.
+
+    (* To make it compatible with ssreflect, we define a decidable
+       equality for concrete tasks. *)
+    Definition task_eqdef (t1 t2: concrete_task) :=
+      (task_id t1  == task_id t2) &&
+      (task_cost t1 == task_cost t2) &&
+      (task_period t1 == task_period t2) &&
+      (task_deadline t1 == task_deadline t2) &&
+      (task_affinity t1 == task_affinity t2).
+
+    (* Next, we prove that task_eqdef is indeed an equality, ... *)
+    Lemma eqn_task : Equality.axiom task_eqdef.
+    Proof.
+      unfold Equality.axiom; intros x y.
+      destruct (task_eqdef x y) eqn:EQ.
+      {
+        apply ReflectT.
+        unfold task_eqdef in *.
+        move: EQ => /andP [/andP [/andP [/andP [/eqP ID /eqP COST]] /eqP PERIOD] /eqP DL] /eqP ALPHA.
+        by destruct x, y; simpl in *; subst. 
+      }
+      {
+        apply ReflectF.
+        unfold task_eqdef, not in *; intro BUG.
+        apply negbT in EQ; rewrite negb_and in EQ.
+        destruct x, y.
+        rewrite negb_and in EQ.
+        move: EQ => /orP [EQ | /eqP DL]; last by apply DL; inversion BUG.
+        move: EQ => /orP [EQ | /eqP PERIOD]; last by apply PERIOD; inversion BUG.
+        rewrite negb_and in EQ.
+        move: EQ => /orP [EQ | /eqP COST]; last by apply COST; inversion BUG.
+        rewrite negb_and in EQ.
+        move: EQ => /orP [/eqP ID | /eqP ALPHA]; last by apply ALPHA; inversion BUG.
+        by apply ID; inversion BUG.
+      }
+    Qed.
+
+    (* ..., which allows instantiating the canonical structure. *)
+    Canonical concrete_task_eqMixin := EqMixin eqn_task.
+    Canonical concrete_task_eqType := Eval hnf in EqType concrete_task concrete_task_eqMixin.
+
+  End Defs.
+
+  Section ConcreteTaskset.
+
+    (* Let num_cpus be the number of processors. *)
+    Variable num_cpus: nat.
+    
+    Definition concrete_taskset :=
+      taskset_of (@concrete_task_eqType num_cpus).
+
+  End ConcreteTaskset.
+  
+End ConcreteTask.
\ No newline at end of file
diff --git a/implementation/basic/arrival_sequence.v b/implementation/basic/arrival_sequence.v
index 96089dc1546e8460cd92de5ebd0e643b98c7ec1c..17920ae1ecc32b11b02c920a4af618c5db48159c 100644
--- a/implementation/basic/arrival_sequence.v
+++ b/implementation/basic/arrival_sequence.v
@@ -95,14 +95,13 @@ Module ConcreteArrivalSequence.
       }
     Qed.
 
-    (* ... and if the task set has no duplicates, the same applies to
-       the arrival sequence. *)
+    (* ... and the arrival sequence has no duplicate jobs. *)
     Theorem periodic_arrivals_is_a_set:
-      uniq ts -> arrival_sequence_is_a_set arr_seq.
+      arrival_sequence_is_a_set arr_seq.
     Proof.
-      intros UNIQ t.
+      intros t.
       unfold arr_seq, periodic_arrival_sequence.
-      apply (pmap_uniq) with (g := job_task); last by done.
+      apply (pmap_uniq) with (g := job_task); last by destruct ts.
       by unfold add_job, ocancel; intro tsk; desf.
     Qed.
       
diff --git a/implementation/basic/bertogna_edf_example.v b/implementation/basic/bertogna_edf_example.v
index 661197da575fd0778e82987f936aa91761d16bac..dfd5e8f997dc692e3435e86cf05d931fa21caa32 100644
--- a/implementation/basic/bertogna_edf_example.v
+++ b/implementation/basic/bertogna_edf_example.v
@@ -23,15 +23,10 @@ Module ResponseTimeAnalysisEDF.
     Let tsk3 := {| task_id := 3; task_cost := 3; task_period := 12; task_deadline := 11|}.
 
     (* Let ts be a task set containing these three tasks. *)
-    Let ts := [:: tsk1; tsk2; tsk3].
+    Program Let ts := Build_set [:: tsk1; tsk2; tsk3] _.
 
     Section FactsAboutTaskset.
 
-      Fact ts_is_a_set: uniq ts.
-      Proof.
-        by compute.
-      Qed.
-      
       Fact ts_has_valid_parameters:
         valid_sporadic_taskset task_cost task_period task_deadline ts.
       Proof.
@@ -109,10 +104,9 @@ Module ResponseTimeAnalysisEDF.
     Proof.
       intros tsk IN.
       have VALID := periodic_arrivals_valid_job_parameters ts ts_has_valid_parameters.
-      have EDFVALID := @edf_valid_policy _ arr_seq job_deadline.
-      unfold valid_JLDP_policy, valid_sporadic_job, valid_realtime_job in *; des.
+      have TSVALID := ts_has_valid_parameters.
+      unfold valid_sporadic_job, valid_realtime_job in *; des.
       apply taskset_schedulable_by_edf_rta with (task_cost := task_cost) (task_period := task_period) (task_deadline := task_deadline) (ts0 := ts).
-      - by apply ts_is_a_set.
       - by apply ts_has_valid_parameters. 
       - by apply ts_has_constrained_deadlines.
       - by apply periodic_arrivals_all_jobs_from_taskset.
@@ -125,7 +119,9 @@ Module ResponseTimeAnalysisEDF.
         -- by apply periodic_arrivals_is_a_set.
       - by apply scheduler_sequential_jobs, periodic_arrivals_is_a_set.
       - by apply scheduler_work_conserving.
-      - by apply scheduler_enforces_policy; ins.
+      - apply scheduler_enforces_policy.
+        -- by apply EDF_is_transitive.
+        -- by apply EDF_is_total.
       - by apply schedulability_test_succeeds.
       - by apply IN.
     Qed.
diff --git a/implementation/basic/bertogna_fp_example.v b/implementation/basic/bertogna_fp_example.v
new file mode 100644
index 0000000000000000000000000000000000000000..e6f766bb79d318a4869a61bf3a2e0d6f073ff1d3
--- /dev/null
+++ b/implementation/basic/bertogna_fp_example.v
@@ -0,0 +1,175 @@
+Require Import rt.util.all.
+Require Import rt.model.basic.job rt.model.basic.task
+               rt.model.basic.schedule rt.model.basic.schedulability
+               rt.model.basic.priority rt.model.basic.platform.
+Require Import rt.analysis.basic.workload_bound
+               rt.analysis.basic.interference_bound_fp
+               rt.analysis.basic.bertogna_fp_comp.
+Require Import rt.implementation.basic.job
+               rt.implementation.basic.task
+               rt.implementation.basic.schedule
+               rt.implementation.basic.arrival_sequence.
+From mathcomp Require Import ssreflect ssrbool ssrnat eqtype seq bigop div.
+
+Module ResponseTimeAnalysisFP.
+
+  Import Job Schedule SporadicTaskset Priority Schedulability Platform InterferenceBoundFP WorkloadBound ResponseTimeIterationFP.
+  Import ConcreteJob ConcreteTask ConcreteArrivalSequence ConcreteScheduler.
+
+  (* In this section, we instantiate a simple example to show that the theorems
+     contain no contradictory assumptions. *)
+  Section ExampleRTA.
+
+    Let tsk1 := {| task_id := 1; task_cost := 2; task_period := 5; task_deadline := 3|}.
+    Let tsk2 := {| task_id := 2; task_cost := 4; task_period := 6; task_deadline := 5|}.
+    Let tsk3 := {| task_id := 3; task_cost := 3; task_period := 12; task_deadline := 11|}.
+
+    (* Let ts be a task set containing these three tasks (sorted by rate-monotonic priority). *)
+    Program Let ts := Build_set [:: tsk1; tsk2; tsk3] _.
+
+    Section FactsAboutTaskset.
+
+      Fact ts_has_valid_parameters:
+        valid_sporadic_taskset task_cost task_period task_deadline ts.
+      Proof.
+        intros tsk IN.
+        repeat (move: IN => /orP [/eqP EQ | IN]; subst; compute); by done.
+      Qed.
+
+      Fact ts_has_constrained_deadlines:
+        forall tsk,
+          tsk \in ts ->
+          task_deadline tsk <= task_period tsk.
+      Proof.
+        intros tsk IN.
+        repeat (move: IN => /orP [/eqP EQ | IN]; subst; compute); by done.
+      Qed.
+      
+    End FactsAboutTaskset.
+
+    (* Assume there are two processors. *)
+    Let num_cpus := 2.
+
+    (* Recall the FP RTA schedulability test. *)
+    Let schedulability_test :=
+      fp_schedulable task_cost task_period task_deadline num_cpus.
+
+    (* Now we show that the schedulability test returns true. *)
+    Fact schedulability_test_succeeds :
+      schedulability_test ts = true.
+    Proof.
+      unfold schedulability_test, fp_schedulable, fp_claimed_bounds; simpl.
+      unfold total_interference_bound_fp, div_floor.
+      rewrite big_nil div0n addn0 /=.
+      unfold div_floor; simpl.
+      set I2 := total_interference_bound_fp task_cost task_period tsk2
+                                            [:: (tsk1, 2)].
+      assert (H1: I2 4 = 1).
+      {
+        by unfold I2, total_interference_bound_fp; rewrite big_cons big_nil; compute.
+      }
+      rewrite H1.
+      have H2: 4 + 1 %/ num_cpus = 4 by compute.
+      rewrite H2 H1 H2.
+      have H3: 3 < 5 by compute.
+      rewrite H3.
+      unfold fp_bound_of_task.
+      have H4: per_task_rta task_cost task_period num_cpus tsk3 [:: (tsk1, 2); (tsk2, 4)]
+                            (max_steps task_cost task_deadline tsk3) = 5.
+      {
+        unfold per_task_rta; simpl.
+        unfold total_interference_bound_fp at 9.
+        rewrite !big_cons big_nil /=.
+        unfold total_interference_bound_fp at 8.
+        rewrite !big_cons big_nil /=.
+        unfold total_interference_bound_fp at 7.
+        rewrite !big_cons big_nil /=.
+        unfold total_interference_bound_fp at 6.
+        rewrite !big_cons big_nil /=.
+        unfold total_interference_bound_fp at 5.
+        rewrite !big_cons big_nil /=.
+        unfold total_interference_bound_fp at 4.
+        rewrite !big_cons big_nil /=.
+        unfold total_interference_bound_fp at 3.
+        rewrite !big_cons big_nil /=.
+        unfold total_interference_bound_fp at 2.
+        rewrite !big_cons big_nil /=.
+        unfold total_interference_bound_fp at 1.
+        by rewrite !big_cons big_nil /=; compute.
+      }
+      by rewrite H4.
+    Qed.
+
+    (* Let arr_seq be the periodic arrival sequence from ts. *)
+    Let arr_seq := periodic_arrival_sequence ts.
+
+    (* Assume rate-monotonic priorities. *)
+    Let higher_priority : JLDP_policy arr_seq :=
+      (FP_to_JLDP job_task (RM task_period)).
+
+    Section FactsAboutPriorityOrder.
+
+      Lemma ts_has_unique_priorities :
+        FP_is_antisymmetric_over_task_set (RM task_period) ts.
+      Proof.
+        unfold RM; intros tsk tsk' IN IN' HP HP'.
+        have EQ: task_period tsk = task_period tsk' by apply/eqP; rewrite eqn_leq HP HP'.
+        clear HP HP'.
+        rewrite !in_cons 2!in_nil 2!orbF in IN IN'; des; rewrite IN IN'; try (by done);
+        subst tsk tsk'; simpl in *; by done.
+      Qed.
+
+      Lemma priority_is_total :
+        FP_is_total_over_task_set (RM task_period) ts.
+      Proof.
+        unfold RM; intros tsk tsk' IN IN'.
+        destruct (leqP (task_period tsk) (task_period tsk'));
+          [by left | by right; apply ltnW].
+      Qed.
+      
+    End FactsAboutPriorityOrder.
+      
+    (* Let sched be the work-conserving RM scheduler. *)
+    Let sched := scheduler job_cost num_cpus arr_seq higher_priority.
+
+    (* Recall the definition of deadline miss. *)
+    Let no_deadline_missed_by :=
+      task_misses_no_deadline job_cost job_deadline job_task sched.
+
+    (* Next, we prove that ts is schedulable with the result of the test. *)
+    Corollary ts_is_schedulable:
+      forall tsk,
+        tsk \in ts ->
+        no_deadline_missed_by tsk.
+    Proof.
+      intros tsk IN.
+      have VALID := periodic_arrivals_valid_job_parameters ts ts_has_valid_parameters.
+      have TSVALID := ts_has_valid_parameters.
+      unfold valid_sporadic_job, valid_realtime_job in *; des.
+      apply taskset_schedulable_by_fp_rta with (task_cost := task_cost)
+       (task_period := task_period) (task_deadline := task_deadline)
+       (ts0 := ts) (higher_priority0 := RM task_period); try (by done).
+      - by apply ts_has_constrained_deadlines.
+      - by apply ts_has_unique_priorities.
+      - by apply priority_is_total.
+      - by apply RM_is_transitive.
+      - by apply periodic_arrivals_all_jobs_from_taskset.
+      - by apply periodic_arrivals_are_sporadic.
+      - by apply scheduler_jobs_must_arrive_to_execute.
+      - apply scheduler_completed_jobs_dont_execute; intro j'.
+        -- by specialize (VALID j'); des.
+        -- by apply periodic_arrivals_is_a_set.
+      - by apply scheduler_sequential_jobs, periodic_arrivals_is_a_set.
+      - by apply scheduler_work_conserving.
+      - apply scheduler_enforces_policy.
+        -- by intros t; apply RM_is_transitive.
+        {
+          unfold FP_to_JLDP; intros t x y; apply/orP.
+          by apply priority_is_total; rewrite periodic_arrivals_all_jobs_from_taskset.
+        }
+      - by apply schedulability_test_succeeds.
+    Qed.
+
+  End ExampleRTA.
+
+End ResponseTimeAnalysisFP.
\ No newline at end of file
diff --git a/implementation/basic/schedule.v b/implementation/basic/schedule.v
index c4ba99cc8467fe63b174ce1a5cb21710dbf71415..99adb9705ae30763e257aa572e2edc0812d9fd09 100644
--- a/implementation/basic/schedule.v
+++ b/implementation/basic/schedule.v
@@ -84,13 +84,15 @@ Module ConcreteScheduler.
 
     (* Next, we provide some helper lemmas about the scheduler construction. *)
     Section HelperLemmas.
+
+      (* Let's use a shorter name for the schedule prefix function. *)
+      Let sched_prefix := schedule_prefix job_cost num_cpus arr_seq higher_eq_priority.
       
       (* First, we show that the scheduler preserves its prefixes. *)
       Lemma scheduler_same_prefix :
         forall t t_max cpu,
           t <= t_max ->
-          schedule_prefix job_cost num_cpus arr_seq higher_eq_priority t_max cpu t =
-          scheduler job_cost num_cpus arr_seq higher_eq_priority cpu t.
+          sched_prefix t_max cpu t = sched cpu t.
       Proof.
         intros t t_max cpu LEt.
         induction t_max.
@@ -102,7 +104,7 @@ Module ConcreteScheduler.
           move: LEt => /orP [/eqP EQ | LESS]; first by subst.
           {
             feed IHt_max; first by done.
-            unfold schedule_prefix, update_schedule at 1.
+            unfold sched_prefix, schedule_prefix, update_schedule at 1.
             assert (FALSE: t == t_max.+1 = false).
             {
               by apply negbTE; rewrite neq_ltn LESS orTb.
@@ -144,7 +146,9 @@ Module ConcreteScheduler.
           apply eq_big_nat; move => t0 /andP [_ LT].
           unfold service_at; apply eq_bigl; red; intros cpu'.
           fold (schedule_prefix job_cost num_cpus arr_seq higher_eq_priority).
-          by rewrite /scheduled_on 2?scheduler_same_prefix ?leqnn //.
+          have SAME := scheduler_same_prefix; unfold sched_prefix, sched in *.
+          rewrite /scheduled_on; f_equal; unfold schedule_prefix.
+          by rewrite SAME // ?leqnn.
         }
       Qed.
       
@@ -168,6 +172,7 @@ Module ConcreteScheduler.
           exists i,
             nth_or_none (sorted_jobs t) i = Some j /\ i >= num_cpus.
       Proof.
+        have SAME := scheduler_same_prefix.
         intros j t BACK.
         move: BACK => /andP [PENDING /negP NOTCOMP].
         assert (IN: j \in sorted_jobs t).
@@ -205,7 +210,8 @@ Module ConcreteScheduler.
           unfold service_at; apply eq_bigl; red; intro cpu.
           unfold scheduled_on; f_equal.
           fold (schedule_prefix job_cost num_cpus arr_seq higher_eq_priority).
-          by rewrite scheduler_same_prefix.
+          unfold sched_prefix in *.
+          by rewrite /schedule_prefix SAME.
         }
       Qed.
 
diff --git a/implementation/jitter/arrival_sequence.v b/implementation/jitter/arrival_sequence.v
index 4a66266ea2b77ddf4a2239e775cacb0357091761..b8b7f4e3554cd4cccdeef2841f46f1d33f6fdaa2 100644
--- a/implementation/jitter/arrival_sequence.v
+++ b/implementation/jitter/arrival_sequence.v
@@ -95,14 +95,13 @@ Module ConcreteArrivalSequence.
       }
     Qed.
 
-    (* ... and if the task set has no duplicates, the same applies to
-       the arrival sequence. *)
+    (* ... and the arrival sequence has no duplicate jobs. *)
     Theorem periodic_arrivals_is_a_set:
-      uniq ts -> arrival_sequence_is_a_set arr_seq.
+      arrival_sequence_is_a_set arr_seq.
     Proof.
-      intros UNIQ t.
+      intros t.
       unfold arr_seq, periodic_arrival_sequence.
-      apply (pmap_uniq) with (g := job_task); last by done.
+      apply (pmap_uniq) with (g := job_task); last by destruct ts.
       by unfold add_job, ocancel; intro tsk; desf.
     Qed.
       
diff --git a/implementation/jitter/bertogna_edf_example.v b/implementation/jitter/bertogna_edf_example.v
index 6cfdd6ac69d61c8a4135037adcf1ab2aa7dc8b14..4763d22d9d62250964e0b04dfd872600b2decdb8 100644
--- a/implementation/jitter/bertogna_edf_example.v
+++ b/implementation/jitter/bertogna_edf_example.v
@@ -23,15 +23,10 @@ Module ResponseTimeAnalysisEDF.
     Let tsk3 := {| task_id := 3; task_cost := 2; task_period := 12; task_deadline := 11; task_jitter := 2|}.
 
     (* Let ts be a task set containing these three tasks. *)
-    Let ts := [:: tsk1; tsk2; tsk3].
+    Program Let ts := Build_set [:: tsk1; tsk2; tsk3] _.
 
     Section FactsAboutTaskset.
 
-      Fact ts_is_a_set: uniq ts.
-      Proof.
-        by compute.
-      Qed.
-      
       Fact ts_has_valid_parameters:
         valid_sporadic_taskset task_cost task_period task_deadline ts.
       Proof.
@@ -110,11 +105,10 @@ Module ResponseTimeAnalysisEDF.
     Proof.
       intros tsk IN.
       have VALID := periodic_arrivals_valid_job_parameters ts ts_has_valid_parameters.
-      have EDFVALID := @edf_valid_policy _ arr_seq job_deadline.
-      unfold valid_JLDP_policy, valid_sporadic_job_with_jitter,
+      have VALIDTS := ts_has_valid_parameters.
+      unfold valid_sporadic_job_with_jitter,
              valid_sporadic_job, valid_realtime_job in *; des.
       apply taskset_schedulable_by_edf_rta with (task_cost := task_cost) (task_period := task_period) (task_deadline := task_deadline) (ts0 := ts) (task_jitter := task_jitter) (job_jitter := job_jitter).
-      - by apply ts_is_a_set.
       - by apply ts_has_valid_parameters. 
       - by apply ts_has_constrained_deadlines.
       - by apply periodic_arrivals_all_jobs_from_taskset.
@@ -127,7 +121,9 @@ Module ResponseTimeAnalysisEDF.
         -- by apply periodic_arrivals_is_a_set.
       - by apply scheduler_sequential_jobs, periodic_arrivals_is_a_set.
       - by apply scheduler_work_conserving.
-      - by apply scheduler_enforces_policy; ins.
+      - apply scheduler_enforces_policy.
+        -- by apply EDF_is_transitive.
+        -- by apply EDF_is_total.
       - by apply schedulability_test_succeeds.
       - by apply IN.
     Qed.
diff --git a/implementation/jitter/bertogna_fp_example.v b/implementation/jitter/bertogna_fp_example.v
new file mode 100644
index 0000000000000000000000000000000000000000..da15c4d31850879ac7f42a42dd3f6f275443b807
--- /dev/null
+++ b/implementation/jitter/bertogna_fp_example.v
@@ -0,0 +1,177 @@
+Require Import rt.util.all.
+Require Import rt.model.jitter.job rt.model.jitter.task
+               rt.model.jitter.schedule rt.model.jitter.schedulability
+               rt.model.jitter.priority rt.model.jitter.platform.
+Require Import rt.analysis.jitter.workload_bound
+               rt.analysis.jitter.interference_bound_fp
+               rt.analysis.jitter.bertogna_fp_comp.
+Require Import rt.implementation.jitter.job
+               rt.implementation.jitter.task
+               rt.implementation.jitter.schedule
+               rt.implementation.jitter.arrival_sequence.
+From mathcomp Require Import ssreflect ssrbool ssrnat eqtype seq bigop div.
+
+Module ResponseTimeAnalysisFP.
+
+  Import JobWithJitter ScheduleWithJitter SporadicTaskset Priority Schedulability Platform InterferenceBoundFP WorkloadBoundJitter ResponseTimeIterationFP.
+  Import ConcreteJob ConcreteTask ConcreteArrivalSequence ConcreteScheduler.
+
+  (* In this section, we instantiate a simple example to show that the theorems
+     contain no contradictory assumptions. *)  
+  Section ExampleRTA.
+
+    Let tsk1 := {| task_id := 1; task_cost := 2; task_period := 5; task_deadline := 3; task_jitter := 0|}.
+    Let tsk2 := {| task_id := 2; task_cost := 4; task_period := 6; task_deadline := 5; task_jitter := 1|}.
+    Let tsk3 := {| task_id := 3; task_cost := 3; task_period := 12; task_deadline := 11; task_jitter := 2|}.
+
+    (* Let ts be a task set containing these three tasks (sorted by rate-monotonic priority). *)
+    Program Let ts := Build_set [:: tsk1; tsk2; tsk3] _.
+
+    Section FactsAboutTaskset.
+
+      Fact ts_has_valid_parameters:
+        valid_sporadic_taskset task_cost task_period task_deadline ts.
+      Proof.
+        intros tsk IN.
+        repeat (move: IN => /orP [/eqP EQ | IN]; subst; compute); by done.
+      Qed.
+
+      Fact ts_has_constrained_deadlines:
+        forall tsk,
+          tsk \in ts ->
+          task_deadline tsk <= task_period tsk.
+      Proof.
+        intros tsk IN.
+        repeat (move: IN => /orP [/eqP EQ | IN]; subst; compute); by done.
+      Qed.
+      
+    End FactsAboutTaskset.
+
+    (* Assume there are two processors. *)
+    Let num_cpus := 2.
+
+    (* Recall the FP RTA schedulability test. *)
+    Let schedulability_test :=
+      fp_schedulable task_cost task_period task_deadline task_jitter num_cpus.
+
+    (* Now we show that the schedulability test returns true. *)
+    Fact schedulability_test_succeeds :
+      schedulability_test ts = true.
+    Proof.
+      unfold schedulability_test, fp_schedulable, fp_claimed_bounds; simpl.
+      unfold total_interference_bound_fp, div_floor.
+      rewrite big_nil div0n addn0 /=.
+      unfold div_floor; simpl.
+      set I2 := total_interference_bound_fp task_cost task_period
+                                    task_jitter tsk2  [:: (tsk1, 2)].
+      assert (H1: I2 4 = 1).
+      {
+        by unfold I2, total_interference_bound_fp; rewrite big_cons big_nil; compute.
+      }
+      rewrite H1.
+      have H2: 4 + 1 %/ num_cpus = 4 by compute.
+      rewrite H2 H1 H2.
+      have H3: (1 + 4 <= 5) by compute.
+      rewrite H3.
+      unfold fp_bound_of_task.
+      have H4: per_task_rta task_cost task_period task_jitter num_cpus
+                 tsk3 [:: (tsk1, 2); (tsk2, 4)]
+                 (max_steps task_cost task_deadline tsk3) = 5.
+      {
+        unfold per_task_rta; simpl.
+        unfold total_interference_bound_fp at 9.
+        rewrite !big_cons big_nil /=.
+        unfold total_interference_bound_fp at 8.
+        rewrite !big_cons big_nil /=.
+        unfold total_interference_bound_fp at 7.
+        rewrite !big_cons big_nil /=.
+        unfold total_interference_bound_fp at 6.
+        rewrite !big_cons big_nil /=.
+        unfold total_interference_bound_fp at 5.
+        rewrite !big_cons big_nil /=.
+        unfold total_interference_bound_fp at 4.
+        rewrite !big_cons big_nil /=.
+        unfold total_interference_bound_fp at 3.
+        rewrite !big_cons big_nil /=.
+        unfold total_interference_bound_fp at 2.
+        rewrite !big_cons big_nil /=.
+        unfold total_interference_bound_fp at 1.
+        by rewrite !big_cons big_nil /=; compute.
+      }
+      by rewrite H4.
+    Qed.
+
+    (* Let arr_seq be the periodic arrival sequence from ts. *)
+    Let arr_seq := periodic_arrival_sequence ts.
+
+    (* Assume rate-monotonic priorities. *)
+    Let higher_priority : JLDP_policy arr_seq :=
+      (FP_to_JLDP job_task (RM task_period)).
+
+    Section FactsAboutPriorityOrder.
+
+      Lemma ts_has_unique_priorities :
+        FP_is_antisymmetric_over_task_set (RM task_period) ts.
+      Proof.
+        unfold RM; intros tsk tsk' IN IN' HP HP'.
+        have EQ: task_period tsk = task_period tsk' by apply/eqP; rewrite eqn_leq HP HP'.
+        clear HP HP'.
+        rewrite !in_cons 2!in_nil 2!orbF in IN IN'; des; rewrite IN IN'; try (by done);
+        subst tsk tsk'; simpl in *; by done.
+      Qed.
+
+      Lemma priority_is_total :
+        FP_is_total_over_task_set (RM task_period) ts.
+      Proof.
+        unfold RM; intros tsk tsk' IN IN'.
+        destruct (leqP (task_period tsk) (task_period tsk'));
+          [by left | by right; apply ltnW].
+      Qed.
+      
+    End FactsAboutPriorityOrder.
+      
+    (* Let sched be the work-conserving RM scheduler. *)
+    Let sched := scheduler job_cost job_jitter num_cpus arr_seq higher_priority.
+
+    (* Recall the definition of deadline miss. *)
+    Let no_deadline_missed_by :=
+      task_misses_no_deadline job_cost job_deadline job_task sched.
+
+    (* Next, we prove that ts is schedulable with the result of the test. *)
+    Corollary ts_is_schedulable:
+      forall tsk,
+        tsk \in ts ->
+        no_deadline_missed_by tsk.
+    Proof.
+      intros tsk IN.
+      have VALID := periodic_arrivals_valid_job_parameters ts ts_has_valid_parameters.
+      have TSVALID := ts_has_valid_parameters.
+      unfold valid_sporadic_job_with_jitter, valid_sporadic_job, valid_realtime_job in *; des.
+      apply taskset_schedulable_by_fp_rta with (task_cost := task_cost)
+        (task_period := task_period) (task_deadline := task_deadline)
+        (task_jitter := task_jitter) (job_jitter := job_jitter)
+        (ts0 := ts) (higher_priority0 := RM task_period); try (by done).
+      - by apply ts_has_constrained_deadlines.
+      - by apply ts_has_unique_priorities.
+      - by apply priority_is_total.
+      - by apply RM_is_transitive.
+      - by apply periodic_arrivals_all_jobs_from_taskset.
+      - by apply periodic_arrivals_are_sporadic.
+      - by apply scheduler_jobs_execute_after_jitter.
+      - apply scheduler_completed_jobs_dont_execute; intro j'.
+        -- by specialize (VALID j'); des.
+        -- by apply periodic_arrivals_is_a_set.
+      - by apply scheduler_sequential_jobs, periodic_arrivals_is_a_set.
+      - by apply scheduler_work_conserving.
+      - apply scheduler_enforces_policy.
+        -- by intros t; apply RM_is_transitive.
+        {
+          unfold FP_to_JLDP; intros t x y; apply/orP.
+          by apply priority_is_total; rewrite periodic_arrivals_all_jobs_from_taskset.
+        }
+      - by apply schedulability_test_succeeds.
+    Qed.
+
+  End ExampleRTA.
+
+End ResponseTimeAnalysisFP.
\ No newline at end of file
diff --git a/implementation/jitter/schedule.v b/implementation/jitter/schedule.v
index d4fc05aa6b11a9d22f76aa0edcd485ed6265cbbc..58268053bcb3214be63db02c1fed4f24b1f7f721 100644
--- a/implementation/jitter/schedule.v
+++ b/implementation/jitter/schedule.v
@@ -86,13 +86,15 @@ Module ConcreteScheduler.
 
     (* Next, we provide some helper lemmas about the scheduler construction. *)
     Section HelperLemmas.
+
+      (* Let's use a shorter name for the schedule prefix function. *)
+      Let sched_prefix := schedule_prefix job_cost job_jitter num_cpus arr_seq higher_eq_priority.
       
       (* First, we show that the scheduler preserves its prefixes. *)
       Lemma scheduler_same_prefix :
         forall t t_max cpu,
           t <= t_max ->
-          schedule_prefix job_cost job_jitter num_cpus arr_seq higher_eq_priority t_max cpu t =
-          scheduler job_cost job_jitter num_cpus arr_seq higher_eq_priority cpu t.
+          sched_prefix t_max cpu t = sched cpu t.
       Proof.
         intros t t_max cpu LEt.
         induction t_max.
@@ -104,7 +106,7 @@ Module ConcreteScheduler.
           move: LEt => /orP [/eqP EQ | LESS]; first by subst.
           {
             feed IHt_max; first by done.
-            unfold schedule_prefix, update_schedule at 1.
+            unfold sched_prefix, schedule_prefix, update_schedule at 1.
             assert (FALSE: t == t_max.+1 = false).
             {
               by apply negbTE; rewrite neq_ltn LESS orTb.
@@ -147,7 +149,8 @@ Module ConcreteScheduler.
           unfold service_at; apply eq_bigl; red; intros cpu'.
           unfold scheduled_on; f_equal.
           fold (schedule_prefix job_cost job_jitter num_cpus arr_seq higher_eq_priority).
-          by rewrite 2?scheduler_same_prefix ?leqnn //.
+          have SAME := scheduler_same_prefix; unfold sched_prefix, sched in *.
+          by rewrite /schedule_prefix SAME.
         }
       Qed.
       
@@ -170,6 +173,7 @@ Module ConcreteScheduler.
           exists i,
             nth_or_none (sorted_jobs t) i = Some j /\ i >= num_cpus.
       Proof.
+        have SAME := scheduler_same_prefix.
         intros j t BACK.
         move: BACK => /andP [PENDING /negP NOTCOMP].
         assert (IN: j \in sorted_jobs t).
@@ -209,7 +213,7 @@ Module ConcreteScheduler.
           unfold service_at; apply eq_bigl; red; intro cpu.
           unfold scheduled_on; f_equal.
           fold (schedule_prefix job_cost job_jitter num_cpus arr_seq higher_eq_priority).
-          by rewrite scheduler_same_prefix //.
+          by unfold sched_prefix in *; rewrite /schedule_prefix SAME.
         }
       Qed.
 
diff --git a/implementation/parallel/arrival_sequence.v b/implementation/parallel/arrival_sequence.v
new file mode 100644
index 0000000000000000000000000000000000000000..4f6f2213a1a2cd21ca461cb2a122425adea7f1a3
--- /dev/null
+++ b/implementation/parallel/arrival_sequence.v
@@ -0,0 +1,2 @@
+(* We reuse the arrival sequence from the basic model. *)
+Require Export rt.implementation.basic.arrival_sequence.
\ No newline at end of file
diff --git a/implementation/parallel/bertogna_edf_example.v b/implementation/parallel/bertogna_edf_example.v
new file mode 100644
index 0000000000000000000000000000000000000000..d2398d72dfb56eee18c09e904ca63322e1567b50
--- /dev/null
+++ b/implementation/parallel/bertogna_edf_example.v
@@ -0,0 +1,137 @@
+Require Import rt.util.all.
+Require Import rt.model.basic.job rt.model.basic.task
+               rt.model.basic.schedule rt.model.basic.schedulability
+               rt.model.basic.priority rt.model.basic.platform.
+Require Import rt.analysis.parallel.workload_bound
+               rt.analysis.parallel.interference_bound_edf
+               rt.analysis.parallel.bertogna_edf_comp.
+Require Import rt.implementation.parallel.job
+               rt.implementation.parallel.task
+               rt.implementation.parallel.schedule
+               rt.implementation.parallel.arrival_sequence.
+From mathcomp Require Import ssreflect ssrbool ssrnat eqtype seq bigop div.
+
+Module ResponseTimeAnalysisEDF.
+
+  Import Job Schedule SporadicTaskset Priority Schedulability Platform InterferenceBoundEDF WorkloadBound ResponseTimeIterationEDF.
+  Import ConcreteJob ConcreteTask ConcreteArrivalSequence ConcreteScheduler.
+
+  Section ExampleRTA.
+
+    Let tsk1 := {| task_id := 1; task_cost := 2; task_period := 6; task_deadline := 6|}.
+    Let tsk2 := {| task_id := 2; task_cost := 3; task_period := 8; task_deadline := 6|}.
+    Let tsk3 := {| task_id := 3; task_cost := 2; task_period := 12; task_deadline := 12|}.
+
+    (* Let ts be a task set containing these three tasks. *)
+    Program Let ts := Build_set [:: tsk1; tsk2; tsk3] _.
+
+    Section FactsAboutTaskset.
+
+      Fact ts_has_valid_parameters:
+        valid_sporadic_taskset task_cost task_period task_deadline ts.
+      Proof.
+        intros tsk IN.
+        repeat (move: IN => /orP [/eqP EQ | IN]; subst; compute); by done.
+      Qed.
+
+      Fact ts_has_constrained_deadlines:
+        forall tsk,
+          tsk \in ts ->
+          task_deadline tsk <= task_period tsk.
+      Proof.
+        intros tsk IN.
+        repeat (move: IN => /orP [/eqP EQ | IN]; subst; compute); by done.
+      Qed.
+      
+    End FactsAboutTaskset.
+
+    (* Assume there are two processors. *)
+    Let num_cpus := 2.
+
+    (* Recall the EDF RTA schedulability test. *)
+    Let schedulability_test :=
+      edf_schedulable task_cost task_period task_deadline num_cpus.
+
+    Fact schedulability_test_succeeds :
+      schedulability_test ts = true.
+    Proof.
+      unfold schedulability_test, edf_schedulable, edf_claimed_bounds; desf.
+      apply negbT in Heq; move: Heq => /negP ALL.
+      exfalso; apply ALL; clear ALL.
+      assert (STEPS: \sum_(tsk <- ts) (task_deadline tsk - task_cost tsk) + 1 = 18).
+      {
+        by rewrite unlock; compute.
+      } rewrite STEPS; clear STEPS.
+      
+      Ltac f :=
+        unfold edf_rta_iteration; simpl;
+        unfold edf_response_time_bound, div_floor, total_interference_bound_edf, interference_bound_edf, interference_bound_generic, W; simpl;
+        repeat rewrite addnE;
+        repeat rewrite big_cons; repeat rewrite big_nil;
+        repeat rewrite addnE; simpl;
+        unfold num_cpus, divn; simpl.
+
+      rewrite [edf_rta_iteration]lock; simpl.
+
+      unfold locked at 18; destruct master_key; f.
+      unfold locked at 17; destruct master_key; f.
+      unfold locked at 16; destruct master_key; f.
+      unfold locked at 15; destruct master_key; f.
+      unfold locked at 14; destruct master_key; f.
+      unfold locked at 13; destruct master_key; f.
+      unfold locked at 12; destruct master_key; f.
+      unfold locked at 11; destruct master_key; f.
+      unfold locked at 10; destruct master_key; f.
+      unfold locked at 9; destruct master_key; f.
+      unfold locked at 8; destruct master_key; f.
+      unfold locked at 7; destruct master_key; f.
+      unfold locked at 6; destruct master_key; f.
+      unfold locked at 5; destruct master_key; f.
+      unfold locked at 4; destruct master_key; f.
+      unfold locked at 3; destruct master_key; f.
+      unfold locked at 2; destruct master_key; f.
+      by unfold locked at 1; destruct master_key; f.
+    Qed.
+
+    (* Let arr_seq be the periodic arrival sequence from ts. *)
+    Let arr_seq := periodic_arrival_sequence ts.
+
+    (* Let sched be the work-conserving EDF scheduler. *)
+    Let sched := scheduler job_cost num_cpus arr_seq (EDF job_deadline).
+
+    (* Recall the definition of deadline miss. *)
+    Let no_deadline_missed_by :=
+      task_misses_no_deadline job_cost job_deadline job_task sched.
+
+    (* Next, we prove that ts is schedulable with the result of the test. *)
+    Corollary ts_is_schedulable:
+      forall tsk,
+        tsk \in ts ->
+        no_deadline_missed_by tsk.
+    Proof.
+      intros tsk IN.
+      have VALID := periodic_arrivals_valid_job_parameters ts ts_has_valid_parameters.
+      have TSVALID := ts_has_valid_parameters.
+      unfold valid_sporadic_job, valid_realtime_job in *; des.
+      apply taskset_schedulable_by_edf_rta with (task_cost := task_cost) (task_period := task_period) (task_deadline := task_deadline) (ts0 := ts).
+      - by apply ts_has_valid_parameters. 
+      - by apply ts_has_constrained_deadlines.
+      - by apply periodic_arrivals_all_jobs_from_taskset.
+      - by apply periodic_arrivals_valid_job_parameters, ts_has_valid_parameters.
+      - by apply periodic_arrivals_are_sporadic.
+      - by compute.
+      - by apply scheduler_jobs_must_arrive_to_execute.
+      - apply scheduler_completed_jobs_dont_execute; intro j'.
+        -- by specialize (VALID j'); des.
+        -- by apply periodic_arrivals_is_a_set.
+      - by apply scheduler_work_conserving.
+      - apply scheduler_enforces_policy.
+        -- by apply EDF_is_transitive.
+        -- by apply EDF_is_total.
+      - by apply schedulability_test_succeeds.
+      - by apply IN.
+    Qed.
+
+  End ExampleRTA.
+
+End ResponseTimeAnalysisEDF.
\ No newline at end of file
diff --git a/implementation/parallel/bertogna_fp_example.v b/implementation/parallel/bertogna_fp_example.v
new file mode 100644
index 0000000000000000000000000000000000000000..aef7d69a421cfa65b501ffb7ef62d2de9e15e17a
--- /dev/null
+++ b/implementation/parallel/bertogna_fp_example.v
@@ -0,0 +1,196 @@
+Require Import rt.util.all.
+Require Import rt.model.basic.job rt.model.basic.task
+               rt.model.basic.schedule rt.model.basic.schedulability
+               rt.model.basic.priority rt.model.basic.platform
+               rt.model.basic.interference.
+Require Import rt.analysis.parallel.workload_bound
+               rt.analysis.parallel.interference_bound_fp
+               rt.analysis.parallel.bertogna_fp_comp.
+Require Import rt.implementation.parallel.job
+               rt.implementation.parallel.task
+               rt.implementation.parallel.schedule
+               rt.implementation.parallel.arrival_sequence.
+From mathcomp Require Import ssreflect ssrbool ssrnat eqtype seq bigop div.
+
+Module ResponseTimeAnalysisFP.
+
+  Import Job Schedule SporadicTaskset Priority Schedulability Platform
+         Interference InterferenceBoundFP WorkloadBound
+         ResponseTimeIterationFP.
+  Import ConcreteJob ConcreteTask ConcreteArrivalSequence ConcreteScheduler.
+
+  (* In this section, we instantiate a simple example to show that the theorems
+     contain no contradictory assumptions. *)
+  Section ExampleRTA.
+
+    Let tsk1 := {| task_id := 1; task_cost := 2; task_period := 6; task_deadline := 6|}.
+    Let tsk2 := {| task_id := 2; task_cost := 3; task_period := 8; task_deadline := 6|}.
+    Let tsk3 := {| task_id := 3; task_cost := 2; task_period := 12; task_deadline := 12|}.
+
+    (* Let ts be a task set containing these three tasks (sorted by rate-monotonic priority). *)
+    Program Let ts := Build_set [:: tsk1; tsk2; tsk3] _.
+
+    Section FactsAboutTaskset.
+
+      Fact ts_has_valid_parameters:
+        valid_sporadic_taskset task_cost task_period task_deadline ts.
+      Proof.
+        intros tsk IN.
+        repeat (move: IN => /orP [/eqP EQ | IN]; subst; compute); by done.
+      Qed.
+
+      Fact ts_has_constrained_deadlines:
+        forall tsk,
+          tsk \in ts ->
+          task_deadline tsk <= task_period tsk.
+      Proof.
+        intros tsk IN.
+        repeat (move: IN => /orP [/eqP EQ | IN]; subst; compute); by done.
+      Qed.
+      
+    End FactsAboutTaskset.
+
+    (* Assume there are two processors. *)
+    Let num_cpus := 2.
+
+    (* Recall the FP RTA schedulability test. *)
+    Let schedulability_test :=
+      fp_schedulable task_cost task_period task_deadline num_cpus.
+
+    (* Now we show that the schedulability test returns true. *)
+    Fact schedulability_test_succeeds :
+      schedulability_test ts = true.
+    Proof.
+      unfold schedulability_test, fp_schedulable, fp_claimed_bounds; simpl.
+      unfold total_interference_bound_fp, div_floor.
+      rewrite big_nil div0n addn0 /=.
+      unfold div_floor; simpl.
+      set I2 := total_interference_bound_fp task_cost task_period [:: (tsk1, 2)].
+      assert (H1: I2 3 = 2).
+      {
+        by rewrite /I2 /total_interference_bound_fp big_cons big_nil; compute.
+      }
+      assert (H2: I2 4 = 2).
+      {
+        by rewrite /I2 /total_interference_bound_fp big_cons big_nil; compute.
+      }
+      rewrite H1 H2.
+      have H3: 3 + 2 %/ num_cpus = 4 by compute.
+      rewrite H3 H2 H3 H2 H3.
+      have H4: 3 < 6 by compute.
+      rewrite H4; clear H4 H2 H1 I2.
+      unfold fp_bound_of_task.
+      have H5: per_task_rta task_cost task_period num_cpus tsk3 [:: (tsk1, 2); (tsk2, 4)] (max_steps task_cost task_deadline tsk3) = 4.
+      {
+        Ltac g := rewrite /total_interference_bound_fp /interference_bound_generic
+          /W /max_jobs /div_floor /div_ceil !big_cons big_nil /= /higher_priority_task /= ?addn0; compute.
+        rewrite /per_task_rta iterSr /div_floor.
+        set x10 := total_interference_bound_fp  _ _ _ _.
+        have I10: x10 = 5 by unfold x10; g.
+        rewrite I10 iterSr.
+        set x9 := total_interference_bound_fp  _ _ _ _.
+        have I9: x9 = 5 by unfold x9; g.
+        rewrite I9 iterSr.
+        set x8 := total_interference_bound_fp  _ _ _ _.
+        have I8: x8 = 5 by unfold x8; g.
+        rewrite I8 iterSr.
+        set x7 := total_interference_bound_fp  _ _ _ _.
+        have I7: x7 = 5 by unfold x7; g.
+        rewrite I7 iterSr.
+        set x6 := total_interference_bound_fp  _ _ _ _.
+        have I6: x6 = 5 by unfold x6; g.
+        rewrite I6 iterSr.
+        set x5 := total_interference_bound_fp  _ _ _ _.
+        have I5: x5 = 5 by unfold x5; g.
+        rewrite I5 iterSr.
+        set x4 := total_interference_bound_fp  _ _ _ _.
+        have I4: x4 = 5 by unfold x4; g.
+        rewrite I4 iterSr.
+        set x3 := total_interference_bound_fp  _ _ _ _.
+        have I3: x3 = 5 by unfold x3; g.
+        rewrite I3 iterSr.
+        set x2 := total_interference_bound_fp  _ _ _ _.
+        have I2: x2 = 5 by unfold x2; g.
+        rewrite I2 iterSr.
+        set x1 := total_interference_bound_fp  _ _ _ _.
+        have I1: x1 = 5 by unfold x1; g.
+        rewrite I1 iterSr.
+        set x0 := total_interference_bound_fp  _ _ _ _.
+        have I0: x0 = 5 by unfold x0; g.
+        by rewrite /= I0; compute.
+      }
+      by rewrite H5.
+    Qed.
+
+    (* Let arr_seq be the periodic arrival sequence from ts. *)
+    Let arr_seq := periodic_arrival_sequence ts.
+
+    (* Assume rate-monotonic priorities. *)
+    Let higher_priority : JLDP_policy arr_seq :=
+      (FP_to_JLDP job_task (RM task_period)).
+
+    Section FactsAboutPriorityOrder.
+
+      Lemma ts_has_unique_priorities :
+        FP_is_antisymmetric_over_task_set (RM task_period) ts.
+      Proof.
+        unfold RM; intros tsk tsk' IN IN' HP HP'.
+        have EQ: task_period tsk = task_period tsk' by apply/eqP; rewrite eqn_leq HP HP'.
+        clear HP HP'.
+        rewrite !in_cons 2!in_nil 2!orbF in IN IN'; des; rewrite IN IN'; try (by done);
+        subst tsk tsk'; simpl in *; by done.
+      Qed.
+
+      Lemma priority_is_total :
+        FP_is_total_over_task_set (RM task_period) ts.
+      Proof.
+        unfold RM; intros tsk tsk' IN IN'.
+        destruct (leqP (task_period tsk) (task_period tsk'));
+          [by left | by right; apply ltnW].
+      Qed.
+      
+    End FactsAboutPriorityOrder.
+      
+    (* Let sched be the work-conserving RM scheduler. *)
+    Let sched := scheduler job_cost num_cpus arr_seq higher_priority.
+
+    (* Recall the definition of deadline miss. *)
+    Let no_deadline_missed_by :=
+      task_misses_no_deadline job_cost job_deadline job_task sched.
+
+    (* Next, we prove that ts is schedulable with the result of the test. *)
+    Corollary ts_is_schedulable:
+      forall tsk,
+        tsk \in ts ->
+        no_deadline_missed_by tsk.
+    Proof.
+      intros tsk IN.
+      have VALID := periodic_arrivals_valid_job_parameters ts ts_has_valid_parameters.
+      have TSVALID := ts_has_valid_parameters.
+      unfold valid_sporadic_job, valid_realtime_job in *; des.
+      apply taskset_schedulable_by_fp_rta with (task_cost := task_cost)
+       (task_period := task_period) (task_deadline := task_deadline)
+       (ts0 := ts) (higher_priority0 := RM task_period); try (by done).
+      - by apply ts_has_constrained_deadlines.
+      - by apply ts_has_unique_priorities.
+      - by apply priority_is_total.
+      - by apply RM_is_transitive.
+      - by apply periodic_arrivals_all_jobs_from_taskset.
+      - by apply periodic_arrivals_are_sporadic.
+      - by apply scheduler_jobs_must_arrive_to_execute.
+      - apply scheduler_completed_jobs_dont_execute; intro j'.
+        -- by specialize (VALID j'); des.
+        -- by apply periodic_arrivals_is_a_set.
+      - by apply scheduler_work_conserving.
+      - apply scheduler_enforces_policy.
+        -- by intros t; apply RM_is_transitive.
+        {
+          unfold FP_to_JLDP; intros t x y; apply/orP.
+          by apply priority_is_total; rewrite periodic_arrivals_all_jobs_from_taskset.
+        }
+      - by apply schedulability_test_succeeds.
+    Qed.
+
+  End ExampleRTA.
+
+End ResponseTimeAnalysisFP.
\ No newline at end of file
diff --git a/implementation/parallel/job.v b/implementation/parallel/job.v
new file mode 100644
index 0000000000000000000000000000000000000000..78b0bd655db83854403535830cdf828d6329ce74
--- /dev/null
+++ b/implementation/parallel/job.v
@@ -0,0 +1,2 @@
+(* We reuse the job from the basic model. *)
+Require Export rt.implementation.basic.job.
\ No newline at end of file
diff --git a/implementation/parallel/schedule.v b/implementation/parallel/schedule.v
new file mode 100644
index 0000000000000000000000000000000000000000..fdfac70394bc838fd58d19c473fd11d69a513052
--- /dev/null
+++ b/implementation/parallel/schedule.v
@@ -0,0 +1,4 @@
+(* For simplicity, we reuse the scheduler from the basic model.
+   Although it does not exploit parallelism, it is a restricted case
+   of a parallel scheduler. *)
+Require Export rt.implementation.basic.schedule.
\ No newline at end of file
diff --git a/implementation/parallel/task.v b/implementation/parallel/task.v
new file mode 100644
index 0000000000000000000000000000000000000000..d4eb8e27de636a10883885260ff913d298a0d176
--- /dev/null
+++ b/implementation/parallel/task.v
@@ -0,0 +1,2 @@
+(* We reuse the task from the basic model. *)
+Require Export rt.implementation.basic.task.
\ No newline at end of file
diff --git a/model/apa/affinity.v b/model/apa/affinity.v
new file mode 100644
index 0000000000000000000000000000000000000000..f819d86a54368335431d9f00c1c9df3e96a52568
--- /dev/null
+++ b/model/apa/affinity.v
@@ -0,0 +1,103 @@
+Require Import rt.util.all.
+Require Import rt.model.apa.job rt.model.apa.task
+               rt.model.apa.arrival_sequence rt.model.apa.schedule.
+From mathcomp Require Import ssreflect ssrbool eqtype ssrnat seq fintype bigop.
+
+(* Definition and properties about processor affinities. *)
+Module Affinity.
+
+  Import ArrivalSequence ScheduleOfSporadicTask.
+  
+  Section AffinityDefs.
+
+    Variable sporadic_task: eqType.
+
+    (* Given the number of processors, ... *)
+    Variable num_cpus: nat.
+      
+    (* ... let a processor affinity be a subset of these processors.
+       Note: notation {set T} is just a list of type T with no duplicates. *)
+    Definition affinity := {set (processor num_cpus)}.
+
+    (* Next, we define a task affinity as the mapping from tasks to affinities. *)
+    Definition task_affinity := sporadic_task -> affinity.
+    
+  End AffinityDefs.
+
+  Section Properties.
+
+    Context {sporadic_task: eqType}.    
+    Context {num_cpus: nat}.
+
+    Section JobProperties.
+
+      (* Assume that each task has a processor affinity alpha. *)
+      Variable alpha: task_affinity sporadic_task num_cpus.
+
+      (* Then, we say that task tsk ...*)
+      Variable tsk: sporadic_task.
+
+      (* ... can execute on processor cpu ...*)
+      Variable cpu: processor num_cpus.
+
+      (* ... iff cpu is part of j's affinity. *)
+      Definition can_execute_on := cpu \in alpha tsk.
+
+    End JobProperties.
+
+    Section ScheduleProperties.
+
+      Context {Job: eqType}.
+      Variable job_task: Job -> sporadic_task.
+      
+      (* Consider any schedule, ... *)
+      Context {arr_seq: arrival_sequence Job}.
+      Variable sched: schedule num_cpus arr_seq.
+
+      (* ... and some affinity alpha. *)
+      Variable alpha: affinity num_cpus.
+      
+      (* We say that a task is executing on alpha at time t iff it is
+         scheduled on some processor in that affinity. *)
+      Definition task_scheduled_on_affinity (tsk: sporadic_task) (t: time) :=
+        [exists cpu, (cpu \in alpha) && task_scheduled_on job_task sched tsk cpu t].
+
+    End ScheduleProperties.
+
+    Section Subset.
+
+      (* Given two affinities alpha' and alpha, ...*)
+      Variable alpha' alpha: affinity num_cpus.
+
+      (* ... we say that alpha' is subaffinity of alpha iff
+         alpha' is contained in alpha. *)
+      Definition is_subaffinity := {subset alpha' <= alpha}.
+
+      Section Lemmas.
+
+        Hypothesis H_subaffinity: is_subaffinity.
+
+        Lemma leq_subaffinity : #|alpha'| <= #|alpha|.
+        Proof.
+          assert (UNIQ: uniq (alpha)). by destruct (alpha).
+          assert (UNIQ': uniq (alpha')). by destruct (alpha').
+          move: (UNIQ) (UNIQ') => /card_uniqP -> /card_uniqP ->.
+          by apply uniq_leq_size.
+        Qed.  
+
+      End Lemmas.
+      
+    End Subset.
+
+    Section IntersectingAffinities.
+
+      (* We say that alpha and alpha' intersect iff there exists a processor
+         that belongs to both affinities. *)
+      Definition affinity_intersects (alpha alpha': affinity num_cpus) :=
+        [exists cpu, (cpu \in alpha) && (cpu \in alpha')].
+
+    End IntersectingAffinities.
+  
+  End Properties.
+  
+End Affinity.
\ No newline at end of file
diff --git a/model/apa/arrival_sequence.v b/model/apa/arrival_sequence.v
new file mode 100644
index 0000000000000000000000000000000000000000..0ab7d9296238e824a51fce508cb97454946058c9
--- /dev/null
+++ b/model/apa/arrival_sequence.v
@@ -0,0 +1,2 @@
+(* The arrival sequence remains the same under APA scheduling. *)
+Require Export rt.model.basic.arrival_sequence.
\ No newline at end of file
diff --git a/model/apa/constrained_deadlines.v b/model/apa/constrained_deadlines.v
new file mode 100644
index 0000000000000000000000000000000000000000..55b1abc631378c74b5becd5dc033e357a2d6d69d
--- /dev/null
+++ b/model/apa/constrained_deadlines.v
@@ -0,0 +1,274 @@
+Require Import rt.util.all.
+Require Import rt.model.apa.task rt.model.apa.job rt.model.apa.schedule
+               rt.model.apa.priority rt.model.apa.task_arrival
+               rt.model.apa.interference rt.model.apa.affinity
+               rt.model.apa.platform.
+From mathcomp Require Import ssreflect ssrbool ssrfun eqtype ssrnat seq fintype bigop.
+
+Module ConstrainedDeadlines.
+
+  Import Job SporadicTaskset ScheduleOfSporadicTask SporadicTaskset
+         SporadicTaskArrival Interference Priority Affinity Platform.
+  
+  Section Lemmas.
+
+    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_cost: Job -> time.
+    Variable job_deadline: Job -> time.
+    Variable job_task: Job -> sporadic_task.
+    
+    (* Assume any job arrival sequence ... *)
+    Context {arr_seq: arrival_sequence Job}.
+
+    (* ... and any schedule of this arrival sequence. *)
+    Context {num_cpus: nat}.
+    Variable sched: schedule num_cpus arr_seq.
+
+    (* Assume that every task has a processor affinity alpha. *)
+    Variable alpha: task_affinity sporadic_task num_cpus.
+    
+    (* Assume all jobs have valid parameters, ...*)
+    Hypothesis H_valid_job_parameters:
+      forall (j: JobIn arr_seq),
+        valid_sporadic_job task_cost task_deadline job_cost job_deadline job_task j.
+
+    (* In this section we prove the absence of multiple jobs of the same
+       task when constrained deadlines are assumed.  *)
+    Section NoMultipleJobs.
+
+      (* Assume any work-conserving priority-based scheduler. *)
+      Variable higher_eq_priority: JLDP_policy arr_seq.
+      Hypothesis H_work_conserving:
+        apa_work_conserving job_cost job_task sched alpha.
+      Hypothesis H_enforces_JLDP_policy:
+        enforces_JLDP_policy_under_weak_APA job_cost job_task sched alpha higher_eq_priority.
+
+      (* Consider task set ts. *)
+      Variable ts: taskset_of sporadic_task.
+
+      (* Assume that all jobs come from the taskset. *)
+      Hypothesis H_all_jobs_from_taskset:
+        forall (j: JobIn arr_seq), job_task j \in ts.
+
+      (* Suppose that jobs are sequential, ...*)
+      Hypothesis H_sequential_jobs: sequential_jobs sched.
+      (* ... jobs only execute after they arrive, ... *)
+      Hypothesis H_jobs_must_arrive_to_execute:
+        jobs_must_arrive_to_execute sched.
+      (* ... and jobs do not execute after completion. *)
+      Hypothesis H_completed_jobs_dont_execute:
+        completed_jobs_dont_execute job_cost sched.
+
+      (* Assume that the schedule satisfies the sporadic task model ...*)
+      Hypothesis H_sporadic_tasks:
+        sporadic_task_model task_period arr_seq job_task.
+
+      (* Consider a valid task tsk, ...*)
+      Variable tsk: sporadic_task.
+      Hypothesis H_valid_task: is_valid_sporadic_task task_cost task_period task_deadline tsk.
+
+      (*... whose job j ... *)
+      Variable j: JobIn arr_seq.
+      Variable H_job_of_tsk: job_task j = tsk.
+
+      (*... is backlogged at time t. *)
+      Variable t: time.
+      Hypothesis H_j_backlogged: backlogged job_cost sched j t.
+
+      (* Assume that any previous jobs of tsk have completed by the period. *)
+      Hypothesis H_all_previous_jobs_completed :
+        forall (j_other: JobIn arr_seq) tsk_other,
+          job_task j_other = tsk_other ->
+          job_arrival j_other + task_period tsk_other <= t ->
+          completed job_cost sched j_other (job_arrival j_other + task_period (job_task j_other)).
+
+      Let scheduled_task_other_than (tsk tsk_other: sporadic_task) :=
+        task_is_scheduled job_task sched tsk_other t && (tsk_other != tsk).
+
+      (* Then, there can be at most one pending job of each task at time t. *)
+      Lemma platform_at_most_one_pending_job_of_each_task :
+        forall j1 j2,
+          pending job_cost sched j1 t ->
+          pending job_cost sched j2 t ->
+          job_task j1 = job_task j2 ->
+          j1 = j2.
+      Proof.
+        rename H_sporadic_tasks into SPO, H_all_previous_jobs_completed into PREV.
+        intros j1 j2 PENDING1 PENDING2 SAMEtsk.
+        apply/eqP; rewrite -[_ == _]negbK; apply/negP; red; move => /eqP DIFF. 
+        move: PENDING1 PENDING2 => /andP [ARRIVED1 /negP NOTCOMP1] /andP [ARRIVED2 /negP NOTCOMP2].
+        destruct (leqP (job_arrival j1) (job_arrival j2)) as [BEFORE1 | BEFORE2].
+        {
+          specialize (SPO j1 j2 DIFF SAMEtsk BEFORE1).
+          assert (LEt: job_arrival j1 + task_period (job_task j1) <= t).
+          {
+            by apply leq_trans with (n := job_arrival j2); first by done.
+          }
+          exploit (PREV j1 (job_task j1)); try (by done).
+          intros COMP1; apply NOTCOMP1.
+          by apply completion_monotonic with (t0 := job_arrival j1 + task_period (job_task j1)). 
+        }
+        {
+          apply ltnW in BEFORE2.
+          exploit (SPO j2 j1); [by red; ins; subst | by rewrite SAMEtsk | by done | intro SPO'].
+          assert (LEt: job_arrival j2 + task_period (job_task j2) <= t).
+          {
+            by apply leq_trans with (n := job_arrival j1); first by done.
+          }
+          exploit (PREV j2 (job_task j2)); try (by done).
+          intros COMP2; apply NOTCOMP2.
+          by apply completion_monotonic with (t0 := job_arrival j2 + task_period (job_task j2)).
+        }
+      Qed.
+
+    End NoMultipleJobs.
+
+    (* In this section we also prove the absence of multiple jobs of the same
+       task when constrained deadlines are assumed, but in the specific case
+       of fixed-priority scheduling.  *)
+    Section NoMultipleJobsFP.
+
+      (* Assume any work-conserving priority-based scheduler. *)
+      Variable higher_eq_priority: FP_policy sporadic_task.
+      Hypothesis H_work_conserving: apa_work_conserving job_cost job_task sched alpha.
+      Hypothesis H_enforces_JLDP_policy:
+        enforces_FP_policy_under_weak_APA job_cost job_task sched alpha higher_eq_priority.
+
+      (* Consider any task set ts. *)
+      Variable ts: taskset_of sporadic_task.
+
+      (* Assume that all jobs come from the taskset. *)
+      Hypothesis H_all_jobs_from_taskset:
+        forall (j: JobIn arr_seq), job_task j \in ts.
+
+      (* Suppose that jobs are sequential, ...*)
+      Hypothesis H_sequential_jobs: sequential_jobs sched.
+      (* ... jobs only execute after the jitter, ... *)
+      Hypothesis H_jobs_must_arrive_to_execute:
+        jobs_must_arrive_to_execute sched.
+      (* ... and jobs do not execute after completion. *)
+      Hypothesis H_completed_jobs_dont_execute:
+        completed_jobs_dont_execute job_cost sched.
+
+      (* Assume that the schedule satisfies the sporadic task model ...*)
+      Hypothesis H_sporadic_tasks:
+        sporadic_task_model task_period arr_seq job_task.
+
+      (* Consider a valid task tsk, ...*)
+      Variable tsk: sporadic_task.
+      Hypothesis H_valid_task: is_valid_sporadic_task task_cost task_period task_deadline tsk.
+
+      (*... whose job j ... *)
+      Variable j: JobIn arr_seq.
+      Variable H_job_of_tsk: job_task j = tsk.
+
+      (*... is backlogged at time t <= job_arrival j + task_period tsk. *)
+      Variable t: time.
+      Hypothesis H_j_backlogged: backlogged job_cost sched j t.
+      Hypothesis H_t_before_period: t < job_arrival j + task_period tsk.
+
+      (* Recall the definition of a higher-priority task in affinity (alpha' tsk). *)
+      Let hp_task_in alpha' := higher_priority_task_in alpha higher_eq_priority tsk alpha'.
+
+      (* Assume that any jobs of higher-priority tasks complete by their period. *)
+      Hypothesis H_all_previous_jobs_completed :
+        forall (j_other: JobIn arr_seq) tsk_other,
+          job_task j_other = tsk_other ->
+          hp_task_in (alpha tsk) tsk_other ->
+          completed job_cost sched j_other (job_arrival j_other + task_period tsk_other).
+
+      (* Assume that any jobs of tsk prior to j complete by their period. *)
+      Hypothesis H_all_previous_jobs_of_tsk_completed :
+        forall j0 : JobIn arr_seq,
+          job_task j0 = tsk ->
+          job_arrival j0 < job_arrival j ->
+          completed job_cost sched j0 (job_arrival j0 + task_period tsk).
+      
+      Definition scheduled_task_with_higher_eq_priority (tsk tsk_other: sporadic_task) :=
+        task_is_scheduled job_task sched tsk_other t &&
+        hp_task_in (alpha tsk) tsk_other.
+                             
+      (* Then, there can be at most one pending job of higher-priority tasks at time t. *)
+      Lemma platform_fp_no_multiple_jobs_of_interfering_tasks :
+          forall j1 j2,
+            pending job_cost sched j1 t ->
+            pending job_cost sched j2 t ->
+            job_task j1 = job_task j2 ->
+            hp_task_in (alpha tsk) (job_task j1) ->
+            j1 = j2.
+      Proof.
+        unfold sporadic_task_model in *.
+        rename H_sporadic_tasks into SPO, H_all_previous_jobs_of_tsk_completed into PREVtsk,
+               H_all_previous_jobs_completed into PREV.
+        intros j1 j2 PENDING1 PENDING2 SAMEtsk INTERF.
+        apply/eqP; rewrite -[_ == _]negbK; apply/negP; red; move => /eqP DIFF.
+        move: PENDING1 PENDING2 => /andP [ARRIVED1 /negP NOTCOMP1] /andP [ARRIVED2 /negP NOTCOMP2].
+        destruct (leqP (job_arrival j1) (job_arrival j2)) as [BEFORE1 | BEFORE2].
+        {
+          specialize (SPO j1 j2 DIFF SAMEtsk BEFORE1).
+          assert (LEt: job_arrival j1 + task_period (job_task j1) <= t).
+            by apply leq_trans with (n := job_arrival j2).
+          exploit (PREV j1 (job_task j1)); [by done | by apply INTERF | intros COMP1].
+          apply NOTCOMP1.
+          by apply completion_monotonic with (t0 := job_arrival j1 + task_period (job_task j1)). 
+        }
+        {
+          apply ltnW in BEFORE2.
+          exploit (SPO j2 j1); [by red; ins; subst j2 | by rewrite SAMEtsk | by done | intro SPO'].
+          assert (LEt: job_arrival j2 + task_period (job_task j2) <= t).
+            by apply leq_trans with (n := job_arrival j1).
+          exploit (PREV j2 (job_task j2));
+            [by done | by rewrite -SAMEtsk | intro COMP2 ].
+          apply NOTCOMP2.
+          by apply completion_monotonic with (t0 := job_arrival j2 + task_period (job_task j2)).
+        }
+      Qed.
+      
+      (* Also, there can be at most one pending job of tsk at time t. *)
+      Lemma platform_fp_no_multiple_jobs_of_tsk :
+          forall j',
+            pending job_cost sched j' t ->
+            job_task j' = tsk ->
+            j' = j.
+      Proof.
+        unfold sporadic_task_model in *.
+        rename H_sporadic_tasks into SPO,
+               H_valid_task into PARAMS,
+               H_all_previous_jobs_of_tsk_completed into PREVtsk,
+               H_all_previous_jobs_completed into PREV,
+               H_j_backlogged into BACK, H_job_of_tsk into JOBtsk.
+        intros j' PENDING' SAMEtsk.
+        apply/eqP; rewrite -[_ == _]negbK; apply/negP; red; move => /eqP DIFF.
+        move: BACK PENDING' => /andP [/andP [ARRIVED /negP NOTCOMP] NOTSCHED]
+                               /andP [ARRIVED' /negP NOTCOMP'].
+        destruct (leqP (job_arrival j') (job_arrival j)) as [BEFORE | BEFORE'].
+        {
+          exploit (SPO j' j DIFF); [by rewrite JOBtsk | by done | intro SPO'].
+          assert (LEt: job_arrival j' + task_period tsk <= t).
+            by apply leq_trans with (n := job_arrival j); first by rewrite -SAMEtsk.
+          apply NOTCOMP'.
+          apply completion_monotonic with (t0 := job_arrival j' + task_period tsk); [by done | by done |].
+          apply PREVtsk; first by done.
+          apply leq_trans with (n := job_arrival j' + task_period tsk); last by rewrite -SAMEtsk.
+          rewrite -addn1; apply leq_add; first by done.
+          by unfold is_valid_sporadic_task in *; des.
+        }
+        {
+          unfold has_arrived in *.
+          rewrite leqNgt in ARRIVED'; move: ARRIVED' => /negP BUG; apply BUG.
+          apply leq_trans with (n := job_arrival j + task_period tsk); first by done.
+          by rewrite -JOBtsk; apply SPO;
+            [by red; ins; subst j' | by rewrite SAMEtsk | by apply ltnW].
+        }
+      Qed.
+      
+    End NoMultipleJobsFP.
+    
+  End Lemmas.
+
+End ConstrainedDeadlines.
\ No newline at end of file
diff --git a/model/apa/interference.v b/model/apa/interference.v
new file mode 100644
index 0000000000000000000000000000000000000000..f0a2fe842157a8bb5b8006fa23e7e8fe2a7bac30
--- /dev/null
+++ b/model/apa/interference.v
@@ -0,0 +1,269 @@
+Require Import rt.util.all rt.util.divround.
+Require Import rt.model.apa.task rt.model.apa.job rt.model.apa.schedule
+               rt.model.apa.priority rt.model.apa.workload rt.model.apa.affinity.
+From mathcomp Require Import ssreflect ssrbool eqtype ssrnat seq fintype bigop.
+
+Module Interference.
+
+  Import Schedule ScheduleOfSporadicTask Priority Workload Affinity.
+
+  (* In this section, we define the notion of a possible interfering task. *)
+  Section PossibleInterferingTasks.
+
+    Context {sporadic_task: eqType}.
+    Variable task_cost: sporadic_task -> time.
+    Variable task_period: sporadic_task -> time.
+    Variable task_deadline: sporadic_task -> time.
+
+    (* Consider an APA platform with affinity alpha. *)
+    Context {num_cpus: nat}.
+    Variable alpha: task_affinity sporadic_task num_cpus.
+    
+    Section FP.
+
+      (* Assume an FP policy. *)
+      Variable higher_eq_priority: FP_policy sporadic_task.
+
+      (* Let tsk be the task to be analyzed ... *)
+      Variable tsk: sporadic_task.
+
+      (* ...assuming a subaffinity alpha'. *)
+      Variable alpha': affinity num_cpus.
+
+      (* Let tsk_other be another task. *)
+      Variable tsk_other: sporadic_task.
+      
+      (* Under FP scheduling with constrained deadlines, tsk_other can only interfere
+         with tsk if it is a different task with higher or equal priority and
+         intersecting affinity. *)
+      Definition higher_priority_task_in :=
+        higher_eq_priority tsk_other tsk &&
+        (tsk_other != tsk) &&
+        affinity_intersects alpha' (alpha tsk_other).
+      
+    End FP.
+
+    Section JLFP.
+
+      (* Let tsk be the task to be analyzed ... *)
+      Variable tsk: sporadic_task.
+
+      (* ...assuming a subaffinity alpha'. *)
+      Variable alpha': affinity num_cpus.
+
+      (* Let tsk_other be another task. *)
+      Variable tsk_other: sporadic_task.
+
+      (* Under JLFP/JLDP scheduling with constrained deadlines, tsk_other can only interfere
+         with tsk if it is a different task with intersecting affinity. *)
+      Definition different_task_in :=
+        (tsk_other != tsk) &&
+        affinity_intersects alpha' (alpha tsk_other).
+
+    End JLFP.
+    
+  End PossibleInterferingTasks.
+  
+  Section InterferenceDefs.
+
+    Context {sporadic_task: eqType}.
+    Context {Job: eqType}.
+    Variable job_cost: Job -> time.
+    Variable job_task: Job -> sporadic_task.
+
+    (* Assume any job arrival sequence...*)
+    Context {arr_seq: arrival_sequence Job}.
+
+    (* ... and any schedule. *)
+    Context {num_cpus: nat}.
+    Variable sched: schedule num_cpus arr_seq.
+
+    (* Assume that every job at any time has a processor affinity alpha. *)
+    Variable alpha: task_affinity sporadic_task num_cpus.
+    
+    (* Consider any job j that incurs interference. *)
+    Variable j: JobIn arr_seq.
+
+    (* Recall the definition of backlogged (pending and not scheduled). *)
+    Let job_is_backlogged (t: time) := backlogged job_cost sched j t.
+
+    (* First, we define total interference. *)
+    Section TotalInterference.
+      
+      (* The total interference incurred by job j during [t1, t2) is the
+         cumulative time in which j is backlogged in this interval. *)
+      Definition total_interference (t1 t2: time) :=
+        \sum_(t1 <= t < t2) job_is_backlogged t.
+
+    End TotalInterference.
+
+    (* Next, we define job interference. *)
+    Section JobInterference.
+
+      (* Let job_other be a job that interferes with j. *)
+      Variable job_other: JobIn arr_seq.
+
+      (* The interference caused by job_other during [t1, t2) is the cumulative
+         time in which j is backlogged while job_other is scheduled. *)
+      Definition job_interference (t1 t2: time) :=
+        \sum_(t1 <= t < t2)
+          \sum_(cpu < num_cpus)
+            (job_is_backlogged t &&
+            can_execute_on alpha (job_task j) cpu &&
+            scheduled_on sched job_other cpu t).
+
+    End JobInterference.
+
+    (* Next, we define task interference. *)
+    Section TaskInterference.
+
+      (* Consider any interfering task tsk_other. *)
+      Variable tsk_other: sporadic_task.
+    
+      (* The interference caused by tsk during [t1, t2) is the cumulative time
+         in which j is backlogged while tsk is scheduled. *)
+      Definition task_interference (t1 t2: time) :=
+        \sum_(t1 <= t < t2)
+          \sum_(cpu < num_cpus)
+            (job_is_backlogged t &&
+            can_execute_on alpha (job_task j) cpu &&
+            task_scheduled_on job_task sched tsk_other cpu t).
+
+    End TaskInterference.
+
+    (* Next, we define an approximation of the total interference based on
+       each per-task interference. *)
+    Section TaskInterferenceJobList.
+
+      Variable tsk_other: sporadic_task.
+
+      Definition task_interference_joblist (t1 t2: time) :=
+        \sum_(j <- jobs_scheduled_between sched t1 t2 | job_task j == tsk_other)
+         job_interference j t1 t2.
+
+    End TaskInterferenceJobList.
+
+    (* Now we prove some basic lemmas about interference. *)
+    Section BasicLemmas.
+
+      (* Total interference cannot be larger than the considered time window. *)
+      Lemma total_interference_le_delta :
+        forall t1 t2,
+          total_interference t1 t2 <= t2 - t1.
+      Proof.
+        unfold total_interference; intros t1 t2.
+        apply leq_trans with (n := \sum_(t1 <= t < t2) 1);
+          first by apply leq_sum; ins; apply leq_b1.
+        by rewrite big_const_nat iter_addn mul1n addn0 leqnn.
+      Qed.
+
+      (* Job interference is bounded by the service of the interfering job. *)
+      Lemma job_interference_le_service :
+        forall j_other t1 t2,
+          job_interference j_other t1 t2 <= service_during sched j_other t1 t2.
+      Proof.
+        intros j_other t1 t2; unfold job_interference, service_during.
+        apply leq_sum; intros t _.
+        unfold service_at; rewrite [\sum_(_ < _ | scheduled_on _ _ _  _)_]big_mkcond.
+        apply leq_sum; intros cpu _.
+        destruct (job_is_backlogged t); [rewrite andTb | by rewrite andFb].
+        destruct (can_execute_on alpha (job_task j) cpu); [rewrite andTb | by rewrite andFb].
+        by destruct (scheduled_on sched j_other cpu t).
+      Qed.
+
+      (* Task interference is bounded by the workload of the interfering task. *)
+      Lemma task_interference_le_workload :
+        forall tsk t1 t2,
+          task_interference tsk t1 t2 <= workload job_task sched tsk t1 t2.
+      Proof.
+        unfold task_interference, workload; intros tsk t1 t2.
+        apply leq_sum; intros t _.
+        apply leq_sum; intros cpu _.
+        destruct (job_is_backlogged t); [rewrite andTb | by rewrite andFb].
+        destruct (can_execute_on alpha (job_task j) cpu); [rewrite andTb | by rewrite andFb].
+        unfold task_scheduled_on, service_of_task.
+        by destruct (sched cpu t).
+      Qed.
+
+    End BasicLemmas.
+
+    (* Now we prove some bounds on interference for sequential jobs. *)
+    Section InterferenceNoParallelism.
+
+      (* If jobs are sequential, ... *)
+      Hypothesis H_sequential_jobs: sequential_jobs sched.
+    
+      (* ... then the interference incurred by a job in an interval
+         of length delta is at most delta. *)
+      Lemma job_interference_le_delta :
+        forall j_other t1 delta,
+          job_interference j_other t1 (t1 + delta) <= delta.
+      Proof.
+        rename H_sequential_jobs into SEQ.
+        unfold job_interference, sequential_jobs in *.
+        intros j_other t1 delta.
+        apply leq_trans with (n := \sum_(t1 <= t < t1 + delta) 1);
+          last by rewrite big_const_nat iter_addn mul1n addn0 addKn leqnn.
+        apply leq_sum; intros t _.
+        destruct ([exists cpu, scheduled_on sched j_other cpu t]) eqn:EX.
+        {
+          move: EX => /existsP [cpu SCHED].
+          rewrite (bigD1 cpu) // /=.
+          rewrite big_mkcond (eq_bigr (fun x => 0)) /=;
+            first by simpl_sum_const; rewrite leq_b1.
+          intros cpu' _; des_if_goal; last by done.
+          destruct (scheduled_on sched j_other cpu' t) eqn:SCHED'; last by rewrite andbF.
+          move: SCHED SCHED' => /eqP SCHED /eqP SCHED'.
+          by specialize (SEQ j_other t cpu cpu' SCHED SCHED'); rewrite SEQ in Heq.
+        }
+        {
+          apply negbT in EX; rewrite negb_exists in EX.
+          move: EX => /forallP EX.
+          rewrite (eq_bigr (fun x => 0)); first by simpl_sum_const.
+          by intros cpu _; specialize (EX cpu); apply negbTE in EX; rewrite EX andbF.
+        }
+      Qed.
+
+    End InterferenceNoParallelism.
+    
+    (* Next, we show that the cumulative per-task interference bounds the total
+       interference. *)
+    Section BoundUsingPerTaskInterference.
+
+      Lemma interference_le_interference_joblist :
+        forall tsk t1 t2,
+          task_interference tsk t1 t2 <= task_interference_joblist tsk t1 t2.
+      Proof.
+        intros tsk t1 t2.
+        unfold task_interference, task_interference_joblist, job_interference, job_is_backlogged.
+        rewrite [\sum_(_ <- _ sched _ _ | _) _]exchange_big /=.
+        rewrite big_nat_cond [\sum_(_ <= _ < _ | true) _]big_nat_cond.
+        apply leq_sum; move => t /andP [LEt _].
+        rewrite exchange_big /=.
+        apply leq_sum; intros cpu _.
+        destruct (backlogged job_cost sched j t) eqn:BACK;      
+          last by rewrite andFb (eq_bigr (fun x => 0));
+            first by rewrite big_const_seq iter_addn mul0n addn0.
+        rewrite andTb.
+        destruct (task_scheduled_on job_task sched tsk cpu t) eqn:SCHED;
+          [rewrite andbT | by rewrite andbF].
+        unfold scheduled_on, task_scheduled_on in *.
+        destruct (sched cpu t) as [j' |] eqn:SOME; last by done.
+        rewrite big_mkcond /= (bigD1_seq j') /=; last by apply undup_uniq.
+        {
+          by rewrite SCHED eq_refl andbT leq_addr.
+        }
+        {
+          unfold jobs_scheduled_between.
+          rewrite mem_undup; apply mem_bigcat_nat with (j := t);
+            first by done.
+          apply mem_bigcat_ord with (j := cpu); first by apply ltn_ord.
+          by unfold make_sequence; rewrite SOME mem_seq1 eq_refl.
+        }
+      Qed.
+        
+    End BoundUsingPerTaskInterference.
+
+  End InterferenceDefs.
+
+End Interference.
\ No newline at end of file
diff --git a/model/apa/interference_edf.v b/model/apa/interference_edf.v
new file mode 100644
index 0000000000000000000000000000000000000000..c1bb429ec65ae6116bf906f6750c1e82940665c4
--- /dev/null
+++ b/model/apa/interference_edf.v
@@ -0,0 +1,76 @@
+Require Import rt.util.all.
+Require Import rt.model.apa.task rt.model.apa.job rt.model.apa.schedule
+               rt.model.apa.priority rt.model.apa.task_arrival rt.model.apa.interference
+               rt.model.apa.arrival_sequence rt.model.apa.platform
+               rt.model.apa.affinity.
+From mathcomp Require Import ssreflect ssrbool ssrfun eqtype ssrnat seq fintype bigop.
+
+Module InterferenceEDF.
+
+  Import Schedule Priority Platform Interference Priority Affinity.
+  
+  Section Lemmas. 
+
+    Context {sporadic_task: eqType}.
+    Context {Job: eqType}.
+    Variable job_cost: Job -> time.
+    Variable job_deadline: Job -> time.
+    Variable job_task: Job -> sporadic_task.
+    
+    (* Assume any job arrival sequence... *)
+    Context {arr_seq: arrival_sequence Job}.
+
+    (* Consider any schedule. *)
+    Variable num_cpus: nat.
+    Variable sched: schedule num_cpus arr_seq.
+
+    (* Assume that every job at any time has a processor affinity alpha. *)
+    Variable alpha: task_affinity sporadic_task num_cpus.
+    
+    (* Assume that the schedule satisfies the global scheduling invariant
+       for EDF, i.e., if any job of tsk is backlogged, every processor
+       must be busy with jobs with no larger absolute deadline. *)
+    Hypothesis H_scheduler_uses_EDF:
+      enforces_JLDP_policy_under_weak_APA job_cost job_task sched alpha (EDF job_deadline). 
+
+    (* Under EDF scheduling, a job only causes interference if its deadline
+       is not larger than the deadline of the analyzed job. *)
+    Lemma interference_under_edf_implies_shorter_deadlines :
+      forall (j j': JobIn arr_seq) t1 t2,
+        job_interference job_cost job_task sched alpha j' j t1 t2 != 0 ->
+        job_arrival j + job_deadline j <= job_arrival j' + job_deadline j'.
+    Proof.
+      rename H_scheduler_uses_EDF into PRIO.
+      unfold enforces_JLDP_policy_under_weak_APA in *.
+      intros j j' t1 t2 INTERF.
+      unfold job_interference in INTERF.
+      destruct ([exists t': 'I_t2,
+                   [exists cpu: processor num_cpus,
+                      (t' >= t1) &&
+                      backlogged job_cost sched j' t' &&
+                      can_execute_on alpha (job_task j') cpu &&
+                      scheduled_on sched j cpu t']]) eqn:EX.
+      {
+        move: EX => /existsP [t' /existsP [cpu /andP [/andP [/andP [LE BACK] CAN] SCHED]]].
+        by eapply PRIO in SCHED; last by apply CAN.
+      }
+      {
+        apply negbT in EX; rewrite negb_exists in EX; move: EX => /forallP ALL.
+        rewrite big_nat_cond (eq_bigr (fun x => 0)) in INTERF;
+          first by rewrite -big_nat_cond big_const_nat iter_addn mul0n  addn0 eq_refl in INTERF.
+        move => i /andP [/andP [GEi LTi] _].
+        specialize (ALL (Ordinal LTi)).
+        rewrite negb_exists in ALL.
+        move: ALL => /forallP ALL.
+        rewrite (eq_bigr (fun x => 0));
+          first by rewrite big_const_ord iter_addn mul0n addn0.
+        intros cpu _; specialize (ALL cpu); simpl in ALL.
+        destruct (backlogged job_cost sched j' i); last by rewrite andFb.
+        rewrite GEi 2!andTb in ALL; rewrite andTb.
+        by apply negbTE in ALL; rewrite ALL.
+      }
+    Qed.
+
+  End Lemmas.
+
+End InterferenceEDF.
\ No newline at end of file
diff --git a/model/apa/job.v b/model/apa/job.v
new file mode 100644
index 0000000000000000000000000000000000000000..3e6878902b7da1994119dd724869a2e3baa0d599
--- /dev/null
+++ b/model/apa/job.v
@@ -0,0 +1,2 @@
+(* The notion of a valid job remains the same under APA scheduling. *)
+Require Export rt.model.basic.job.
\ No newline at end of file
diff --git a/model/apa/platform.v b/model/apa/platform.v
new file mode 100644
index 0000000000000000000000000000000000000000..d734339292813ac5390e93d4770c3e184a6dedd0
--- /dev/null
+++ b/model/apa/platform.v
@@ -0,0 +1,87 @@
+Require Import rt.util.all.
+Require Import rt.model.apa.task rt.model.apa.job rt.model.apa.schedule
+               rt.model.apa.priority rt.model.apa.task_arrival
+               rt.model.apa.interference rt.model.apa.affinity.
+From mathcomp Require Import ssreflect ssrbool ssrfun eqtype ssrnat seq fintype bigop.
+
+Module Platform.
+
+  Import Job SporadicTaskset ScheduleOfSporadicTask SporadicTaskset
+         SporadicTaskArrival Interference Priority Affinity.
+
+  Section Properties.
+    
+    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_cost: Job -> time.
+    Variable job_deadline: Job -> time.
+    Variable job_task: Job -> sporadic_task.
+    
+    (* Assume any job arrival sequence ... *)
+    Context {arr_seq: arrival_sequence Job}.
+
+    (* ... and any schedule of this arrival sequence. *)
+    Context {num_cpus: nat}.
+    Variable sched: schedule num_cpus arr_seq.
+
+    (* Assume that every task has a processor affinity alpha. *)
+    Variable alpha: task_affinity sporadic_task num_cpus.
+    
+    Section Execution.
+
+      (* A schedule is work-conserving iff when a job j is backlogged, all
+         processors *on which j can be scheduled* are busy with other jobs. *)
+      Definition apa_work_conserving :=
+        forall j t,
+          backlogged job_cost sched j t ->
+          forall cpu,
+            can_execute_on alpha (job_task j) cpu ->
+            exists j_other,
+              scheduled_on sched j_other cpu t.
+
+      (* In a schedule that enforces affinities, a job is scheduled
+         only if its affinity allows it. *)
+      Definition respects_affinity :=
+        forall j cpu t,
+          scheduled_on sched j cpu t ->
+          can_execute_on alpha (job_task j) cpu.
+
+    End Execution.
+
+    Section JLDP.
+
+      (* A JLFP/JLDP policy ...*)
+      Variable higher_eq_priority: JLDP_policy arr_seq.
+
+      (* ... is enforced by a weak APA scheduler iff at any time t,
+         for any backlogged job j, if there is another job j_hp
+         executing on j's affinity, then j_hp's priority must be
+         as high as j's priority. *)
+      Definition enforces_JLDP_policy_under_weak_APA :=
+        forall (j j_hp: JobIn arr_seq) cpu t,
+          backlogged job_cost sched j t ->
+          scheduled_on sched j_hp cpu t ->
+          can_execute_on alpha (job_task j) cpu ->
+          higher_eq_priority t j_hp j.
+      
+    End JLDP.
+    
+    Section FP.
+
+      (* Given an FP policy, ...*)
+      Variable higher_eq_priority: FP_policy sporadic_task.
+
+      (* ... this policy is enforced by a weak APA scheduler iff
+         the corresponding JLDP policy is enforced by the scheduler. *)
+      Definition enforces_FP_policy_under_weak_APA :=
+        enforces_JLDP_policy_under_weak_APA (FP_to_JLDP job_task higher_eq_priority).
+
+    End FP.
+
+  End Properties.
+  
+End Platform.
\ No newline at end of file
diff --git a/model/apa/priority.v b/model/apa/priority.v
new file mode 100644
index 0000000000000000000000000000000000000000..ee2fe4187ad5f9e099450d57026e0a05ede7c0cf
--- /dev/null
+++ b/model/apa/priority.v
@@ -0,0 +1,2 @@
+(* The definition of priority remains the same under APA scheduling. *)
+Require Export rt.model.basic.priority.
\ No newline at end of file
diff --git a/model/apa/response_time.v b/model/apa/response_time.v
new file mode 100644
index 0000000000000000000000000000000000000000..06c3c32c6294099338ab2e33f00382a08a44487c
--- /dev/null
+++ b/model/apa/response_time.v
@@ -0,0 +1,2 @@
+(* The definition of response time remains the same under APA scheduling. *)
+Require Export rt.model.basic.response_time.
\ No newline at end of file
diff --git a/model/apa/schedulability.v b/model/apa/schedulability.v
new file mode 100644
index 0000000000000000000000000000000000000000..db9b150a58cc5222236c5269952218bb7abe79c4
--- /dev/null
+++ b/model/apa/schedulability.v
@@ -0,0 +1,2 @@
+(* The notion of schedulability remains the same under APA scheduling. *)
+Require Export rt.model.basic.schedulability.
\ No newline at end of file
diff --git a/model/apa/schedule.v b/model/apa/schedule.v
new file mode 100644
index 0000000000000000000000000000000000000000..80aa78b0d6d4d096740ddce3a839fdf3b14a7d2e
--- /dev/null
+++ b/model/apa/schedule.v
@@ -0,0 +1,2 @@
+(* The definition of schedule remains the same under APA scheduling. *)
+Require Export rt.model.basic.schedule.
\ No newline at end of file
diff --git a/model/apa/task.v b/model/apa/task.v
new file mode 100644
index 0000000000000000000000000000000000000000..12257fa0840d540106f3908d33a0190d24ec37fa
--- /dev/null
+++ b/model/apa/task.v
@@ -0,0 +1,2 @@
+(* The notion of a valid task remains the same under APA scheduling. *)
+Require Export rt.model.basic.task.
\ No newline at end of file
diff --git a/model/apa/task_arrival.v b/model/apa/task_arrival.v
new file mode 100644
index 0000000000000000000000000000000000000000..d7299e0141593fe1e59ad6a3ebc964267bde4af5
--- /dev/null
+++ b/model/apa/task_arrival.v
@@ -0,0 +1,2 @@
+(* The notion of interarrival times does not change under APA scheduling. *)
+Require Export rt.model.basic.task_arrival.
\ No newline at end of file
diff --git a/model/apa/time.v b/model/apa/time.v
new file mode 100644
index 0000000000000000000000000000000000000000..394817a0f5a1deef15d9e34ac23dea748feec9ec
--- /dev/null
+++ b/model/apa/time.v
@@ -0,0 +1,2 @@
+(* The definition of time is the same. *)
+Require Export rt.model.basic.time.
\ No newline at end of file
diff --git a/model/apa/workload.v b/model/apa/workload.v
new file mode 100644
index 0000000000000000000000000000000000000000..b3482234eedcb5ac8bd3676ff5a7156d51b60d5d
--- /dev/null
+++ b/model/apa/workload.v
@@ -0,0 +1,2 @@
+(* The definition of workload does not change under APA scheduling. *)
+Require Export rt.model.basic.workload.
\ No newline at end of file
diff --git a/model/basic/platform_fp.v b/model/basic/constrained_deadlines.v
similarity index 57%
rename from model/basic/platform_fp.v
rename to model/basic/constrained_deadlines.v
index 6befe703964baad39a7873e65c7fd76589d6a6df..04d6105d588574118f9915440a1becc7fbe5a2c1 100644
--- a/model/basic/platform_fp.v
+++ b/model/basic/constrained_deadlines.v
@@ -1,15 +1,16 @@
 Require Import rt.util.all.
 Require Import rt.model.basic.task rt.model.basic.job rt.model.basic.schedule
-               rt.model.basic.task_arrival rt.model.basic.interference
-               rt.model.basic.priority rt.model.basic.platform.
+               rt.model.basic.priority rt.model.basic.task_arrival
+               rt.model.basic.interference rt.model.basic.platform.
 From mathcomp Require Import ssreflect ssrbool ssrfun eqtype ssrnat seq fintype bigop.
 
-Module PlatformFP.
-
-  Import Job SporadicTaskset Schedule ScheduleOfSporadicTask SporadicTaskset SporadicTaskArrival Interference Priority Platform.
+Module ConstrainedDeadlines.
 
+  Import Job SporadicTaskset ScheduleOfSporadicTask SporadicTaskset
+         SporadicTaskArrival Interference Priority Platform.
+  
   Section Lemmas.
-    
+
     Context {sporadic_task: eqType}.
     Variable task_cost: sporadic_task -> time.
     Variable task_period: sporadic_task -> time.
@@ -20,19 +21,181 @@ Module PlatformFP.
     Variable job_deadline: Job -> time.
     Variable job_task: Job -> sporadic_task.
     
-    (* Consider any job arrival sequence ... *)
+    (* Assume any job arrival sequence ... *)
     Context {arr_seq: arrival_sequence Job}.
 
     (* ... and any schedule of this arrival sequence. *)
     Context {num_cpus: nat}.
     Variable sched: schedule num_cpus arr_seq.
-    
-    (* Assume that all jobs have valid parameters. *)
+
+    (* Assume all jobs have valid parameters, ...*)
     Hypothesis H_valid_job_parameters:
       forall (j: JobIn arr_seq),
         valid_sporadic_job task_cost task_deadline job_cost job_deadline job_task j.
 
-    Section JobInvariantAsTaskInvariant.
+    (* In this section we prove the absence of multiple jobs of the same
+       task when constrained deadlines are assumed.  *)
+    Section NoMultipleJobs.
+
+      (* Assume any work-conserving priority-based scheduler. *)
+      Variable higher_eq_priority: JLDP_policy arr_seq.
+      Hypothesis H_work_conserving: work_conserving job_cost sched.
+      Hypothesis H_enforces_JLDP_policy:
+        enforces_JLDP_policy job_cost sched higher_eq_priority.
+
+      (* Consider task set ts. *)
+      Variable ts: taskset_of sporadic_task.
+
+      (* Assume that all jobs come from the taskset. *)
+      Hypothesis H_all_jobs_from_taskset:
+        forall (j: JobIn arr_seq), job_task j \in ts.
+
+      (* Suppose that jobs are sequential, ...*)
+      Hypothesis H_sequential_jobs: sequential_jobs sched.
+      (* ... jobs must arrive to execute, ... *)
+      Hypothesis H_completed_jobs_dont_execute:
+        completed_jobs_dont_execute job_cost sched.
+      (* ... and jobs do not execute after completion. *)
+      Hypothesis H_jobs_must_arrive_to_execute:
+        jobs_must_arrive_to_execute sched.
+
+      (* Assume that the schedule satisfies the sporadic task model ...*)
+      Hypothesis H_sporadic_tasks:
+        sporadic_task_model task_period arr_seq job_task.
+
+      (* Consider a valid task tsk, ...*)
+      Variable tsk: sporadic_task.
+      Hypothesis H_valid_task: is_valid_sporadic_task task_cost task_period task_deadline tsk.
+
+      (*... whose job j ... *)
+      Variable j: JobIn arr_seq.
+      Variable H_job_of_tsk: job_task j = tsk.
+
+      (*... is backlogged at time t. *)
+      Variable t: time.
+      Hypothesis H_j_backlogged: backlogged job_cost sched j t.
+
+      (* Assume that any previous jobs of tsk have completed by the period. *)
+      Hypothesis H_all_previous_jobs_completed :
+        forall (j_other: JobIn arr_seq) tsk_other,
+          job_task j_other = tsk_other ->
+          job_arrival j_other + task_period tsk_other <= t ->
+          completed job_cost sched j_other (job_arrival j_other + task_period (job_task j_other)).
+
+      Let scheduled_task_other_than (tsk tsk_other: sporadic_task) :=
+        task_is_scheduled job_task sched tsk_other t && (tsk_other != tsk).
+
+      (* Then, there can be at most one pending job of each task at time t. *)
+      Lemma platform_at_most_one_pending_job_of_each_task :
+        forall j1 j2,
+          pending job_cost sched j1 t ->
+          pending job_cost sched j2 t ->
+          job_task j1 = job_task j2 ->
+          j1 = j2.
+      Proof.
+        rename H_sporadic_tasks into SPO, H_all_previous_jobs_completed into PREV.
+        intros j1 j2 PENDING1 PENDING2 SAMEtsk.
+        apply/eqP; rewrite -[_ == _]negbK; apply/negP; red; move => /eqP DIFF. 
+        move: PENDING1 PENDING2 => /andP [ARRIVED1 /negP NOTCOMP1] /andP [ARRIVED2 /negP NOTCOMP2].
+        destruct (leqP (job_arrival j1) (job_arrival j2)) as [BEFORE1 | BEFORE2].
+        {
+          specialize (SPO j1 j2 DIFF SAMEtsk BEFORE1).
+          exploit (PREV j1 (job_task j1));
+            [by done | by apply leq_trans with (n := job_arrival j2) | intros COMP1].
+          apply NOTCOMP1.
+          apply completion_monotonic with (t0 := job_arrival j1 + task_period (job_task j1));
+            try (by done).
+          by apply leq_trans with (n := job_arrival j2). 
+        }
+        {
+          apply ltnW in BEFORE2.
+          exploit (SPO j2 j1); [by red; ins; subst | by rewrite SAMEtsk | by done | intro SPO'].
+          exploit (PREV j2 (job_task j2));
+            [by done | by apply leq_trans with (n := job_arrival j1) | intros COMP2].
+          apply NOTCOMP2.
+          apply completion_monotonic with (t0 := job_arrival j2 + task_period (job_task j2));
+            try (by done).
+          by apply leq_trans with (n := job_arrival j1).
+        }
+      Qed.
+
+      (* Therefore, all processors are busy with tasks other than tsk. *)
+      Lemma platform_cpus_busy_with_interfering_tasks :      
+        count (scheduled_task_other_than tsk) ts = num_cpus.
+      Proof.
+        have UNIQ := platform_at_most_one_pending_job_of_each_task.
+        rename H_all_jobs_from_taskset into FROMTS,
+               H_sequential_jobs into SEQUENTIAL,
+               H_work_conserving into WORK,
+               H_enforces_JLDP_policy into PRIO,
+               H_j_backlogged into BACK,
+               H_job_of_tsk into JOBtsk,
+               H_valid_job_parameters into JOBPARAMS,
+               H_valid_task into TASKPARAMS,
+               H_all_previous_jobs_completed into PREV,
+               H_completed_jobs_dont_execute into COMP,
+               H_jobs_must_arrive_to_execute into ARRIVE.
+        apply work_conserving_eq_work_conserving_count in WORK.
+        unfold valid_sporadic_job, valid_realtime_job,
+               enforces_JLDP_policy, completed_jobs_dont_execute,
+               sporadic_task_model, is_valid_sporadic_task,
+               jobs_of_same_task_dont_execute_in_parallel,
+               sequential_jobs in *.  
+        apply/eqP; rewrite eqn_leq; apply/andP; split.
+        {
+          apply leq_trans with (n := count (fun x => task_is_scheduled job_task sched x t) ts);
+            first by apply sub_count; first by red; move => x /andP [SCHED _].    
+          unfold task_is_scheduled.
+          apply count_exists; first by destruct ts.
+          {
+            intros cpu x1 x2 SCHED1 SCHED2.
+            unfold task_scheduled_on in *.
+            destruct (sched cpu t); last by done.
+            move: SCHED1 SCHED2 => /eqP SCHED1 /eqP SCHED2.
+            by rewrite -SCHED1 -SCHED2.
+          }
+        }
+        {
+          rewrite -(WORK j t) // -count_predT.       
+          apply leq_trans with (n := count (fun j: JobIn arr_seq => scheduled_task_other_than tsk (job_task j)) (jobs_scheduled_at sched t));
+            last first.
+          {
+            rewrite -count_map.
+            apply count_sub_uniqr;
+              last by red; move => tsk' /mapP [j' _ JOBtsk']; subst; apply FROMTS.
+            rewrite map_inj_in_uniq; first by apply scheduled_jobs_uniq.
+            red; intros j1 j2 SCHED1 SCHED2 SAMEtsk.
+            rewrite 2!mem_scheduled_jobs_eq_scheduled in SCHED1 SCHED2.
+            apply scheduled_implies_pending with (job_cost0 := job_cost) in SCHED1; try (by done).
+            apply scheduled_implies_pending with (job_cost0 := job_cost) in SCHED2; try (by done).
+            by apply UNIQ.
+          }
+          {
+            apply sub_in_count; intros j' SCHED' _.
+            rewrite mem_scheduled_jobs_eq_scheduled in SCHED'.
+            unfold scheduled_task_other_than; apply/andP; split.
+            {
+              move: SCHED' => /existsP [cpu /eqP SCHED'].
+              by apply/existsP; exists cpu; rewrite /task_scheduled_on SCHED' eq_refl.
+            }
+            {
+              apply/eqP; red; intro SAMEtsk; symmetry in SAMEtsk.
+              move: BACK => /andP [PENDING NOTSCHED].
+              generalize SCHED'; intro PENDING'.
+              apply scheduled_implies_pending with (job_cost0 := job_cost) in PENDING'; try (by done).
+              exploit (UNIQ j j' PENDING PENDING'); [by rewrite -SAMEtsk | intro EQjob; subst].
+              by rewrite SCHED' in NOTSCHED.
+            }
+          }
+        }
+      Qed.
+      
+    End NoMultipleJobs.
+
+    (* In this section we also prove the absence of multiple jobs of the same
+       task when constrained deadlines are assumed, but in the specific case
+       of fixed-priority scheduling.  *)
+    Section NoMultipleJobsFP.
 
       (* Assume any work-conserving priority-based scheduler. *)
       Variable higher_eq_priority: FP_policy sporadic_task.
@@ -40,12 +203,10 @@ Module PlatformFP.
       Hypothesis H_enforces_JLDP_policy:
         enforces_FP_policy job_cost job_task sched higher_eq_priority.
 
-      (* Consider any task set ts ... *)
+      (* Consider any task set ts. *)
       Variable ts: taskset_of sporadic_task.
 
-      (* ... that has no duplicate tasks ... *)
-      Hypothesis H_ts_is_a_set: uniq ts.
-      (* ... and such that all jobs come from the taskset. *)
+      (* Assume that all jobs come from the taskset. *)
       Hypothesis H_all_jobs_from_taskset:
         forall (j: JobIn arr_seq), job_task j \in ts.
 
@@ -75,13 +236,14 @@ Module PlatformFP.
       Hypothesis H_j_backlogged: backlogged job_cost sched j t.
       Hypothesis H_t_before_period: t < job_arrival j + task_period tsk.
 
-      Let can_interfere_with_tsk := fp_can_interfere_with higher_eq_priority tsk.
+      (* Recall the definition of a higher-priority task (with respect to tsk). *)
+      Let is_hp_task := higher_priority_task higher_eq_priority tsk.
 
       (* Assume that any jobs of higher-priority tasks complete by their period. *)
       Hypothesis H_all_previous_jobs_completed :
         forall (j_other: JobIn arr_seq) tsk_other,
           job_task j_other = tsk_other ->
-          can_interfere_with_tsk tsk_other ->
+          is_hp_task tsk_other ->
           completed job_cost sched j_other (job_arrival j_other + task_period tsk_other).
 
       (* Assume that any jobs of tsk prior to j complete by their period. *)
@@ -93,7 +255,7 @@ Module PlatformFP.
       
       Definition scheduled_task_with_higher_eq_priority (tsk tsk_other: sporadic_task) :=
         task_is_scheduled job_task sched tsk_other t &&
-        can_interfere_with_tsk tsk_other.
+        is_hp_task tsk_other.
                              
       (* Then, there can be at most one pending job of higher-priority tasks at time t. *)
       Lemma platform_fp_no_multiple_jobs_of_interfering_tasks :
@@ -101,7 +263,7 @@ Module PlatformFP.
             pending job_cost sched j1 t ->
             pending job_cost sched j2 t ->
             job_task j1 = job_task j2 ->
-            can_interfere_with_tsk (job_task j1) ->
+            is_hp_task (job_task j1) ->
             j1 = j2.
       Proof.
         unfold sporadic_task_model in *.
@@ -192,17 +354,16 @@ Module PlatformFP.
         apply work_conserving_eq_work_conserving_count in WORK.
         unfold valid_sporadic_job, valid_realtime_job,
                enforces_FP_policy, enforces_JLDP_policy, FP_to_JLDP,
-               task_precedence_constraints, completed_jobs_dont_execute,
+               completed_jobs_dont_execute, sequential_jobs,
                sporadic_task_model, is_valid_sporadic_task,
                jobs_of_same_task_dont_execute_in_parallel,
-               sequential_jobs,
-               can_interfere_with_tsk in *.
+               is_hp_task in *.
         apply/eqP; rewrite eqn_leq; apply/andP; split.
         {
           apply leq_trans with (n := count (fun x => task_is_scheduled job_task sched x t) ts);
             first by apply sub_count; red; move => x /andP [SCHED _].
           unfold task_is_scheduled.
-          apply count_exists; first by done.
+          apply count_exists; first by destruct ts.
           {
             intros cpu x1 x2 SCHED1 SCHED2.
             unfold task_scheduled_on in *.
@@ -261,9 +422,9 @@ Module PlatformFP.
           }
         }
       Qed.
-
-    End JobInvariantAsTaskInvariant.
-
+      
+    End NoMultipleJobsFP.
+    
   End Lemmas.
-  
-End PlatformFP.
\ No newline at end of file
+
+End ConstrainedDeadlines.
\ No newline at end of file
diff --git a/model/basic/interference.v b/model/basic/interference.v
index 9cf6985e57ad2de5d835d72b9ce3e130f3f7def3..b1d1031be6dcf2125c03b11d3cbad1d344f592fd 100644
--- a/model/basic/interference.v
+++ b/model/basic/interference.v
@@ -7,6 +7,7 @@ Module Interference.
 
   Import Schedule ScheduleOfSporadicTask Priority Workload.
 
+  (* In this section, we define the notion of a possible interfering task. *)
   Section PossibleInterferingTasks.
 
     Context {sporadic_task: eqType}.
@@ -19,19 +20,31 @@ Module Interference.
       (* Assume an FP policy. *)
       Variable higher_eq_priority: FP_policy sporadic_task.
 
-      (* Under constrained deadlines, tsk_other can only interfere with tsk
-         if it's a different task with higher or equal priority. *)
-      Definition fp_can_interfere_with (tsk tsk_other: sporadic_task) :=
-        higher_eq_priority tsk_other tsk && (tsk_other != tsk).
+      (* Let tsk be the task to be analyzed ... *)
+      Variable tsk: sporadic_task.
+
+      (* ...and let tsk_other be another task. *)
+      Variable tsk_other: sporadic_task.
+
+      (* Under FP scheduling with constrained deadlines, tsk_other can only interfere
+         with tsk if it is a different task with higher priority. *)
+      Definition higher_priority_task :=
+        higher_eq_priority tsk_other tsk &&
+        (tsk_other != tsk).
 
     End FP.
 
     Section JLFP.
 
-      (* Under JLFP/JLDP policies, any two different tasks can interfere with
-         each other. *)
-      Definition jldp_can_interfere_with (tsk tsk_other: sporadic_task) :=
-        tsk_other != tsk.
+      (* Let tsk be the task to be analyzed ... *)
+      Variable tsk: sporadic_task.
+
+      (* ...and let tsk_other be another task. *)
+      Variable tsk_other: sporadic_task.
+
+      (* Under JLFP/JLDP scheduling with constrained deadlines, tsk_other can only interfere
+         with tsk if it is a different task. *)
+      Definition different_task := tsk_other != tsk.
 
     End JLFP.
     
@@ -58,22 +71,24 @@ Module Interference.
     Let job_is_backlogged (t: time) :=
       backlogged job_cost sched j t.
 
+    (* First, we define total interference. *)
     Section TotalInterference.
       
-      (* First, we define the total interference incurred by job j during [t1, t2)
-         as the cumulative time in which j is backlogged in this interval. *)
+      (* The total interference incurred by job j during [t1, t2) is the
+         cumulative time in which j is backlogged in this interval. *)
       Definition total_interference (t1 t2: time) :=
         \sum_(t1 <= t < t2) job_is_backlogged t.
 
     End TotalInterference.
-    
+
+    (* Next, we define job interference. *)    
     Section JobInterference.
 
       (* Let job_other be a job that interferes with j. *)
       Variable job_other: JobIn arr_seq.
 
-      (* We define the total interference caused by job_other during [t1, t2) as the
-         cumulative service received by job_other while j is backlogged. *)
+      (* The interference caused by job_other during [t1, t2) is the cumulative
+         time in which j is backlogged while job_other is scheduled. *)
       Definition job_interference (t1 t2: time) :=
         \sum_(t1 <= t < t2)
           \sum_(cpu < num_cpus)
@@ -81,13 +96,14 @@ Module Interference.
 
     End JobInterference.
 
+    (* Next, we define task interference. *)
     Section TaskInterference.
 
       (* In order to define task interference, consider any interfering task tsk_other. *)
       Variable tsk_other: sporadic_task.
       
-      (* We define the total interference caused by tsk during [t1, t2) as
-         the cumulative service received by tsk while j is backlogged. *)
+      (* The interference caused by tsk during [t1, t2) is the cumulative time
+         in which j is backlogged while tsk is scheduled. *)
       Definition task_interference (t1 t2: time) :=
         \sum_(t1 <= t < t2)
           \sum_(cpu < num_cpus)
@@ -96,6 +112,8 @@ Module Interference.
 
     End TaskInterference.
 
+    (* Next, we define an approximation of the total interference based on
+       each per-task interference. *)
     Section TaskInterferenceJobList.
 
       Variable tsk_other: sporadic_task.
@@ -106,6 +124,7 @@ Module Interference.
 
     End TaskInterferenceJobList.
 
+    (* Now we prove some basic lemmas about interference. *)
     Section BasicLemmas.
 
       (* Interference cannot be larger than the considered time window. *)
@@ -119,6 +138,7 @@ Module Interference.
         by rewrite big_const_nat iter_addn mul1n addn0 leqnn.
       Qed.
 
+      (* Job interference is bounded by the service of the interfering job. *)
       Lemma job_interference_le_service :
         forall j_other t1 t2,
           job_interference j_other t1 t2 <= service_during sched j_other t1 t2.
@@ -131,6 +151,7 @@ Module Interference.
         by destruct (scheduled_on sched j_other cpu t).
       Qed.
       
+      (* Task interference is bounded by the workload of the interfering task. *)
       Lemma task_interference_le_workload :
         forall tsk t1 t2,
           task_interference tsk t1 t2 <= workload job_task sched tsk t1 t2.
@@ -145,6 +166,7 @@ Module Interference.
 
     End BasicLemmas.
 
+    (* Now we prove some bounds on interference for sequential jobs. *)
     Section InterferenceSequentialJobs.
 
       (* If jobs are sequential, ... *)
@@ -183,7 +205,8 @@ Module Interference.
 
     End InterferenceSequentialJobs.
 
-    (* The sequential per-job interference bounds the actual interference. *)    
+    (* Next, we show that the cumulative per-task interference bounds the total
+       interference. *)
     Section BoundUsingPerJobInterference.
       
       Lemma interference_le_interference_joblist :
diff --git a/model/basic/platform.v b/model/basic/platform.v
index f89ff7697319f22f16b75e399f151216fb51334d..d7e31764f77f5bf1f974b71113cff6bc0c7f1386 100644
--- a/model/basic/platform.v
+++ b/model/basic/platform.v
@@ -7,7 +7,7 @@ Module Platform.
 
   Import Job SporadicTaskset Schedule ScheduleOfSporadicTask SporadicTaskset SporadicTaskArrival Interference Priority.
 
-  Section SchedulingInvariants.
+  Section Properties.
     
     Context {sporadic_task: eqType}.
     Variable task_cost: sporadic_task -> time.
@@ -26,7 +26,7 @@ Module Platform.
     Context {num_cpus: nat}.
     Variable sched: schedule num_cpus arr_seq.
 
-    Section WorkConserving.
+    Section Execution.
 
       (* A scheduler is work-conserving iff when a job j is backlogged,
          all processors are busy with other jobs. *)
@@ -43,7 +43,7 @@ Module Platform.
           backlogged job_cost sched j t ->
           size (jobs_scheduled_at sched t) = num_cpus.
       
-    End WorkConserving.
+    End Execution.
 
     Section JLDP.
 
@@ -154,170 +154,11 @@ Module Platform.
             }
           }
         Qed.
-          
+
       End EquivalentDefinitions.
       
-      Section JobInvariantAsTaskInvariant.
-
-        (* Assume any work-conserving priority-based scheduler. *)
-        Variable higher_eq_priority: JLDP_policy arr_seq.
-        Hypothesis H_work_conserving: work_conserving.
-        Hypothesis H_enforces_JLDP_policy: enforces_JLDP_policy higher_eq_priority.
-                   
-        (* Consider task set ts. *)
-        Variable ts: taskset_of sporadic_task.
-
-        (* Assume the task set has no duplicates, ... *)
-        Hypothesis H_ts_is_a_set: uniq ts.
-        (* ... and all jobs come from the taskset. *)
-        Hypothesis H_all_jobs_from_taskset:
-          forall (j: JobIn arr_seq), job_task j \in ts.
-
-        (* Suppose that jobs are sequential, ...*)
-        Hypothesis H_sequential_jobs: sequential_jobs sched.
-        (* ... jobs must arrive to execute, ... *)
-        Hypothesis H_completed_jobs_dont_execute:
-          completed_jobs_dont_execute job_cost sched.
-        (* ... and jobs do not execute after completion. *)
-        Hypothesis H_jobs_must_arrive_to_execute:
-          jobs_must_arrive_to_execute sched.
-
-        (* Assume that the schedule satisfies the sporadic task model ...*)
-        Hypothesis H_sporadic_tasks:
-          sporadic_task_model task_period arr_seq job_task.
-
-        (* Consider a valid task tsk, ...*)
-        Variable tsk: sporadic_task.
-        Hypothesis H_valid_task: is_valid_sporadic_task task_cost task_period task_deadline tsk.
-
-        (*... whose job j ... *)
-        Variable j: JobIn arr_seq.
-        Variable H_job_of_tsk: job_task j = tsk.
-
-        (*... is backlogged at time t. *)
-        Variable t: time.
-        Hypothesis H_j_backlogged: backlogged job_cost sched j t.
-
-        (* Assume that any previous jobs of tsk have completed by the period. *)
-        Hypothesis H_all_previous_jobs_completed :
-          forall (j_other: JobIn arr_seq) tsk_other,
-            job_task j_other = tsk_other ->
-            job_arrival j_other + task_period tsk_other <= t ->
-            completed job_cost sched j_other (job_arrival j_other + task_period (job_task j_other)).
-
-        Let scheduled_task_other_than (tsk tsk_other: sporadic_task) :=
-          task_is_scheduled job_task sched tsk_other t && (tsk_other != tsk).
-
-        (* Then, there can be at most one pending job of each task at time t. *)
-        Lemma platform_at_most_one_pending_job_of_each_task :
-          forall j1 j2,
-            pending job_cost sched j1 t ->
-            pending job_cost sched j2 t ->
-            job_task j1 = job_task j2 ->
-            j1 = j2.
-        Proof.
-          rename H_sporadic_tasks into SPO, H_all_previous_jobs_completed into PREV.
-          intros j1 j2 PENDING1 PENDING2 SAMEtsk.
-          apply/eqP; rewrite -[_ == _]negbK; apply/negP; red; move => /eqP DIFF. 
-          move: PENDING1 PENDING2 => /andP [ARRIVED1 /negP NOTCOMP1] /andP [ARRIVED2 /negP NOTCOMP2].
-          destruct (leqP (job_arrival j1) (job_arrival j2)) as [BEFORE1 | BEFORE2].
-          {
-            specialize (SPO j1 j2 DIFF SAMEtsk BEFORE1).
-            exploit (PREV j1 (job_task j1));
-              [by done | by apply leq_trans with (n := job_arrival j2) | intros COMP1].
-            apply NOTCOMP1.
-            apply completion_monotonic with (t0 := job_arrival j1 + task_period (job_task j1));
-              try (by done).
-            by apply leq_trans with (n := job_arrival j2). 
-          }
-          {
-            apply ltnW in BEFORE2.
-            exploit (SPO j2 j1); [by red; ins; subst | by rewrite SAMEtsk | by done | intro SPO'].
-            exploit (PREV j2 (job_task j2));
-              [by done | by apply leq_trans with (n := job_arrival j1) | intros COMP2].
-            apply NOTCOMP2.
-            apply completion_monotonic with (t0 := job_arrival j2 + task_period (job_task j2));
-              try (by done).
-            by apply leq_trans with (n := job_arrival j1).
-          }
-        Qed.
-
-        (* Therefore, all processors are busy with tasks other than tsk. *)
-        Lemma platform_cpus_busy_with_interfering_tasks :      
-          count (scheduled_task_other_than tsk) ts = num_cpus.
-        Proof.
-          have UNIQ := platform_at_most_one_pending_job_of_each_task.
-          rename H_all_jobs_from_taskset into FROMTS,
-                 H_sequential_jobs into SEQUENTIAL,
-                 H_work_conserving into WORK,
-                 H_enforces_JLDP_policy into PRIO,
-                 H_j_backlogged into BACK,
-                 H_job_of_tsk into JOBtsk,
-                 H_valid_job_parameters into JOBPARAMS,
-                 H_valid_task into TASKPARAMS,
-                 H_all_previous_jobs_completed into PREV,
-                 H_completed_jobs_dont_execute into COMP,
-                 H_jobs_must_arrive_to_execute into ARRIVE.
-          apply work_conserving_eq_work_conserving_count in WORK.
-          unfold valid_sporadic_job, valid_realtime_job,
-                 enforces_JLDP_policy,
-                 task_precedence_constraints, completed_jobs_dont_execute,
-                 sporadic_task_model, is_valid_sporadic_task,
-                 jobs_of_same_task_dont_execute_in_parallel,
-                 sequential_jobs in *.  
-          apply/eqP; rewrite eqn_leq; apply/andP; split.
-          {
-            apply leq_trans with (n := count (fun x => task_is_scheduled job_task sched x t) ts);
-              first by apply sub_count; first by red; move => x /andP [SCHED _].    
-            unfold task_is_scheduled.
-            apply count_exists; first by done.
-            {
-              intros cpu x1 x2 SCHED1 SCHED2.
-              unfold task_scheduled_on in *.
-              destruct (sched cpu t); last by done.
-              move: SCHED1 SCHED2 => /eqP SCHED1 /eqP SCHED2.
-              by rewrite -SCHED1 -SCHED2.
-            }
-          }
-          {
-            rewrite -(WORK j t) // -count_predT.       
-            apply leq_trans with (n := count (fun j: JobIn arr_seq => scheduled_task_other_than tsk (job_task j)) (jobs_scheduled_at sched t));
-              last first.
-            {
-              rewrite -count_map.
-              apply count_sub_uniqr;
-                last by red; move => tsk' /mapP [j' _ JOBtsk']; subst; apply FROMTS.
-              rewrite map_inj_in_uniq; first by apply scheduled_jobs_uniq.
-              red; intros j1 j2 SCHED1 SCHED2 SAMEtsk.
-              rewrite 2!mem_scheduled_jobs_eq_scheduled in SCHED1 SCHED2.
-              apply scheduled_implies_pending with (job_cost0 := job_cost) in SCHED1; try (by done).
-              apply scheduled_implies_pending with (job_cost0 := job_cost) in SCHED2; try (by done).
-              by apply UNIQ.
-            }
-            {
-              apply sub_in_count; intros j' SCHED' _.
-              rewrite mem_scheduled_jobs_eq_scheduled in SCHED'.
-              unfold scheduled_task_other_than; apply/andP; split.
-              {
-                move: SCHED' => /existsP [cpu /eqP SCHED'].
-                by apply/existsP; exists cpu; rewrite /task_scheduled_on SCHED' eq_refl.
-              }
-              {
-                apply/eqP; red; intro SAMEtsk; symmetry in SAMEtsk.
-                move: BACK => /andP [PENDING NOTSCHED].
-                generalize SCHED'; intro PENDING'.
-                apply scheduled_implies_pending with (job_cost0 := job_cost) in PENDING'; try (by done).
-                exploit (UNIQ j j' PENDING PENDING'); [by rewrite -SAMEtsk | intro EQjob; subst].
-                by rewrite SCHED' in NOTSCHED.
-              }
-            }
-          }
-        Qed.
-
-      End JobInvariantAsTaskInvariant.
-
     End Lemmas.
 
-  End SchedulingInvariants.
+  End Properties.
   
 End Platform.
\ No newline at end of file
diff --git a/model/basic/priority.v b/model/basic/priority.v
index b96753c05474719c6d5b4354753c4c827ee9bda6..f2898b5181c5e023ea646d48cb8a5ac37cc7062e 100644
--- a/model/basic/priority.v
+++ b/model/basic/priority.v
@@ -6,151 +6,185 @@ Set Implicit Arguments.
 (* Definitions of FP and JLFP/JLDP priority relations. *)
 Module Priority.
 
-  Import SporadicTask Schedule.
+  Import SporadicTaskset Schedule.
 
   Section PriorityDefs.
 
-    (* Assume a given job arrival sequence. *)
-    Context {Job: eqType}.
-    Variable arr_seq: arrival_sequence Job.
+    Section JLDP.
 
-    Section Definitions.
+      (* Consider any job arrival sequence. *)
+      Context {Job: eqType}.
+      Variable arr_seq: arrival_sequence Job.
       
-      (* In the following, we define all priority relations as non-strict, i.e., they specify that
-         "job_high has higher priority than (or the same priority as) job_low". *)
-
-      (* A JLDP policy is a generic relation between two jobs of an arrival sequence
-         that can vary with time. *)
+      (* We define a JLDP policy as a relation between jobs in the
+         arrival sequence at each point in time.
+         Although this definition doesn't specify how the policy was
+         constructed (e.g., whether it depends on the schedule, on
+         job parameters, etc.), it is as general as possible.
+         Knowing the priority of the jobs is sufficient to make
+         scheduling decisions. *)
       Definition JLDP_policy := time -> JobIn arr_seq -> JobIn arr_seq -> bool.
 
-      (* FP policy is simply a relation between tasks.
-         Because our model of processor platform is based on a generic JLDP policy,
-         we generate a JLDP policy from an FP policy whenever required. *)
-      Variable sporadic_task: eqType.
-      Definition FP_policy := sporadic_task -> sporadic_task -> bool.
+    End JLDP.
 
-    End Definitions.
-    
-    Section ValidJLFPPolicy.
+    Section FP.
 
-      Variable is_higher_priority: JLDP_policy.
+      (* Let Task denote any type of task. *)
+      Variable Task: eqType.
 
-      (* A policy is reflexive, since a job has the same priority as itself. *)
-      Definition JLFP_is_reflexive :=
-        forall t, reflexive (is_higher_priority t).
+      (* We define an FP policy as a relation between tasks. *)
+      Definition FP_policy := Task -> Task -> bool.
 
-      (* A policy is transitive. *)
-      Definition JLFP_is_transitive :=
-        forall t, transitive (is_higher_priority t).
-      
-      (* A policy is total, since it must know the priority of any two jobs at any time. *)
-      Definition JLFP_is_total :=
-        forall t, total (is_higher_priority t).
-
-      (* A JLDP policy is valid iff it satisfies the three properties.
-         Note that, for generality, we don't enforce antisymmetry and allow multiple
-         jobs with same priority. *)
-      Definition valid_JLDP_policy :=
-        JLFP_is_reflexive /\ JLFP_is_transitive /\ JLFP_is_total.
-
-    End ValidJLFPPolicy.
-
-    Section MonotonicPriorities.
-
-      Context {sporadic_task: eqType}.
-      Variable job_task: Job -> sporadic_task.
-      Variable higher_eq_priority: JLDP_policy.
-            
-      Definition monotonic_priorities :=
-        forall (j j': JobIn arr_seq) t,
-          job_task j = job_task j' ->
-          job_arrival j < job_arrival j' ->
-          higher_eq_priority t j j'.
-      
-    End MonotonicPriorities.
+    End FP.
     
-    Section ValidFPPolicy.
+  End PriorityDefs.
 
-      Context {sporadic_task: eqType}.
-      
-      Variable is_higher_priority: FP_policy sporadic_task.
+  (* To avoid repeating definitions for JLDP and FP policies,
+     we state everything in terms of JLDP policies.
+     For that, we define a function that converts the relation
+     between tasks to a relation between jobs. *)
+  Section GeneralizeFP.
 
-      Definition FP_is_reflexive := reflexive is_higher_priority.
+    (* Consider any arrival sequence of jobs spawned by tasks. *)
+    Context {Task: eqType}.
+    Context {Job: eqType}.
+    Variable job_task: Job -> Task.
+    Variable arr_seq: arrival_sequence Job.
 
-      Definition FP_is_transitive := transitive is_higher_priority.
-      
-      Definition FP_is_total := total is_higher_priority.
+    (* We convert FP to JLDP policies using the following function. *)
+    Definition FP_to_JLDP (task_hp: FP_policy Task) : JLDP_policy arr_seq :=
+      fun (t: time) (jhigh jlow: JobIn arr_seq) =>
+        task_hp (job_task jhigh) (job_task jlow).
 
-      (* We enforce the same restrictions for FP policy: reflexive, transitive, total. *)
-      Definition valid_FP_policy :=
-        FP_is_reflexive /\ FP_is_transitive /\ FP_is_total.
+  End GeneralizeFP.
 
-    End ValidFPPolicy.
+  (* Next, we define properties of a JLDP policy. *)
+  Section PropertiesJLDP.
 
-  End PriorityDefs.
-  
-  Section RateDeadlineMonotonic.
+    (* Consider any JLDP policy. *)
+    Context {Job: eqType}.
+    Context {arr_seq: arrival_sequence Job}.
+    Variable job_priority: JLDP_policy arr_seq.
 
-    Context {sporadic_task: eqType}.
-    Variable task_period: sporadic_task -> time.
-    Variable task_deadline: sporadic_task -> time.
+    (* Now we define the properties. *)
     
-    (* Rate-Monotonic and Deadline-Monotonic as priority order *)
-    Definition RM (tsk1 tsk2: sporadic_task) := task_period tsk1 <= task_period tsk2.
-
-    Definition DM (tsk1 tsk2: sporadic_task) := task_deadline tsk1 <= task_deadline tsk2.
-
-    (* Rate-Montonic is a valid FP policy. *)
-    Lemma rm_is_valid : valid_FP_policy RM.
-    Proof.
-      unfold valid_FP_policy, FP_is_reflexive, FP_is_transitive, RM;
-        repeat (split; try red); first by ins.
-      by intros x y z XY YZ; apply leq_trans with (n := task_period x).
-      by red; intros tsk1 tsk2; apply/orP; destruct (leqP (task_period tsk1) (task_period tsk2));
-        [by left | by right; apply ltnW].
-    Qed.
-
-    (* Deadline-Monotonic is a valid FP policy. *)
-    Lemma dm_is_valid : valid_FP_policy DM.
-    Proof.
-      unfold valid_FP_policy, FP_is_reflexive, FP_is_transitive, DM;
-      repeat (split; try red); first by ins.
-        by intros x y z; apply leq_trans.
-        by red; intros tsk1 tsk2; apply/orP; destruct (leqP (task_deadline tsk1) (task_deadline tsk2));
-          [by left | by right; apply ltnW].
-    Qed.
-
-  End RateDeadlineMonotonic.
+    (* Whether the JLDP policy is reflexive. *)
+    Definition JLDP_is_reflexive :=
+      forall t, reflexive (job_priority t).
 
-  Section GeneralizeFP.
+    (* Whether the JLDP policy is irreflexive. *)
+    Definition JLDP_is_irreflexive :=
+      forall t, irreflexive (job_priority t).
 
-    Context {sporadic_task: eqType}.
+    (* Whether the JLDP policy is transitive. *)
+    Definition JLDP_is_transitive :=
+      forall t, transitive (job_priority t).
+
+    (* Whether the JLDP policy is total. *)
+    Definition JLDP_is_total :=
+      forall t, total (job_priority t).
+
+  End PropertiesJLDP.
+
+  (* Next we define properties of an FP policy. *)
+  Section PropertiesFP.
+
+    (* Assume that jobs are spawned by tasks. *)
     Context {Job: eqType}.
-    Variable job_task: Job -> sporadic_task.
+    Context {Task: eqType}.
+    Variable job_task: Job -> Task.
+
+    (* Consider any job arrival sequence... *)
     Variable arr_seq: arrival_sequence Job.
-    Variable num_cpus: nat.
 
-    (* We define a function to get from FP to JLDP policy. *)
-    Definition FP_to_JLDP (task_hp: FP_policy sporadic_task) : JLDP_policy arr_seq :=
-      fun (t: time) (jhigh jlow: JobIn arr_seq) =>
-        task_hp (job_task jhigh) (job_task jlow).
+    (* ...and let task_priority be any FP policy. *)
+    Variable task_priority: FP_policy Task.
 
-    (* With this function, from a valid FP policy comes a valid JLDP policy. *)
-    Lemma valid_FP_is_valid_JLDP :
-      forall task_hp (FP: valid_FP_policy task_hp),
-        valid_JLDP_policy (FP_to_JLDP task_hp).
-    Proof.
-      unfold FP_to_JLDP, valid_FP_policy, valid_JLDP_policy,
-             FP_is_reflexive, FP_is_transitive, FP_is_total,
-             JLFP_is_reflexive, JLFP_is_transitive, JLFP_is_total, reflexive, transitive.
-      ins; destruct FP as [REFL [TRANS TOTAL]]; repeat (split; try red); des; last by ins.
-        by ins; rewrite REFL.
-        by intros sched t y x z; apply TRANS.
-    Qed.
+    (* Now we define the properties. *)
+    
+    (* Whether the FP policy is reflexive. *)
+    Definition FP_is_reflexive := reflexive task_priority.
 
-  End GeneralizeFP.
+    (* Whether the FP policy is irreflexive. *)
+    Definition FP_is_irreflexive := irreflexive task_priority.
+
+    (* Whether the FP policy is transitive. *)
+    Definition FP_is_transitive := transitive task_priority.
+    
+    Section Antisymmetry.
+
+      (* Consider any task set ts. *)
+      Variable ts: taskset_of Task.
+
+      (* First we define whether task set ts is totally ordered with
+         the priority. *)
+      Definition FP_is_total_over_task_set :=
+        total_over_list task_priority ts. 
+      
+      (* Then we define whether an FP policy is antisymmetric over task set ts, i.e.,
+         whether the task set has unique priorities. *)
+      Definition FP_is_antisymmetric_over_task_set :=
+        antisymmetric_over_list task_priority ts. 
+                  
+    End Antisymmetry.
+
+  End PropertiesFP. 
+    
+  (* Next we define some known FP policies. *)
+  Section KnownFPPolicies.
+
+    Context {Job: eqType}.
+    Context {Task: eqType}.
+    Variable task_period: Task -> time.
+    Variable task_deadline: Task -> time.
+    Variable job_task: Job -> Task.
+    
+    (* Rate-monotonic orders tasks by smaller periods. *)
+    Definition RM (tsk1 tsk2: Task) :=
+      task_period tsk1 <= task_period tsk2.
 
-  Section JLFPDefs.
+    (* Deadline-monotonic orders tasks by smaller relative deadlines. *)
+    Definition DM (tsk1 tsk2: Task) :=
+      task_deadline tsk1 <= task_deadline tsk2.
+
+    Section Properties.
+
+      Variable arr_seq: arrival_sequence Job.
+
+      (* RM is reflexive. *)
+      Lemma RM_is_reflexive : FP_is_reflexive RM.
+      Proof.
+        unfold FP_is_reflexive, reflexive, RM.
+        by intros tsk; apply leqnn.
+      Qed.
+
+      (* RM is transitive. *)
+      Lemma RM_is_transitive : FP_is_transitive RM.
+      Proof.
+        unfold FP_is_transitive, transitive, RM.
+        by intros y x z; apply leq_trans.
+      Qed.
+
+      (* DM is reflexive. *)
+      Lemma DM_is_reflexive : FP_is_reflexive DM.
+      Proof.
+        unfold FP_is_reflexive, reflexive, DM.
+        by intros tsk; apply leqnn.
+      Qed.
+
+      (* DM is transitive. *)
+      Lemma DM_is_transitive : FP_is_transitive DM.
+      Proof.
+        unfold FP_is_transitive, transitive, DM.
+        by intros y x z; apply leq_trans.
+      Qed.
+
+    End Properties.
+
+  End KnownFPPolicies.
+
+  (* Next, we define the notion of JLFP policies. *)
+  Section JLFP.
 
     Context {sporadic_task: eqType}.
     Context {Job: eqType}.
@@ -159,48 +193,58 @@ Module Priority.
 
     Variable is_higher_priority: JLDP_policy arr_seq.
 
-    (* JLFP policy is a JLDP policy where the priorities do not vary with time. *)
-    Definition is_JLFP_policy (is_higher_priority: JLDP_policy arr_seq) :=
+    (* We call a policy JLFP iff job priorities do not vary with time. *)
+    Definition is_JLFP_policy :=
       forall j1 j2 t t',
         is_higher_priority t j1 j2 -> is_higher_priority t' j1 j2.
 
-    (* Lemma: every FP policy is a JLFP policy. *)
-    Variable job_task: Job -> sporadic_task.
-    Lemma FP_is_JLFP :
-      forall FP_higher_priority,
-        is_JLFP_policy (FP_to_JLDP job_task FP_higher_priority).
-    Proof.
-      by unfold is_JLFP_policy, valid_FP_policy.
-    Qed.
-
-  End JLFPDefs.
+  End JLFP.
 
-  Section EDFDefs.
+  (* In this section, we define known JLFP policies. *)
+  Section KnownJLFPPolicies.
 
     Context {Job: eqType}.
     Variable arr_seq: arrival_sequence Job.
     Variable job_deadline: Job -> time.
-      
+
+    (* Earliest deadline first (EDF) orders jobs by absolute deadlines. *)
     Definition EDF (t: time) (j1 j2: JobIn arr_seq) :=
       job_arrival j1 + job_deadline j1 <= job_arrival j2 + job_deadline j2.
 
-    (* Lemma: EDF is a JLFP policy. *)
-    Lemma edf_JLFP : is_JLFP_policy EDF. 
-    Proof.
-      by unfold is_JLFP_policy, EDF.
-    Qed.
-
-    Lemma edf_valid_policy : valid_JLDP_policy EDF.
-    Proof.
-      unfold valid_JLDP_policy, EDF, JLFP_is_reflexive, JLFP_is_transitive, reflexive, transitive.
-      repeat (split; try red).
-        by ins; apply leqnn.
-        by intros sched t y x z; apply leq_trans.
-        by intros _; red; intros j1 j2; apply/orP;
-          destruct (leqP (job_arrival j1 + job_deadline j1) (job_arrival j2 + job_deadline j2));
-            [by left | by right; apply ltnW].
-    Qed.
-
-  End EDFDefs.
+    Section Properties.
+      
+      (* EDF is a JLFP policy. *)
+      Lemma edf_JLFP : is_JLFP_policy EDF. 
+      Proof.
+        by unfold is_JLFP_policy, EDF.
+      Qed.
+
+      (* EDF is reflexive. *)
+      Lemma EDF_is_reflexive : JLDP_is_reflexive EDF.
+      Proof.
+        unfold JLDP_is_reflexive, reflexive.
+        by intros t j; apply leqnn.
+      Qed.
+
+      (* EDF is transitive. *)
+      Lemma EDF_is_transitive : JLDP_is_transitive EDF.
+      Proof.
+        unfold JLDP_is_transitive, transitive.
+        by intros t y x z; apply leq_trans.
+      Qed.
+
+      (* EDF is total. *)
+      Lemma EDF_is_total : JLDP_is_total EDF.
+      Proof.
+        unfold JLDP_is_total, total, EDF.
+        intros _ x y.
+        by case (leqP (job_arrival x + job_deadline x)
+                      (job_arrival y + job_deadline y));
+          [by rewrite orTb | by move/ltnW => ->].
+      Qed.
+
+    End Properties.
+
+  End KnownJLFPPolicies.
 
 End Priority.
\ No newline at end of file
diff --git a/model/basic/schedule.v b/model/basic/schedule.v
index 47cfa1baa78ea77d0324bebb0074efb3650547f7..ec938aab7da97b2e05f6df984b5e7ab427c07262 100644
--- a/model/basic/schedule.v
+++ b/model/basic/schedule.v
@@ -561,18 +561,11 @@ Module ScheduleOfSporadicTask.
     Context {num_cpus: nat}.
     Variable sched: schedule num_cpus arr_seq.
 
-    (* Next we define intra-task parallelism, ... *)
+    (* Next we define intra-task parallelism. *)
     Definition jobs_of_same_task_dont_execute_in_parallel :=
       forall (j j': JobIn arr_seq) t,
         job_task j = job_task j' ->
         scheduled sched j t -> scheduled sched j' t -> j = j'.
-
-    (* ... and task precedence constraints. *)
-    Definition task_precedence_constraints :=
-      forall (j j': JobIn arr_seq) t,
-        job_task j = job_task j' ->
-        job_arrival j < job_arrival j' ->
-        pending job_cost sched j t -> ~~ scheduled sched j' t.
     
   End ScheduleProperties.
 
diff --git a/model/basic/task.v b/model/basic/task.v
index df65a6d58a6d3af7539050109ff51b0781064369..f6c3f0d85476e414753236f43d63f5b3cc6cf934 100644
--- a/model/basic/task.v
+++ b/model/basic/task.v
@@ -41,10 +41,11 @@ Module SporadicTaskset.
 
   Section TasksetDefs.
 
-    (* A task set is just a sequence of tasks. *)
-    Definition taskset_of (Task: eqType) := seq Task.
+    (* A task set is defined as a {set ...}, which denotes a sequence
+       of tasks with no duplicates. *)
+    Definition taskset_of (Task: eqType) := {set Task}.
 
-    (* Next, we define some properties of a task set. *)
+    (* Next, we define some properties abouts sequences of tasks. *)
     Section TasksetProperties.
 
       Context {Task: eqType}.
@@ -55,7 +56,7 @@ Module SporadicTaskset.
       Let is_valid_task :=
         is_valid_sporadic_task task_cost task_period task_deadline.
 
-      Variable ts: taskset_of Task.
+      Variable ts: seq Task.
 
       (* A valid sporadic taskset only contains valid tasks. *)
       Definition valid_sporadic_taskset :=
diff --git a/model/jitter/platform_fp.v b/model/jitter/constrained_deadlines.v
similarity index 55%
rename from model/jitter/platform_fp.v
rename to model/jitter/constrained_deadlines.v
index e330c42bc3a9f46ca6f73feb18cedfe4891317f1..807164d5f742e4380e40abeb5831c037a691ee20 100644
--- a/model/jitter/platform_fp.v
+++ b/model/jitter/constrained_deadlines.v
@@ -1,16 +1,16 @@
 Require Import rt.util.all.
 Require Import rt.model.jitter.task rt.model.jitter.job rt.model.jitter.schedule
-               rt.model.jitter.task_arrival rt.model.jitter.interference
-               rt.model.jitter.priority rt.model.jitter.platform.
+               rt.model.jitter.priority rt.model.jitter.task_arrival
+               rt.model.jitter.interference rt.model.jitter.platform.
 From mathcomp Require Import ssreflect ssrbool ssrfun eqtype ssrnat seq fintype bigop.
 
-Module PlatformFP.
+Module ConstrainedDeadlines.
 
   Import Job SporadicTaskset ScheduleOfSporadicTaskWithJitter SporadicTaskset
          SporadicTaskArrival Interference Priority Platform.
-
+  
   Section Lemmas.
-    
+
     Context {sporadic_task: eqType}.
     Variable task_cost: sporadic_task -> time.
     Variable task_period: sporadic_task -> time.
@@ -22,19 +22,189 @@ Module PlatformFP.
     Variable job_task: Job -> sporadic_task.
     Variable job_jitter: Job -> time.
     
-    (* Assume any job arrival sequence... *)
+    (* Assume any job arrival sequence ... *)
     Context {arr_seq: arrival_sequence Job}.
 
-    (* Consider any schedule. *)
+    (* ... and any schedule of this arrival sequence. *)
     Context {num_cpus: nat}.
     Variable sched: schedule num_cpus arr_seq.
-    
+
     (* Assume all jobs have valid parameters, ...*)
     Hypothesis H_valid_job_parameters:
       forall (j: JobIn arr_seq),
         valid_sporadic_job task_cost task_deadline job_cost job_deadline job_task j.
 
-    Section JobInvariantAsTaskInvariant.
+    (* In this section we prove the absence of multiple jobs of the same
+       task when constrained deadlines are assumed.  *)
+    Section NoMultipleJobs.
+
+        (* Assume any work-conserving priority-based scheduler. *)
+        Variable higher_eq_priority: JLDP_policy arr_seq.
+        Hypothesis H_work_conserving:
+          work_conserving job_cost job_jitter sched.
+        Hypothesis H_enforces_JLDP_policy:
+          enforces_JLDP_policy job_cost job_jitter sched higher_eq_priority.
+        
+        (* Consider task set ts. *)
+        Variable ts: taskset_of sporadic_task.
+
+        (* Assume that all jobs come from the taskset. *)
+        Hypothesis H_all_jobs_from_taskset:
+          forall (j: JobIn arr_seq), job_task j \in ts.
+
+        (* Suppose that jobs are sequential, ...*)
+        Hypothesis H_sequential_jobs: sequential_jobs sched.
+        (* ... jobs only execute after the jitter, ... *)
+        Hypothesis H_jobs_execute_after_jitter:
+          jobs_execute_after_jitter job_jitter sched.
+        (* ... and jobs do not execute after completion. *)
+        Hypothesis H_completed_jobs_dont_execute:
+          completed_jobs_dont_execute job_cost sched.
+        
+        (* Assume that the schedule satisfies the sporadic task model ...*)
+        Hypothesis H_sporadic_tasks:
+          sporadic_task_model task_period arr_seq job_task.
+
+        (* Consider a valid task tsk, ...*)
+        Variable tsk: sporadic_task.
+        Hypothesis H_valid_task: is_valid_sporadic_task task_cost task_period task_deadline tsk.
+
+        (*... whose job j ... *)
+        Variable j: JobIn arr_seq.
+        Variable H_job_of_tsk: job_task j = tsk.
+
+        (*... is backlogged at time t. *)
+        Variable t: time.
+        Hypothesis H_j_backlogged: backlogged job_cost job_jitter sched j t.
+
+        (* Assume that any previous jobs of tsk have completed by the period. *)
+        Hypothesis H_all_previous_jobs_completed :
+          forall (j_other: JobIn arr_seq) tsk_other,
+            job_task j_other = tsk_other ->
+            job_arrival j_other + task_period tsk_other <= t ->
+            completed job_cost sched j_other (job_arrival j_other + task_period (job_task j_other)).
+
+        Let scheduled_task_other_than (tsk tsk_other: sporadic_task) :=
+          task_is_scheduled job_task sched tsk_other t && (tsk_other != tsk).
+
+        (* Then, there can be at most one pending job of each task at time t. *)
+        Lemma platform_at_most_one_pending_job_of_each_task :
+          forall j1 j2,
+            pending job_cost job_jitter sched j1 t ->
+            pending job_cost job_jitter sched j2 t ->
+            job_task j1 = job_task j2 ->
+            j1 = j2.
+        Proof.
+          rename H_sporadic_tasks into SPO, H_all_previous_jobs_completed into PREV.
+          intros j1 j2 PENDING1 PENDING2 SAMEtsk.
+          apply/eqP; rewrite -[_ == _]negbK; apply/negP; red; move => /eqP DIFF. 
+          move: PENDING1 PENDING2 => /andP [ARRIVED1 /negP NOTCOMP1] /andP [ARRIVED2 /negP NOTCOMP2].
+          unfold jitter_has_passed, actual_arrival in *.
+          destruct (leqP (job_arrival j1) (job_arrival j2)) as [BEFORE1 | BEFORE2].
+          {
+            specialize (SPO j1 j2 DIFF SAMEtsk BEFORE1).
+            assert (LEt: job_arrival j1 + task_period (job_task j1) <= t).
+            {
+              apply leq_trans with (n := job_arrival j2); first by done.
+              by apply leq_trans with (n := job_arrival j2 + job_jitter j2); first by apply leq_addr.
+            }
+            exploit (PREV j1 (job_task j1)); try (by done).
+            intros COMP1; apply NOTCOMP1.
+            by apply completion_monotonic with (t0 := job_arrival j1 + task_period (job_task j1)). 
+          }
+          {
+            apply ltnW in BEFORE2.
+            exploit (SPO j2 j1); [by red; ins; subst | by rewrite SAMEtsk | by done | intro SPO'].
+            assert (LEt: job_arrival j2 + task_period (job_task j2) <= t).
+            {
+              apply leq_trans with (n := job_arrival j1); first by done.
+              by apply leq_trans with (n := job_arrival j1 + job_jitter j1); first by apply leq_addr.
+            }
+            exploit (PREV j2 (job_task j2)); try (by done).
+            intros COMP2; apply NOTCOMP2.
+            by apply completion_monotonic with (t0 := job_arrival j2 + task_period (job_task j2)).
+          }
+        Qed.
+
+        (* Therefore, all processors are busy with tasks other than tsk. *)
+        Lemma platform_cpus_busy_with_interfering_tasks :      
+          count (scheduled_task_other_than tsk) ts = num_cpus.
+        Proof.
+          have UNIQ := platform_at_most_one_pending_job_of_each_task.
+          rename H_all_jobs_from_taskset into FROMTS,
+                 H_sequential_jobs into SEQUENTIAL,
+                 H_work_conserving into WORK,
+                 H_enforces_JLDP_policy into PRIO,
+                 H_j_backlogged into BACK,
+                 H_job_of_tsk into JOBtsk,
+                 H_valid_job_parameters into JOBPARAMS,
+                 H_valid_task into TASKPARAMS,
+                 H_all_previous_jobs_completed into PREV,
+                 H_completed_jobs_dont_execute into COMP,
+                 H_jobs_execute_after_jitter into JITTER.
+          apply work_conserving_eq_work_conserving_count in WORK.
+          unfold valid_sporadic_job, valid_realtime_job,
+                 enforces_JLDP_policy, completed_jobs_dont_execute,
+                 sporadic_task_model, is_valid_sporadic_task,
+                 jobs_of_same_task_dont_execute_in_parallel,
+                 sequential_jobs in *.  
+          apply/eqP; rewrite eqn_leq; apply/andP; split.
+          {
+            apply leq_trans with (n := count (fun x => task_is_scheduled job_task sched x t) ts);
+              first by apply sub_count; first by red; move => x /andP [SCHED _].    
+            unfold task_is_scheduled.
+            apply count_exists; first by destruct ts.
+            {
+              intros cpu x1 x2 SCHED1 SCHED2.
+              destruct (sched cpu t); last by done.
+              move: SCHED1 SCHED2 => /eqP SCHED1 /eqP SCHED2.
+              by rewrite -SCHED1 -SCHED2.
+            }
+          }
+          {
+            rewrite -(WORK j t) // -count_predT.       
+            apply leq_trans with (n := count (fun j: JobIn arr_seq => scheduled_task_other_than tsk (job_task j)) (jobs_scheduled_at sched t));
+              last first.
+            {
+              rewrite -count_map.
+              apply count_sub_uniqr;
+                last by red; move => tsk' /mapP [j' _ JOBtsk']; subst; apply FROMTS.
+              rewrite map_inj_in_uniq; first by apply scheduled_jobs_uniq.
+              red; intros j1 j2 SCHED1 SCHED2 SAMEtsk.
+              rewrite 2!mem_scheduled_jobs_eq_scheduled in SCHED1 SCHED2.
+              apply scheduled_implies_pending with (job_cost0 := job_cost)
+                    (job_jitter0 := job_jitter) in SCHED1; try (by done).
+              apply scheduled_implies_pending with (job_cost0 := job_cost)
+                    (job_jitter0 := job_jitter) in SCHED2; try (by done).
+              by apply UNIQ.
+            }
+            {
+              apply sub_in_count; intros j' SCHED' _.
+              rewrite mem_scheduled_jobs_eq_scheduled in SCHED'.
+              unfold scheduled_task_other_than; apply/andP; split.
+              {
+                move: SCHED' => /existsP [cpu /eqP SCHED'].
+                by apply/existsP; exists cpu; rewrite /task_scheduled_on SCHED' eq_refl.
+              }
+              {
+                apply/eqP; red; intro SAMEtsk; symmetry in SAMEtsk.
+                move: BACK => /andP [PENDING NOTSCHED].
+                generalize SCHED'; intro PENDING'.
+                apply scheduled_implies_pending with (job_cost0 := job_cost)
+                      (job_jitter0 := job_jitter) in PENDING'; try (by done).
+                exploit (UNIQ j j' PENDING PENDING'); [by rewrite -SAMEtsk | intro EQjob; subst].
+                by rewrite SCHED' in NOTSCHED.
+              }
+            }
+          }
+        Qed.
+
+    End NoMultipleJobs.
+
+    (* In this section we also prove the absence of multiple jobs of the same
+       task when constrained deadlines are assumed, but in the specific case
+       of fixed-priority scheduling.  *)
+    Section NoMultipleJobsFP.
 
       (* Assume any work-conserving priority-based scheduler. *)
       Variable higher_eq_priority: FP_policy sporadic_task.
@@ -42,12 +212,10 @@ Module PlatformFP.
       Hypothesis H_enforces_JLDP_policy:
         enforces_FP_policy job_cost job_task job_jitter sched higher_eq_priority.
 
-      (* Consider task set ts. *)
+      (* Consider any task set ts. *)
       Variable ts: taskset_of sporadic_task.
 
-      (* Assume the task set has no duplicates, ... *)
-      Hypothesis H_ts_is_a_set: uniq ts.
-      (* ... and all jobs come from the taskset. *)
+      (* Assume that all jobs come from the taskset. *)
       Hypothesis H_all_jobs_from_taskset:
         forall (j: JobIn arr_seq), job_task j \in ts.
 
@@ -77,13 +245,14 @@ Module PlatformFP.
       Hypothesis H_j_backlogged: backlogged job_cost job_jitter sched j t.
       Hypothesis H_t_before_period: t < job_arrival j + task_period tsk.
 
-      Let can_interfere_with_tsk := fp_can_interfere_with higher_eq_priority tsk.
+      (* Recall the definition of a higher-priority task (with respect to tsk). *)
+      Let is_hp_task := higher_priority_task higher_eq_priority tsk.
 
       (* Assume that any jobs of higher-priority tasks complete by their period. *)
       Hypothesis H_all_previous_jobs_completed :
         forall (j_other: JobIn arr_seq) tsk_other,
           job_task j_other = tsk_other ->
-          can_interfere_with_tsk tsk_other ->
+          is_hp_task tsk_other ->
           completed job_cost sched j_other (job_arrival j_other + task_period tsk_other).
 
       (* Assume that any jobs of tsk prior to j complete by their period. *)
@@ -93,9 +262,9 @@ Module PlatformFP.
           job_arrival j0 < job_arrival j ->
           completed job_cost sched j0 (job_arrival j0 + task_period tsk).
       
-      Definition scheduled_task_with_higher_eq_priority (tsk tsk_other: sporadic_task) :=
+      Definition scheduled_task_with_higher_eq_priority (tsk_other: sporadic_task) :=
         task_is_scheduled job_task sched tsk_other t &&
-        can_interfere_with_tsk tsk_other.
+        is_hp_task tsk_other.
                              
       (* Then, there can be at most one pending job of higher-priority tasks at time t. *)
       Lemma platform_fp_no_multiple_jobs_of_interfering_tasks :
@@ -103,7 +272,7 @@ Module PlatformFP.
             pending job_cost job_jitter sched j1 t ->
             pending job_cost job_jitter sched j2 t ->
             job_task j1 = job_task j2 ->
-            can_interfere_with_tsk (job_task j1) ->
+            is_hp_task (job_task j1) ->
             j1 = j2.
       Proof.
         unfold sporadic_task_model in *.
@@ -183,7 +352,7 @@ Module PlatformFP.
       
       (* Therefore, all processors are busy with tasks other than tsk. *)
       Lemma platform_fp_cpus_busy_with_interfering_tasks :      
-        count (scheduled_task_with_higher_eq_priority tsk) ts = num_cpus.
+        count scheduled_task_with_higher_eq_priority ts = num_cpus.
       Proof.
         have UNIQ := platform_fp_no_multiple_jobs_of_interfering_tasks.
         have UNIQ' := platform_fp_no_multiple_jobs_of_tsk. 
@@ -203,17 +372,16 @@ Module PlatformFP.
         apply work_conserving_eq_work_conserving_count in WORK.
         unfold valid_sporadic_job, valid_realtime_job,
                enforces_FP_policy, enforces_JLDP_policy, FP_to_JLDP,
-               task_precedence_constraints, completed_jobs_dont_execute,
+               completed_jobs_dont_execute, sequential_jobs,
                sporadic_task_model, is_valid_sporadic_task,
                jobs_of_same_task_dont_execute_in_parallel,
-               sequential_jobs,
-               can_interfere_with_tsk in *.
+               is_hp_task in *.
         apply/eqP; rewrite eqn_leq; apply/andP; split.
         {
           apply leq_trans with (n := count (fun x => task_is_scheduled job_task sched x t) ts);
             first by apply sub_count; red; move => x /andP [SCHED _].
           unfold task_is_scheduled.
-          apply count_exists; first by done.
+          apply count_exists; first by destruct ts.
           {
             intros cpu x1 x2 SCHED1 SCHED2.
             destruct (sched cpu t); last by done.
@@ -224,13 +392,13 @@ Module PlatformFP.
         {
           rewrite -(WORK j t) // -count_predT.
           apply leq_trans with (n := count (fun j: JobIn arr_seq =>
-            scheduled_task_with_higher_eq_priority tsk (job_task j)) (jobs_scheduled_at sched t));
+            scheduled_task_with_higher_eq_priority (job_task j)) (jobs_scheduled_at sched t));
             last first.
           {
             rewrite -count_map.
-            apply leq_trans with (n := count predT [seq x <- (map (fun (j: JobIn arr_seq) => job_task j) (jobs_scheduled_at sched t)) | scheduled_task_with_higher_eq_priority tsk x]);
+            apply leq_trans with (n := count predT [seq x <- (map (fun (j: JobIn arr_seq) => job_task j) (jobs_scheduled_at sched t)) | scheduled_task_with_higher_eq_priority x]);
               first  by rewrite count_filter; apply sub_count; red; ins.
-            apply leq_trans with (n := count predT [seq x <- ts | scheduled_task_with_higher_eq_priority tsk x]);
+            apply leq_trans with (n := count predT [seq x <- ts | scheduled_task_with_higher_eq_priority x]);
               last by rewrite count_predT size_filter.
             apply count_sub_uniqr;
               last first.
@@ -274,9 +442,9 @@ Module PlatformFP.
           }
         }
       Qed.
-
-    End JobInvariantAsTaskInvariant.
-
+      
+    End NoMultipleJobsFP.
+    
   End Lemmas.
-  
-End PlatformFP.
\ No newline at end of file
+
+End ConstrainedDeadlines.
\ No newline at end of file
diff --git a/model/jitter/interference.v b/model/jitter/interference.v
index 63dca9f75da1e18564d34a73523a09f0a3274e4b..f6ee8eed753bbd64ce44462c9a5e6388345a13e6 100644
--- a/model/jitter/interference.v
+++ b/model/jitter/interference.v
@@ -36,22 +36,24 @@ Module Interference.
     Let job_is_backlogged (t: time) :=
       backlogged job_cost job_jitter sched j t.
 
+    (* First, we define total interference. *)
     Section TotalInterference.
       
-      (* First, we define the total interference incurred by job j during [t1, t2)
-         as the cumulative time in which j is backlogged in this interval. *)
+      (* The total interference incurred by job j during [t1, t2) is the
+         cumulative time in which j is backlogged in this interval. *)
       Definition total_interference (t1 t2: time) :=
         \sum_(t1 <= t < t2) job_is_backlogged t.
 
     End TotalInterference.
     
+    (* Next, we define job interference. *)
     Section JobInterference.
 
       (* Let job_other be a job that interferes with j. *)
       Variable job_other: JobIn arr_seq.
 
-      (* We define the total interference caused by job_other during [t1, t2) as the
-         cumulative service received by job_other while j is backlogged. *)
+      (* The interference caused by job_other during [t1, t2) is the cumulative
+         time in which j is backlogged while job_other is scheduled. *)
       Definition job_interference (t1 t2: time) :=
         \sum_(t1 <= t < t2)
           \sum_(cpu < num_cpus)
@@ -60,13 +62,14 @@ Module Interference.
 
     End JobInterference.
     
+    (* Next, we define task interference. *)
     Section TaskInterference.
 
       (* In order to define task interference, consider any interfering task tsk_other. *)
       Variable tsk_other: sporadic_task.
     
-      (* We define the total interference caused by tsk during [t1, t2) as
-         the cumulative service received by tsk while j is backlogged. *)
+      (* The interference caused by tsk during [t1, t2) is the cumulative time
+         in which j is backlogged while tsk is scheduled. *)
       Definition task_interference (t1 t2: time) :=
         \sum_(t1 <= t < t2)
           \sum_(cpu < num_cpus)
@@ -75,6 +78,8 @@ Module Interference.
 
     End TaskInterference.
 
+    (* Next, we define an approximation of the total interference based on
+       each per-task interference. *)
     Section TaskInterferenceJobList.
 
       Variable tsk_other: sporadic_task.
@@ -85,6 +90,7 @@ Module Interference.
 
     End TaskInterferenceJobList.
 
+    (* Now we prove some basic lemmas about interference. *)
     Section BasicLemmas.
 
       (* Interference cannot be larger than the considered time window. *)
@@ -98,6 +104,7 @@ Module Interference.
         by rewrite big_const_nat iter_addn mul1n addn0 leqnn.
       Qed.
 
+      (* Job interference is bounded by the service of the interfering job. *)
       Lemma job_interference_le_service :
         forall j_other t1 t2,
           job_interference j_other t1 t2 <= service_during sched j_other t1 t2.
@@ -110,6 +117,7 @@ Module Interference.
         by destruct (scheduled_on sched j_other cpu t).
       Qed.
       
+      (* Task interference is bounded by the workload of the interfering task. *)
       Lemma task_interference_le_workload :
         forall tsk t1 t2,
           task_interference tsk t1 t2 <= workload job_task sched tsk t1 t2.
@@ -124,6 +132,7 @@ Module Interference.
 
     End BasicLemmas.
 
+    (* Now we prove some bounds on interference for sequential jobs. *)
     Section InterferenceSequentialJobs.
 
       (* If jobs are sequential, ... *)
@@ -162,7 +171,8 @@ Module Interference.
 
     End InterferenceSequentialJobs.
 
-    (* The sequential per-job interference bounds the actual interference. *)    
+    (* Next, we show that the cumulative per-task interference bounds the total
+       interference. *)
     Section BoundUsingPerJobInterference.
       
       Lemma interference_le_interference_joblist :
diff --git a/model/jitter/platform.v b/model/jitter/platform.v
index ad283f800cb28ee2bf9cabd7a450bbe7046675c3..cb916f90809d4cb0f54a88461af86936c0bf55bc 100644
--- a/model/jitter/platform.v
+++ b/model/jitter/platform.v
@@ -7,7 +7,7 @@ Module Platform.
 
   Import Job SporadicTaskset ScheduleOfSporadicTaskWithJitter SporadicTaskset SporadicTaskArrival Interference Priority.
 
-  Section SchedulingInvariants.
+  Section Properties.
     
     Context {sporadic_task: eqType}.
     Variable task_cost: sporadic_task -> time.
@@ -27,11 +27,11 @@ Module Platform.
     Context {num_cpus: nat}.
     Variable sched: schedule num_cpus arr_seq.
 
-    Section WorkConserving.
+    Section Execution.
 
       (* A scheduler is work-conserving iff when a job j is backlogged,
          all processors are busy with other jobs.
-         NOTE: backlogged means that jitter has already passed. *)
+         Note: backlogged means that jitter has already passed. *)
       Definition work_conserving :=
         forall j t,
           backlogged job_cost job_jitter sched j t ->
@@ -45,7 +45,7 @@ Module Platform.
           backlogged job_cost job_jitter sched j t ->
           size (jobs_scheduled_at sched t) = num_cpus.
 
-    End WorkConserving.
+    End Execution.
 
     Section JLDP.
 
@@ -159,174 +159,8 @@ Module Platform.
           
       End EquivalentDefinitions.
 
-      Section JobInvariantAsTaskInvariant.
-
-        (* Assume any work-conserving priority-based scheduler. *)
-        Variable higher_eq_priority: JLDP_policy arr_seq.
-        Hypothesis H_work_conserving: work_conserving.
-        Hypothesis H_enforces_JLDP_policy: enforces_JLDP_policy higher_eq_priority.
-                   
-        (* Consider task set ts. *)
-        Variable ts: taskset_of sporadic_task.
-
-        (* Assume the task set has no duplicates, ... *)
-        Hypothesis H_ts_is_a_set: uniq ts.
-        (* ... and all jobs come from the taskset. *)
-        Hypothesis H_all_jobs_from_taskset:
-          forall (j: JobIn arr_seq), job_task j \in ts.
-
-        (* Suppose that jobs are sequential, ...*)
-        Hypothesis H_sequential_jobs: sequential_jobs sched.
-        (* ... jobs only execute after the jitter, ... *)
-        Hypothesis H_jobs_execute_after_jitter:
-          jobs_execute_after_jitter job_jitter sched.
-        (* ... and jobs do not execute after completion. *)
-        Hypothesis H_completed_jobs_dont_execute:
-          completed_jobs_dont_execute job_cost sched.
-        
-        (* Assume that the schedule satisfies the sporadic task model ...*)
-        Hypothesis H_sporadic_tasks:
-          sporadic_task_model task_period arr_seq job_task.
-
-        (* Consider a valid task tsk, ...*)
-        Variable tsk: sporadic_task.
-        Hypothesis H_valid_task: is_valid_sporadic_task task_cost task_period task_deadline tsk.
-
-        (*... whose job j ... *)
-        Variable j: JobIn arr_seq.
-        Variable H_job_of_tsk: job_task j = tsk.
-
-        (*... is backlogged at time t. *)
-        Variable t: time.
-        Hypothesis H_j_backlogged: backlogged job_cost job_jitter sched j t.
-
-        (* Assume that any previous jobs of tsk have completed by the period. *)
-        Hypothesis H_all_previous_jobs_completed :
-          forall (j_other: JobIn arr_seq) tsk_other,
-            job_task j_other = tsk_other ->
-            job_arrival j_other + task_period tsk_other <= t ->
-            completed job_cost sched j_other (job_arrival j_other + task_period (job_task j_other)).
-
-        Let scheduled_task_other_than (tsk tsk_other: sporadic_task) :=
-          task_is_scheduled job_task sched tsk_other t && (tsk_other != tsk).
-
-        (* Then, there can be at most one pending job of each task at time t. *)
-        Lemma platform_at_most_one_pending_job_of_each_task :
-          forall j1 j2,
-            pending job_cost job_jitter sched j1 t ->
-            pending job_cost job_jitter sched j2 t ->
-            job_task j1 = job_task j2 ->
-            j1 = j2.
-        Proof.
-          rename H_sporadic_tasks into SPO, H_all_previous_jobs_completed into PREV.
-          intros j1 j2 PENDING1 PENDING2 SAMEtsk.
-          apply/eqP; rewrite -[_ == _]negbK; apply/negP; red; move => /eqP DIFF. 
-          move: PENDING1 PENDING2 => /andP [ARRIVED1 /negP NOTCOMP1] /andP [ARRIVED2 /negP NOTCOMP2].
-          unfold jitter_has_passed, actual_arrival in *.
-          destruct (leqP (job_arrival j1) (job_arrival j2)) as [BEFORE1 | BEFORE2].
-          {
-            specialize (SPO j1 j2 DIFF SAMEtsk BEFORE1).
-            assert (LEt: job_arrival j1 + task_period (job_task j1) <= t).
-            {
-              apply leq_trans with (n := job_arrival j2); first by done.
-              by apply leq_trans with (n := job_arrival j2 + job_jitter j2); first by apply leq_addr.
-            }
-            exploit (PREV j1 (job_task j1)); try (by done).
-            intros COMP1; apply NOTCOMP1.
-            by apply completion_monotonic with (t0 := job_arrival j1 + task_period (job_task j1)). 
-          }
-          {
-            apply ltnW in BEFORE2.
-            exploit (SPO j2 j1); [by red; ins; subst | by rewrite SAMEtsk | by done | intro SPO'].
-            assert (LEt: job_arrival j2 + task_period (job_task j2) <= t).
-            {
-              apply leq_trans with (n := job_arrival j1); first by done.
-              by apply leq_trans with (n := job_arrival j1 + job_jitter j1); first by apply leq_addr.
-            }
-            exploit (PREV j2 (job_task j2)); try (by done).
-            intros COMP2; apply NOTCOMP2.
-            by apply completion_monotonic with (t0 := job_arrival j2 + task_period (job_task j2)).
-          }
-        Qed.
-
-        (* Therefore, all processors are busy with tasks other than tsk. *)
-        Lemma platform_cpus_busy_with_interfering_tasks :      
-          count (scheduled_task_other_than tsk) ts = num_cpus.
-        Proof.
-          have UNIQ := platform_at_most_one_pending_job_of_each_task.
-          rename H_all_jobs_from_taskset into FROMTS,
-                 H_sequential_jobs into SEQUENTIAL,
-                 H_work_conserving into WORK,
-                 H_enforces_JLDP_policy into PRIO,
-                 H_j_backlogged into BACK,
-                 H_job_of_tsk into JOBtsk,
-                 H_valid_job_parameters into JOBPARAMS,
-                 H_valid_task into TASKPARAMS,
-                 H_all_previous_jobs_completed into PREV,
-                 H_completed_jobs_dont_execute into COMP,
-                 H_jobs_execute_after_jitter into JITTER.
-          apply work_conserving_eq_work_conserving_count in WORK.
-          unfold valid_sporadic_job, valid_realtime_job,
-                 enforces_JLDP_policy,
-                 task_precedence_constraints, completed_jobs_dont_execute,
-                 sporadic_task_model, is_valid_sporadic_task,
-                 jobs_of_same_task_dont_execute_in_parallel,
-                 sequential_jobs in *.  
-          apply/eqP; rewrite eqn_leq; apply/andP; split.
-          {
-            apply leq_trans with (n := count (fun x => task_is_scheduled job_task sched x t) ts);
-              first by apply sub_count; first by red; move => x /andP [SCHED _].    
-            unfold task_is_scheduled.
-            apply count_exists; first by done.
-            {
-              intros cpu x1 x2 SCHED1 SCHED2.
-              destruct (sched cpu t); last by done.
-              move: SCHED1 SCHED2 => /eqP SCHED1 /eqP SCHED2.
-              by rewrite -SCHED1 -SCHED2.
-            }
-          }
-          {
-            rewrite -(WORK j t) // -count_predT.       
-            apply leq_trans with (n := count (fun j: JobIn arr_seq => scheduled_task_other_than tsk (job_task j)) (jobs_scheduled_at sched t));
-              last first.
-            {
-              rewrite -count_map.
-              apply count_sub_uniqr;
-                last by red; move => tsk' /mapP [j' _ JOBtsk']; subst; apply FROMTS.
-              rewrite map_inj_in_uniq; first by apply scheduled_jobs_uniq.
-              red; intros j1 j2 SCHED1 SCHED2 SAMEtsk.
-              rewrite 2!mem_scheduled_jobs_eq_scheduled in SCHED1 SCHED2.
-              apply scheduled_implies_pending with (job_cost0 := job_cost)
-                    (job_jitter0 := job_jitter) in SCHED1; try (by done).
-              apply scheduled_implies_pending with (job_cost0 := job_cost)
-                    (job_jitter0 := job_jitter) in SCHED2; try (by done).
-              by apply UNIQ.
-            }
-            {
-              apply sub_in_count; intros j' SCHED' _.
-              rewrite mem_scheduled_jobs_eq_scheduled in SCHED'.
-              unfold scheduled_task_other_than; apply/andP; split.
-              {
-                move: SCHED' => /existsP [cpu /eqP SCHED'].
-                by apply/existsP; exists cpu; rewrite /task_scheduled_on SCHED' eq_refl.
-              }
-              {
-                apply/eqP; red; intro SAMEtsk; symmetry in SAMEtsk.
-                move: BACK => /andP [PENDING NOTSCHED].
-                generalize SCHED'; intro PENDING'.
-                apply scheduled_implies_pending with (job_cost0 := job_cost)
-                      (job_jitter0 := job_jitter) in PENDING'; try (by done).
-                exploit (UNIQ j j' PENDING PENDING'); [by rewrite -SAMEtsk | intro EQjob; subst].
-                by rewrite SCHED' in NOTSCHED.
-              }
-            }
-          }
-        Qed.
-
-      End JobInvariantAsTaskInvariant.
-
     End Lemmas.
 
-  End SchedulingInvariants.
+  End Properties.
   
 End Platform.
\ No newline at end of file
diff --git a/model/jitter/schedule.v b/model/jitter/schedule.v
index a78068ba480014d8ebe99f9019d99bc54eb23d90..fdb9016714391c3cb6d65981d98d4cb1d0c78139 100644
--- a/model/jitter/schedule.v
+++ b/model/jitter/schedule.v
@@ -195,18 +195,11 @@ Module ScheduleOfSporadicTaskWithJitter.
     Context {num_cpus: nat}.
     Variable sched: schedule num_cpus arr_seq.
 
-    (* Next we define intra-task parallelism, ... *)
+    (* Next we define intra-task parallelism. *)
     Definition jobs_of_same_task_dont_execute_in_parallel :=
       forall (j j': JobIn arr_seq) t,
         job_task j = job_task j' ->
         scheduled sched j t -> scheduled sched j' t -> j = j'.
-
-    (* ... and task precedence constraints. *)
-    Definition task_precedence_constraints :=
-      forall (j j': JobIn arr_seq) t,
-        job_task j = job_task j' ->
-        job_arrival j < job_arrival j' ->
-        pending job_cost job_jitter sched j t -> ~~ scheduled sched j' t.
     
   End ScheduleProperties.
 
diff --git a/util/exists.v b/util/exists.v
index 7dd564da3724ad6cd7a3d5d3c9ae8fdcdf1a5857..1376b4dfb1714ea766e572bc199a2d854c8861f7 100644
--- a/util/exists.v
+++ b/util/exists.v
@@ -51,3 +51,22 @@ Section OrdExists.
 
 End OrdExists.
 
+(*Section Computation.
+
+  Variable m: nat.
+  Hypothesis H_m_positive: m > 0.
+
+  Variable P: 'I_m -> bool.
+
+  Program Fixpoint compute_exists n (Q: n < m) :=
+    match n with
+    | 0 => P (@Ordinal m 0 _)
+    | S n => P (@Ordinal m (S n) _) || compute_exists n _
+    end.
+
+  Program Definition compute_exists_ordinal := compute_exists (m.-1) _.
+  Next Obligation.
+    by rewrite -(ltn_add2r 1) 2!addn1 prednK //.
+  Qed.
+
+End Computation.*)
\ No newline at end of file
diff --git a/util/seqset.v b/util/seqset.v
index fb734c841989a720f968347ea170812c64e67ff8..af3a5ffe58cdce34d40a023835ec1decf499d8a3 100644
--- a/util/seqset.v
+++ b/util/seqset.v
@@ -33,6 +33,11 @@ Section Lemmas.
     by destruct s.
   Qed.
 
+  Lemma set_mem : forall x, (x \in s) = (x \in _set_seq s).
+  Proof.
+    by intros x; destruct s.
+  Qed.
+  
 End Lemmas.
 
 Section LemmasFinType.
diff --git a/util/sorting.v b/util/sorting.v
index e28d8003bcaff85a1ac8e26216945d627003a734..30d2f7d89c6190d9fbd9721d3521de5bda5f7413 100644
--- a/util/sorting.v
+++ b/util/sorting.v
@@ -1,4 +1,4 @@
-Require Import rt.util.tactics rt.util.induction.
+Require Import rt.util.tactics rt.util.induction rt.util.list.
 From mathcomp Require Import ssreflect ssrbool eqtype ssrnat seq fintype bigop path.
 
 (* Lemmas about sorted lists. *)
@@ -72,9 +72,10 @@ Section Sorting.
     }
   Qed.
 
-  Lemma sorted_uniq_rel_implies_le_idx :
+  Lemma sorted_rel_implies_le_idx :
     forall {T: eqType} (leT: rel T) (s: seq T) x0 i1 i2,
-      irreflexive leT ->
+      uniq s ->
+      antisymmetric_over_list leT s ->
       transitive leT ->
       sorted leT s ->
       leT (nth x0 s i1) (nth x0 s i2) ->
@@ -82,7 +83,7 @@ Section Sorting.
       i2 < size s ->
       i1 <= i2.
   Proof.
-    intros T leT s x0 i1 i2 IRR TRANS SORT REL SIZE1 SIZE2.
+    intros T leT s x0 i1 i2 UNIQ ANTI TRANS SORT REL SIZE1 SIZE2.
     generalize dependent i2.
     induction i1; first by done.
     {
@@ -92,12 +93,12 @@ Section Sorting.
       rewrite ltn_neqAle; apply/andP; split.
       {
         apply/eqP; red; intro BUG; subst.
-        assert (REL': leT (nth x0 s i2) (nth x0 s i2)).
-        {
-          rewrite /transitive (TRANS (nth x0 s i2.+1)) //.
+        assert (REL': leT (nth x0 s i2) (nth x0 s i2.+1)).
           by apply sorted_lt_idx_implies_rel; rewrite // ltnSn.
-        }
-        by rewrite /irreflexive IRR in REL'.
+        rewrite /antisymmetric_over_list in ANTI.
+        exploit (ANTI (nth x0 s i2) (nth x0 s i2.+1)); rewrite ?mem_nth //.
+        move => /eqP EQ; rewrite nth_uniq in EQ; try (by done).
+        by rewrite -[_ == _]negbK in EQ; move: EQ => /negP EQ; apply EQ; apply/eqP.
       }
       {
         apply IHi1; last by done.