@@ -10,6 +10,7 @@ open EcSubst
1010
1111open EcMatching.Position
1212open EcCoreGoal
13+ open EcCoreFol
1314open EcLowGoal
1415open EcLowPhlGoal
1516
@@ -442,6 +443,84 @@ module Core = struct
442443 | `Right -> f_equivS (snd es.es_ml) mt (es_pr es) es.es_sl s (es_po es) in
443444 FApi. xmutate1 tc (`RndSem pos) [concl]
444445
446+ (* -------------------------------------------------------------------- *)
447+ let t_equiv_coupling_r side g tc =
448+ (* process the following pRHL goal, where g is a coupling of g1 and g2 *)
449+ (* {phi} c1; x <$ g1 ~ c2; y <$ g2 {psi} *)
450+ let env = FApi. tc1_env tc in
451+ let es = tc1_as_equivS tc in
452+ let ml = fst es.es_ml in
453+ let mr = fst es.es_mr in
454+ let (lvL, muL), sl' = tc1_last_rnd tc es.es_sl in
455+ let (lvR, muR), sr' = tc1_last_rnd tc es.es_sr in
456+ let tyL = proj_distr_ty env (e_ty muL) in
457+ let tyR = proj_distr_ty env (e_ty muR) in
458+ let muL = (EcFol. ss_inv_of_expr ml muL).inv in
459+ let muR = (EcFol. ss_inv_of_expr mr muR).inv in
460+ let g = g.inv in
461+
462+ let goal =
463+ match side with
464+ | None ->
465+ (* Goal: {phi} c1 ~ c2 {iscoupling g muL muR /\ (forall u v, (u, v) \in supp(g) => psi[x -> u, y -> v])} *)
466+ (* Generate two free variables u and v and the pair (u, v) *)
467+ let u_id = EcIdent. create " u" in
468+ let v_id = EcIdent. create " v" in
469+ let u = f_local u_id tyL in
470+ let v = f_local v_id tyR in
471+ let ab = f_tuple [u; v] in
472+
473+ (* Generate the coupling distribution type: (tyL * tyR) distr *)
474+ let coupling_ty = ttuple [tyL; tyR] in
475+ let g_app = f_app_simpl g [] (tdistr coupling_ty) in
476+
477+ let iscoupling_op = EcPath. extend EcCoreLib. p_top [" Distr" ; " iscoupling" ] in
478+ let iscoupling_ty = tfun (tdistr tyL) (tfun (tdistr tyR) (tfun (tdistr coupling_ty) tbool)) in
479+ let iscoupling_pred = f_app (f_op iscoupling_op [tyL; tyR] iscoupling_ty) [muL; muR; g_app] tbool in
480+
481+ (* Substitute in the postcondition *)
482+ let post = (es_po es) in
483+ let post_subst = subst_form_lv_left env lvL {ml= ml;mr= mr;inv= u} post in
484+ let post_subst = subst_form_lv_right env lvR {ml= ml;mr= mr;inv= v} post_subst in
485+
486+ let goal = map_ts_inv1 (f_imp (f_in_supp ab g_app)) post_subst in
487+ let goal = map_ts_inv1 (f_forall_simpl [(u_id, GTty tyL); (v_id, GTty tyR)]) goal in
488+ map_ts_inv1 (f_and iscoupling_pred) goal
489+ | Some side ->
490+ (* Goal (left): {phi} c1 ~ c2 {dmap d1 g = d2 /\
491+ forall u, u \in supp(d1) -> psi[x -> u, y -> g(u)]} *)
492+ (* Goal (right): {phi} c1 ~ c2 {d1 = dmap d2 g /\
493+ forall v, v \in supp(d2) -> psi[x -> g(v), y -> v]} *)
494+ let is_left = (side = `Left ) in
495+ let (mu_main, mu_other) = if is_left then (muL, muR) else (muR, muL) in
496+ let (ty_main, ty_other) = if is_left then (tyL, tyR) else (tyR, tyL) in
497+ let (lv_main, lv_other) = if is_left then (lvL, lvR) else (lvR, lvL) in
498+ let (subst_main, subst_other) =
499+ if is_left then (subst_form_lv_left, subst_form_lv_right)
500+ else (subst_form_lv_right, subst_form_lv_left) in
501+
502+ let dmap_op = EcPath. extend EcCoreLib. p_top [" Distr" ; " dmap" ] in
503+ let dmap_ty = tfun (tdistr ty_main) (tfun (tfun ty_main ty_other) (tdistr ty_other)) in
504+ let dmap_pred = f_app (f_op dmap_op [ty_main; ty_other] dmap_ty) [mu_main ; g] (tdistr ty_other) in
505+ let dmap_eq = f_eq dmap_pred mu_other in
506+
507+ let var_name = if is_left then " u" else " v" in
508+ let var_ty = if is_left then tyL else tyR in
509+ let var_id = EcIdent. create var_name in
510+ let var = f_local var_id var_ty in
511+ let g_applied = f_app_simpl g [var] (if is_left then tyR else tyL) in
512+
513+ let post = es_po es in
514+ let post_subst = subst_main env lv_main {ml;mr;inv= var} post in
515+ let post_subst = subst_other env lv_other {ml;mr;inv= g_applied} post_subst in
516+ let goal = map_ts_inv1 (f_imp (f_in_supp var mu_main)) post_subst in
517+ let goal = map_ts_inv1 (f_forall_simpl [(var_id, GTty var_ty)]) goal in
518+
519+ map_ts_inv1 (f_and dmap_eq) goal
520+ in
521+ let goal = f_equivS (snd es.es_ml) (snd es.es_mr) (es_pr es) sl' sr' goal in
522+
523+ FApi. xmutate1 tc `Rnd [goal]
445524end (* Core *)
446525
447526(* -------------------------------------------------------------------- *)
@@ -713,3 +792,24 @@ let process_rndsem ~reduce side pos tc =
713792 | Some side when is_equivS concl ->
714793 t_equiv_rndsem reduce side pos tc
715794 | _ -> tc_error !! tc " invalid arguments"
795+
796+ let process_coupling side g tc =
797+ let concl = FApi. tc1_goal tc in
798+
799+ if not (is_equivS concl) then
800+ tc_error !! tc " coupling can only be used on pRHL goals"
801+ else
802+ let env = FApi. tc1_env tc in
803+ let es = tc1_as_equivS tc in
804+ let (_, muL), _ = tc1_last_rnd tc es.es_sl in
805+ let (_, muR), _ = tc1_last_rnd tc es.es_sr in
806+ let tyL = proj_distr_ty env (e_ty muL) in
807+ let tyR = proj_distr_ty env (e_ty muR) in
808+
809+ let coupling_ty =
810+ match side with
811+ | None -> tdistr (ttuple [tyL; tyR])
812+ | Some `Left -> tfun tyL tyR
813+ | Some `Right -> tfun tyR tyL in
814+ let g_form = TTC. tc1_process_prhl_form tc coupling_ty g in
815+ Core. t_equiv_coupling_r side g_form tc
0 commit comments