Section CR.
Open Scope recurrence_scope.
Inductive CR :=
| liftBRToCR: BR -> CR
| recurCR: R -> (R -> R -> R) -> CR -> CR
.
(** Interpret a CR as nested BRs as described in section 2 - Chains of Recurrences *)
Fixpoint CR_to_BR (cr: CR): BR :=
match cr with
| liftBRToCR br =>  br
| recurCR r0 bop cr' => mkBR r0 bop (evalAtIx (CR_to_BR cr'))
end.
Definition evalCR(cr: CR) (n: nat): R := (CR_to_BR cr) # n.
(** Define what it means to evaluate a CR *)
Global Instance loopVariantCR : LoopVariant CR R :=
{
evalAtIx (cr: CR) (n: nat) := evalCR cr n
}.
Hint Resolve creval_lift_br_to_cr_inverses: Recurrence.
Hint Rewrite @evalCR_zero: Recurrence.
Hint Rewrite @evalCR_step: Recurrence.
(** Now that we know what happens at 0 and inductive case, we're done *)
Opaque evalCR.
Hint Resolve add_const_to_CR : Recurrence.
Hint Rewrite @add_const_to_CR : Recurrence.
(** Theory of pure CRs (CRs that have only one binary operator *)
Inductive PureCR (bop: R -> R -> R): Type :=
| PureBR: R -> R -> PureCR bop
|  PureCRRec:
R ->
PureCR bop ->
PureCR bop
.
(** Convert a `PureCR` into a CR by inserting the correct `bop` everywhere *)
Fixpoint purecr_to_cr (bop: R -> R -> R) (pure: PureCR bop): CR :=
match pure with
| PureBR r1 r2 => liftBRToCR (mkBR r1 bop (const r2))
| PureCRRec r cr' => (recurCR r bop (purecr_to_cr cr'))
end.
Global Instance loopVariantPureCR (f: R -> R -> R):
LoopVariant (PureCR f) (R : Type) :=
{
evalAtIx (purecr: PureCR f) (n: nat) := (purecr_to_cr purecr) # n
}.
(** Zip the two pureCRs together, to construct a longer PureCR. NOTE: This assumes that cr1 and cr2 have the same length NOTE: If they do not, we can always ammend a CR with {0, +, 0} **)
Fixpoint zip_purecrs_eq_len
{bop: R -> R -> R}
(cr1 cr2: PureCR bop):
option (PureCR bop) :=
match (cr1, cr2) with
(** recurrence *)
| (PureCRRec r1 cr'1, PureCRRec r2 cr'2) =>
option_map (PureCRRec (bop r1 r2)) (zip_purecrs_eq_len cr'1 cr'2)
(** Base case when cr1 and cr2 have the same length *)
| (PureBR r11 r12, PureBR r21 r22) =>
Some (PureBR bop (bop r11 r21) (bop r12 r22))
| _ => None
end.
(* Define pure-sum and pure-product CRs *)
Notation PureSumCR := (PureCR plus).
Notation PureProdCR := (PureCR mult).

(** Lemma 22 *)
Lemma rewrite_pure_sum_cr_on_add_cr: forall
`{Ring R}
(cr1 cr2: PureSumCR)
(pureout: PureSumCR)
(ZIP: zip_purecrs_eq_len cr1 cr2 = Some pureout)
(n: nat),
cr1 # n + cr2 # n = pureout # n.