@@ -1174,6 +1174,107 @@ let elab_equiv_simplify (ctx : walker_ctx) (s : Alethe.step)
11741174 raise (Walker_error
11751175 " 'equiv_simplify' expects clause (cl (= lhs rhs))" )
11761176
1177+ (* =========================================================
1178+ [equiv_pos1] / [equiv_pos2] (R-10): 3-literal Boolean
1179+ tautologies, no premises. The two positive-polarity halves of
1180+ propositional-equivalence reasoning cvc5 emits alongside the
1181+ R-7 boolean cluster (deferred from R-7 as they are nested
1182+ case-splits). Built by nested [classic] case analysis with the
1183+ R-7/R-8 [eq_mp]/[eq_mpr] transports; footprint [{classic}]
1184+ (the transports go through axiom-free [eq_ind]). Mirror of
1185+ Lean #50's [elabEquivPos1]/[elabEquivPos2].
1186+ ========================================================= *)
1187+
1188+ (* * [equiv_pos1]: clause [(cl (not (= a b)) a (not b))] ≡
1189+ [~(a=b) \/ a \/ ~b]. Nested [classic]: case [a=b]; if not, left
1190+ disjunct. If [a=b], case [b]; if [b], transport to [a] via
1191+ [eq_mpr] for the middle disjunct; if [~b], the right disjunct. *)
1192+ let elab_equiv_pos1 (ctx : walker_ctx ) (s : Alethe.step )
1193+ : EConstr.t * Alethe.Sexp.t list =
1194+ match s.clause with
1195+ | [ List [ Atom " not" ; List [ Atom " =" ; a; b ] ]; a';
1196+ List [ Atom " not" ; b' ] ] ->
1197+ if a <> a' || b <> b' then
1198+ raise (Walker_error " 'equiv_pos1' argument mismatch in clause" );
1199+ let a_e = sexp_to_constr ctx a in
1200+ let b_e = sexp_to_constr ctx b in
1201+ let eq_ab = sexp_to_constr ctx (List [ Atom " =" ; a; b ]) in
1202+ let not_eq = mk_not eq_ab in
1203+ let not_b = mk_not b_e in
1204+ let inner_ty = mk_or a_e not_b in
1205+ let result_ty = mk_or not_eq inner_ty in
1206+ let pos_outer =
1207+ abstract_lam ctx.sigma_ref " eqH" eq_ab (fun eq_h ->
1208+ let pos_inner =
1209+ abstract_lam ctx.sigma_ref " hb" b_e (fun hb ->
1210+ let a_proof = eq_mpr ctx eq_h a_e b_e hb in
1211+ mk_or_intror not_eq inner_ty
1212+ (mk_or_introl a_e not_b a_proof))
1213+ in
1214+ let neg_inner =
1215+ abstract_lam ctx.sigma_ref " hnb" not_b (fun hnb ->
1216+ mk_or_intror not_eq inner_ty
1217+ (mk_or_intror a_e not_b hnb))
1218+ in
1219+ mk_or_ind b_e not_b result_ty pos_inner neg_inner
1220+ (mk_classic b_e))
1221+ in
1222+ let neg_outer =
1223+ abstract_lam ctx.sigma_ref " hne" not_eq (fun hne ->
1224+ mk_or_introl not_eq inner_ty hne)
1225+ in
1226+ (mk_or_ind eq_ab not_eq result_ty pos_outer neg_outer
1227+ (mk_classic eq_ab),
1228+ s.clause)
1229+ | _ ->
1230+ raise (Walker_error
1231+ " 'equiv_pos1' expects clause (cl (not (= a b)) a (not b))" )
1232+
1233+ (* * [equiv_pos2]: clause [(cl (not (= a b)) (not a) b)] ≡
1234+ [~(a=b) \/ ~a \/ b]. Mirror of [equiv_pos1]: if [a=b] and [a]
1235+ holds, transport to [b] via [eq_mp] for the right disjunct; if
1236+ [~a], the middle disjunct. *)
1237+ let elab_equiv_pos2 (ctx : walker_ctx ) (s : Alethe.step )
1238+ : EConstr.t * Alethe.Sexp.t list =
1239+ match s.clause with
1240+ | [ List [ Atom " not" ; List [ Atom " =" ; a; b ] ];
1241+ List [ Atom " not" ; a' ]; b' ] ->
1242+ if a <> a' || b <> b' then
1243+ raise (Walker_error " 'equiv_pos2' argument mismatch in clause" );
1244+ let a_e = sexp_to_constr ctx a in
1245+ let b_e = sexp_to_constr ctx b in
1246+ let eq_ab = sexp_to_constr ctx (List [ Atom " =" ; a; b ]) in
1247+ let not_eq = mk_not eq_ab in
1248+ let not_a = mk_not a_e in
1249+ let inner_ty = mk_or not_a b_e in
1250+ let result_ty = mk_or not_eq inner_ty in
1251+ let pos_outer =
1252+ abstract_lam ctx.sigma_ref " eqH" eq_ab (fun eq_h ->
1253+ let pos_inner =
1254+ abstract_lam ctx.sigma_ref " ha" a_e (fun ha ->
1255+ let b_proof = eq_mp ctx eq_h a_e b_e ha in
1256+ mk_or_intror not_eq inner_ty
1257+ (mk_or_intror not_a b_e b_proof))
1258+ in
1259+ let neg_inner =
1260+ abstract_lam ctx.sigma_ref " hna" not_a (fun hna ->
1261+ mk_or_intror not_eq inner_ty
1262+ (mk_or_introl not_a b_e hna))
1263+ in
1264+ mk_or_ind a_e not_a result_ty pos_inner neg_inner
1265+ (mk_classic a_e))
1266+ in
1267+ let neg_outer =
1268+ abstract_lam ctx.sigma_ref " hne" not_eq (fun hne ->
1269+ mk_or_introl not_eq inner_ty hne)
1270+ in
1271+ (mk_or_ind eq_ab not_eq result_ty pos_outer neg_outer
1272+ (mk_classic eq_ab),
1273+ s.clause)
1274+ | _ ->
1275+ raise (Walker_error
1276+ " 'equiv_pos2' expects clause (cl (not (= a b)) (not a) b)" )
1277+
11771278let elab_false_step (_ctx : walker_ctx ) (s : Alethe.step )
11781279 : EConstr.t * Alethe.Sexp.t list =
11791280 match s.clause with
@@ -1262,15 +1363,18 @@ let elab_step (env : Environ.env) (sigma_ref : Evd.evar_map ref)
12621363 | "and_neg" -> elab_and_neg ctx s
12631364 (* Propositional-equality tautology simplification (R-8). *)
12641365 | "equiv_simplify" -> elab_equiv_simplify ctx s
1366+ (* 3-literal equivalence tautologies (R-10). *)
1367+ | "equiv_pos1" -> elab_equiv_pos1 ctx s
1368+ | "equiv_pos2" -> elab_equiv_pos2 ctx s
12651369 | other ->
12661370 raise (Walker_error
12671371 (Printf. sprintf
1268- " rule '%s' not yet supported (R-8 scope: \
1372+ " rule '%s' not yet supported (R-10 scope: \
12691373 assume / or / resolution (n-ary) / false / \
12701374 la_generic / la_mult_neg / refl / symm / trans / \
12711375 cong / hole / rare_rewrite / implies / equiv1 / \
1272- equiv2 / not_and / and_neg / equiv_simplify; \
1273- subsequent PRs add equiv_pos )"
1376+ equiv2 / not_and / and_neg / equiv_simplify / \
1377+ equiv_pos1 / equiv_pos2 )"
12741378 other))
12751379 in
12761380 store_step st s.id proof clause
0 commit comments