bedrock.lang.cpp.algebra.cfrac
(*
* Copyright (c) 2022 BlueRock Security, Inc.
*
* This software is distributed under the terms of the BedRock Open-Source License.
* See the LICENSE-BedRock file in the repository root for details.
*)
Require Import iris.algebra.cmra.
Require Import iris.algebra.frac.
Require Import bedrock.prelude.bool.
Require Import bedrock.lang.bi.split_frac.
#[local] Set Printing Coercions.
* Copyright (c) 2022 BlueRock Security, Inc.
*
* This software is distributed under the terms of the BedRock Open-Source License.
* See the LICENSE-BedRock file in the repository root for details.
*)
Require Import iris.algebra.cmra.
Require Import iris.algebra.frac.
Require Import bedrock.prelude.bool.
Require Import bedrock.lang.bi.split_frac.
#[local] Set Printing Coercions.
Const/mutable fractions
- Type cQp.t pairs together a positive fraction cQp.frac with a
- cQp.mut (short name cQp.m), cQp.const (cQp.c) inject
- cQp.add combines two const/mutable fractions using && on the
- cQp.scale q scales the fraction in a const/mutable fraction by
- Canonical OFE, CMRA structures for cQp.t
- Module cQp_compat exports a coercion cQp._mut : Qp >-> cQp.t
Declare Scope cQp_scope.
Delimit Scope cQp_scope with cQp.
Module cQp.
#[projections(primitive)]
Record t : Set := mk { is_const : bool ; frac : Qp }.
#[global] Hint Opaque is_const frac : typeclass_instances.
Notation mut := (mk false).
Notation m := (mk false) (only parsing).
Notation const := (mk true).
Notation c := (mk true) (only parsing).
Definition add (cq1 cq2 : t) : t :=
mk (cq1.(is_const) && cq2.(is_const)) (cq1.(frac) + cq2.(frac)).
#[global] Hint Opaque add : typeclass_instances.
Definition scale (q : Qp) (cq : t) : t :=
mk cq.(is_const) (q * cq.(frac)).
#[global] Hint Opaque scale : typeclass_instances.
Module Import notation.
Add Printing Constructor t.
#[global] Bind Scope cQp_scope with t.
Infix "+" := add : cQp_scope.
End notation.
#[local] Open Scope cQp_scope.
#[global] Instance t_inhabited : Inhabited t.
Proof. apply populate, mk; apply inhabitant. Qed.
#[global] Instance t_eq_dec : EqDecision t.
Proof. solve_decision. Defined.
#[global] Instance t_countable : Countable t.
Proof.
apply (inj_countable'
(fun x => (x.(is_const), x.(frac)))
(fun p => mk p.1 p.2)).
abstract (by intros []).
Defined.
Lemma eta q : q = mk q.(is_const) q.(frac).
Proof. done. Qed.
Properties of add
#[global] Instance add_comm : Comm (=) add.
Proof.
rewrite /add. intros x y. f_equal; by rewrite comm_L.
Qed.
#[global] Instance add_assoc : Assoc (=) add.
Proof.
rewrite /add. intros x y z. cbn. by rewrite !assoc_L.
Qed.
Lemma add_eq x y : x + y = Reduce (add x y).
Proof. done. Qed.
Lemma is_const_add x y : is_const (x + y) = is_const x && is_const y.
Proof. done. Qed.
Lemma frac_add x y : frac (x + y) = (frac x + frac y)%Qp.
Proof. done. Qed.
Lemma mk_add' c q1 q2 : mk c (q1 + q2) = mk c q1 + mk c q2.
Proof. rewrite /add /=. by rewrite andb_diag. Qed.
Lemma mk_add `{!split_frac.SplitFrac q q1 q2} c : mk c q = mk c q1 + mk c q2.
Proof. by rewrite (split_frac q) mk_add'. Qed.
Lemma mut_const' q1 q2 : mut (q1 + q2) = mut q1 + const q2.
Proof. done. Qed.
Lemma mut_const `{!split_frac.SplitFrac q q1 q2} : mut q = mut q1 + const q2.
Proof. by rewrite (split_frac q). Qed.
Properties of scale
Lemma scale_eq q cq : scale q cq = Reduce (scale q cq).
Proof. done. Qed.
Lemma is_const_scale q cq : is_const (scale q cq) = is_const cq.
Proof. done. Qed.
Lemma frac_scale q cq : frac (scale q cq) = (q * frac cq)%Qp.
Proof. done. Qed.
Lemma scale_mk c q1 q2 : scale q1 (mk c q2) = mk c (q1 * q2).
Proof. done. Qed.
Lemma scale_mut q1 q2 : scale q1 (mut q2) = mut (q1 * q2).
Proof. done. Qed.
Lemma scale_const q1 q2 : scale q1 (const q2) = const (q1 * q2).
Proof. done. Qed.
Lemma scale_1 cq : scale 1 cq = cq.
Proof. rewrite /scale. by rewrite Qp.mul_1_l. Qed.
Lemma scale_comm q1 q2 q : scale q1 (scale q2 q) = scale q2 (scale q1 q).
Proof. rewrite /scale. cbn. by rewrite !assoc_L [(q1*q2)%Qp]comm_L. Qed.
Lemma scale_mul q1 q2 q : scale (q1 * q2) q = scale q1 (scale q2 q).
Proof. rewrite/scale. cbn. by rewrite assoc_L. Qed.
Lemma scale_add_l q1 q2 q : scale (q1 + q2) q = scale q1 q + scale q2 q.
Proof.
rewrite /scale/add. cbn.
by rewrite Bool.andb_diag Qp.mul_add_distr_r.
Qed.
Lemma scale_add_r q q1 q2 : scale q (q1 + q2) = scale q q1 + scale q q2.
Proof.
rewrite /scale/add. cbn. by rewrite Qp.mul_add_distr_l.
Qed.
Canonical Structure tO : ofe := leibnizO t.
#[global] Instance: Params mk 0 := {}.
#[global] Instance mk_ne : NonExpansive2 mk.
Proof.
intros n. by do 2!(intros ??; rewrite -discrete_iff leibniz_equiv_iff=>->).
Qed.
#[global] Instance mk_proper : Proper (equiv ==> equiv ==> equiv) mk.
Proof. exact: ne_proper_2. Qed.
#[global] Instance: Params is_const 0 := {}.
#[global] Instance is_const_ne : NonExpansive is_const := _.
#[global] Instance is_const_proper : Proper (equiv ==> equiv) is_const := _.
#[global] Instance: Params frac 0 := {}.
#[global] Instance frac_ne : NonExpansive frac := _.
#[global] Instance frac_proper : Proper (equiv ==> equiv) frac := _.
#[global] Instance mk_inj_eq : Inj2 eq eq eq mk.
Proof. by intros ???? [= ->->]. Qed.
#[global] Instance mk_inj_equiv : Inj2 equiv equiv equiv mk.
Proof. intros ????. fold_leibniz. apply mk_inj_eq. Qed.
#[global] Instance mk_inj_dist n : Inj2 (dist n) (dist n) (dist n) mk.
Proof. intros ????. rewrite -!discrete_iff. apply mk_inj_equiv. Qed.
Section cmra.
#[local] Open Scope bool_scope.
Implicit Types (x y : t).
#[local] Instance t_op_instance : Op t := add.
#[local] Instance t_pcore_instance : PCore t := fun x => None.
#[local] Instance t_valid_instance : Valid t := fun x => ✓ x.(frac).
Lemma t_op x y : x ⋅ y = x + y.
Proof. done. Qed.
Lemma t_pcore x : pcore x = None.
Proof. done. Qed.
Lemma t_valid x : ✓ x = ✓ x.(frac).
Proof. done. Qed.
Lemma t_included x y :
x ≼ y <-> y.(is_const) <= x.(is_const) /\ (x.(frac) < y.(frac))%Qp.
Proof.
rewrite -frac_included. split.
- intros [z ->%leibniz_equiv]. cbn. split. done. by exists (frac z).
- intros [? [frac Hfrac]]. exists (mk y.(is_const) frac).
rewrite t_op /add /=.
rewrite Bool.andb_min_r//. by rewrite -frac_op -{}Hfrac.
Qed.
#[global] Instance mk_mono : Proper (flip Bool.le ==> Qp.lt ==> (≼)) mk.
Proof. solve_proper_prepare. by rewrite t_included. Qed.
#[global] Instance is_const_mono : Proper ((≼) ==> flip Bool.le) is_const.
Proof. by intros x y [??]%t_included. Qed.
#[global] Instance frac_mono : Proper ((≼) ==> Qp.lt) frac.
Proof. by intros x y [??]%t_included. Qed.
Lemma t_ra_mixin : RAMixin t.
Proof.
split; first [apply _|done|idtac].
-
ra_valid_op_l
intros x y. rewrite t_op t_valid /=.
exact: cmra_valid_op_l.
Qed.
Canonical Structure tR : cmra := discreteR t t_ra_mixin.
#[global] Instance t_cmra_discrete : CmraDiscrete t.
Proof. split. by apply _. done. Qed.
#[global] Instance t_exclusive b : Exclusive (mk b 1).
Proof.
intros x. rewrite -cmra_discrete_valid_iff t_valid /=.
exact: exclusive_l.
Qed.
#[global] Instance t_id_free x : IdFree x.
Proof.
apply: discrete_id_free=>y. rewrite t_valid=>/= Hv.
rewrite t_op=>/mk_inj_equiv [] _ Heq.
exact: (id_free_r x.(frac)).
Qed.
#[global] Instance t_cancelable q : Cancelable (mk true q).
Proof.
apply: discrete_cancelable=>y z.
rewrite (eta y) !t_op /add /=.
rewrite t_valid=>/= Hv. move=>/mk_inj_equiv [] -> Heq. f_equiv.
exact: (cancelable q).
Qed.
End cmra.
#[local] Definition _refl (c : bool) (q : Qp) : mk c q = mk c q := eq_refl _.
#[deprecated(since="20221223")]
Notation refl := _refl (only parsing).
#[local] Lemma scale_combine' q1 q2 cq :
scale q1 cq + scale q2 cq = scale (q1 + q2) cq.
Proof. by rewrite scale_add_l. Qed.
#[deprecated(since="20230106", note="use [cQp.scale_add_l].")]
Notation scale_combine := scale_combine'.
End cQp.
Export cQp.notation.
Canonical Structure cQp.tO.
Canonical Structure cQp.tR.
(* as with C++, we make mutable the default *)
#[global] Coercion cQp.frac : cQp.t >-> Qp.
exact: cmra_valid_op_l.
Qed.
Canonical Structure tR : cmra := discreteR t t_ra_mixin.
#[global] Instance t_cmra_discrete : CmraDiscrete t.
Proof. split. by apply _. done. Qed.
#[global] Instance t_exclusive b : Exclusive (mk b 1).
Proof.
intros x. rewrite -cmra_discrete_valid_iff t_valid /=.
exact: exclusive_l.
Qed.
#[global] Instance t_id_free x : IdFree x.
Proof.
apply: discrete_id_free=>y. rewrite t_valid=>/= Hv.
rewrite t_op=>/mk_inj_equiv [] _ Heq.
exact: (id_free_r x.(frac)).
Qed.
#[global] Instance t_cancelable q : Cancelable (mk true q).
Proof.
apply: discrete_cancelable=>y z.
rewrite (eta y) !t_op /add /=.
rewrite t_valid=>/= Hv. move=>/mk_inj_equiv [] -> Heq. f_equiv.
exact: (cancelable q).
Qed.
End cmra.
#[local] Definition _refl (c : bool) (q : Qp) : mk c q = mk c q := eq_refl _.
#[deprecated(since="20221223")]
Notation refl := _refl (only parsing).
#[local] Lemma scale_combine' q1 q2 cq :
scale q1 cq + scale q2 cq = scale (q1 + q2) cq.
Proof. by rewrite scale_add_l. Qed.
#[deprecated(since="20230106", note="use [cQp.scale_add_l].")]
Notation scale_combine := scale_combine'.
End cQp.
Export cQp.notation.
Canonical Structure cQp.tO.
Canonical Structure cQp.tR.
(* as with C++, we make mutable the default *)
#[global] Coercion cQp.frac : cQp.t >-> Qp.
Module cQp_compat.
Module cQp.
Export cfrac.cQp.
Definition _mut : Qp -> t := mut.
#[global] Arguments cQp._mut /.
End cQp.
Coercion cQp._mut : Qp >-> cQp.t.
Module cQp.
Export cfrac.cQp.
Definition _mut : Qp -> t := mut.
#[global] Arguments cQp._mut /.
End cQp.
Coercion cQp._mut : Qp >-> cQp.t.
To enable the _mut coercion, we replay some Qp literals and
(unambigous) operations in cQp_scope, which is bound to type
cQp.t.
Notation "1" := 1%Qp (only parsing) : cQp_scope.
Notation "2" := 2%Qp (only parsing) : cQp_scope.
Notation "3" := 3%Qp (only parsing) : cQp_scope.
Notation "4" := 4%Qp (only parsing) : cQp_scope.
Infix "/" := Qp.div (only parsing) : cQp_scope.
Infix "*" := Qp.mul (only parsing) : cQp_scope.
End cQp_compat.
Section TEST.
Variable TEST : cQp.t -> cQp.t -> cQp.t -> cQp.t -> Prop.
Import cQp_compat.
(* TODO: to make this work without the %Qp, we need to register the
notations for Qp as notations in cvq_scope. *)
Goal TEST 1 (cQp.c 1) (1/2) (cQp.c (1/4)).
Abort.
End TEST.
Notation "2" := 2%Qp (only parsing) : cQp_scope.
Notation "3" := 3%Qp (only parsing) : cQp_scope.
Notation "4" := 4%Qp (only parsing) : cQp_scope.
Infix "/" := Qp.div (only parsing) : cQp_scope.
Infix "*" := Qp.mul (only parsing) : cQp_scope.
End cQp_compat.
Section TEST.
Variable TEST : cQp.t -> cQp.t -> cQp.t -> cQp.t -> Prop.
Import cQp_compat.
(* TODO: to make this work without the %Qp, we need to register the
notations for Qp as notations in cvq_scope. *)
Goal TEST 1 (cQp.c 1) (1/2) (cQp.c (1/4)).
Abort.
End TEST.