(* big tactic hopefully proving inequalities *)
Ltac try_prove_ineq := 
unfold qic, cs;
try tauto; intros; subst; cbn; try omega;
repeat rewrite Nat.add_0_r; repeat rewrite Nat.max_0_r; 
repeat rewrite Nat.add_max_distr_r; repeat rewrite Nat.add_1_r;
repeat rewrite Nat.max_id, Max.max_r;
try (solve [simpl; auto with *]);
try (nia); repeat (case QI.value_assignment); auto with *.(* tactic proving 1 inequality *)
Ltac prove_rule H :=
  rewrite cons_app; apply QI.p_smc_QI_app;
  [ intros f lp t s; cbn; intro H;
    repeat (destruct H as [ H | H ];
      [inversion H; subst; unfold map; simpl; 
       repeat rewrite QI.value_as_p_term_assignment;
        eexists; eexists; repeat split; repeat try_prove_ineq| ]); inversion H
  |].Definition spec_QI (f : function) (qf : list nat -> nat) :=
 fun g => if function_beq g f then Some qf else None.Ltac inst f qi := idtac "instantiating" f; apply QI.p_smc_split with (h := spec_QI f qi).(* get next function name and instantiate it *)
Ltac inst_next qi :=
match goal with 
| |- context[rulify (Syntax.fapply ?ff _) _ ::_] => 
    (idtac "instantiating" ff;
    apply QI.p_smc_split with (h := spec_QI ff qi))
end.Ltac qif_monotonic_tac :=
  repeat (apply forall2_le_suml || apply forall2_le_maxl ||
          apply Plus.plus_le_compat_r || apply Plus.plus_le_compat_l || apply Plus.plus_le_compat ||
          apply Nat.max_le_compat_r || apply Nat.max_le_compat_l || apply Nat.max_le_compat ||
          apply Mult.mult_le_compat_l || apply Mult.mult_le_compat_r || apply Mult.mult_le_compat
         ); trivial.Definition qif_proofs : {qif | subterm qif /\ monotonicity_qif qif /\ compatible_QI qic qif}.Definition qif := proj1_sig qif_proofs.Proposition qi_is_valid : valid_QI mcs qic qif cs.

(* Specifications: *)
Theorem polytime: forall i s p c f lv d v, let t := fapply f lv in let pi := cbv_update i s p c t d v in cbv_wf pi -> cache_bounded qic qif c -> cbv_size pi <= global_bound mcs qif f lv c.