use crate::prelude::*;
use crate::verify::verify_builtin_rules::{
compare_normalized_number_str_to_zero, NumberCompareResult,
};
use crate::verify::verify_number_in_standard_set::is_integer_after_simplification;
use std::rc::Rc;
#[inline]
pub fn objs_equal_by_display_string(a: &Obj, b: &Obj) -> bool {
a.to_string() == b.to_string()
}
pub fn verify_equality_by_they_are_the_same(left: &Obj, right: &Obj) -> bool {
objs_equal_by_display_string(left, right)
}
#[inline]
fn obj_expr_mentions_bare_id_on_two(l: &Obj, r: &Obj, id: &str) -> bool {
obj_expr_mentions_bare_id(l, id) || obj_expr_mentions_bare_id(r, id)
}
fn obj_expr_mentions_bare_id(obj: &Obj, id: &str) -> bool {
match obj {
Obj::Atom(AtomObj::Identifier(i)) => i.name == id,
Obj::Number(_) | Obj::StandardSet(_) => false,
Obj::Add(b) => obj_expr_mentions_bare_id_on_two(b.left.as_ref(), b.right.as_ref(), id),
Obj::Sub(b) => obj_expr_mentions_bare_id_on_two(b.left.as_ref(), b.right.as_ref(), id),
Obj::Mul(b) => obj_expr_mentions_bare_id_on_two(b.left.as_ref(), b.right.as_ref(), id),
Obj::Div(b) => obj_expr_mentions_bare_id_on_two(b.left.as_ref(), b.right.as_ref(), id),
Obj::Mod(b) => obj_expr_mentions_bare_id_on_two(b.left.as_ref(), b.right.as_ref(), id),
Obj::Max(b) => obj_expr_mentions_bare_id_on_two(b.left.as_ref(), b.right.as_ref(), id),
Obj::Min(b) => obj_expr_mentions_bare_id_on_two(b.left.as_ref(), b.right.as_ref(), id),
Obj::Union(b) => obj_expr_mentions_bare_id_on_two(b.left.as_ref(), b.right.as_ref(), id),
Obj::Intersect(b) => obj_expr_mentions_bare_id_on_two(b.left.as_ref(), b.right.as_ref(), id),
Obj::SetMinus(b) => obj_expr_mentions_bare_id_on_two(b.left.as_ref(), b.right.as_ref(), id),
Obj::SetDiff(b) => obj_expr_mentions_bare_id_on_two(b.left.as_ref(), b.right.as_ref(), id),
Obj::MatrixAdd(b) => obj_expr_mentions_bare_id_on_two(b.left.as_ref(), b.right.as_ref(), id),
Obj::MatrixSub(b) => obj_expr_mentions_bare_id_on_two(b.left.as_ref(), b.right.as_ref(), id),
Obj::MatrixMul(b) => obj_expr_mentions_bare_id_on_two(b.left.as_ref(), b.right.as_ref(), id),
Obj::Pow(p) => {
obj_expr_mentions_bare_id(p.base.as_ref(), id)
|| obj_expr_mentions_bare_id(p.exponent.as_ref(), id)
}
Obj::MatrixScalarMul(m) => {
obj_expr_mentions_bare_id(m.scalar.as_ref(), id)
|| obj_expr_mentions_bare_id(m.matrix.as_ref(), id)
}
Obj::MatrixPow(m) => {
obj_expr_mentions_bare_id(m.base.as_ref(), id)
|| obj_expr_mentions_bare_id(m.exponent.as_ref(), id)
}
Obj::Abs(u) => obj_expr_mentions_bare_id(u.arg.as_ref(), id),
Obj::PowerSet(u) => obj_expr_mentions_bare_id(u.set.as_ref(), id),
Obj::Cup(u) => obj_expr_mentions_bare_id(u.left.as_ref(), id),
Obj::Cap(u) => obj_expr_mentions_bare_id(u.left.as_ref(), id),
Obj::Log(l) => {
obj_expr_mentions_bare_id(l.base.as_ref(), id)
|| obj_expr_mentions_bare_id(l.arg.as_ref(), id)
}
Obj::ListSet(list) => list
.list
.iter()
.any(|o| obj_expr_mentions_bare_id(o.as_ref(), id)),
Obj::Tuple(t) => t
.args
.iter()
.any(|o| obj_expr_mentions_bare_id(o.as_ref(), id)),
Obj::Cart(c) => c
.args
.iter()
.any(|o| obj_expr_mentions_bare_id(o.as_ref(), id)),
Obj::Count(c) => obj_expr_mentions_bare_id(c.set.as_ref(), id),
Obj::TupleDim(t) => obj_expr_mentions_bare_id(t.arg.as_ref(), id),
Obj::CartDim(c) => obj_expr_mentions_bare_id(c.set.as_ref(), id),
Obj::Proj(p) => {
obj_expr_mentions_bare_id(p.set.as_ref(), id)
|| obj_expr_mentions_bare_id(p.dim.as_ref(), id)
}
Obj::ObjAtIndex(oi) => {
obj_expr_mentions_bare_id(oi.obj.as_ref(), id)
|| obj_expr_mentions_bare_id(oi.index.as_ref(), id)
}
Obj::Range(r) => obj_expr_mentions_bare_id_on_two(r.start.as_ref(), r.end.as_ref(), id),
Obj::ClosedRange(r) => obj_expr_mentions_bare_id_on_two(r.start.as_ref(), r.end.as_ref(), id),
Obj::Sum(s) => {
obj_expr_mentions_bare_id(s.start.as_ref(), id)
|| obj_expr_mentions_bare_id(s.end.as_ref(), id)
|| obj_expr_mentions_bare_id(s.func.as_ref(), id)
}
Obj::Product(p) => {
obj_expr_mentions_bare_id(p.start.as_ref(), id)
|| obj_expr_mentions_bare_id(p.end.as_ref(), id)
|| obj_expr_mentions_bare_id(p.func.as_ref(), id)
}
Obj::FiniteSeqListObj(f) => f
.objs
.iter()
.any(|o| obj_expr_mentions_bare_id(o.as_ref(), id)),
Obj::MatrixListObj(m) => m.rows.iter().any(|row| {
row.iter()
.any(|o| obj_expr_mentions_bare_id(o.as_ref(), id))
}),
Obj::Choose(ch) => obj_expr_mentions_bare_id(ch.set.as_ref(), id),
Obj::FamilyObj(fo) => fo
.params
.iter()
.any(|p| obj_expr_mentions_bare_id(p, id)),
Obj::FiniteSeqSet(fs) => {
obj_expr_mentions_bare_id(fs.set.as_ref(), id)
|| obj_expr_mentions_bare_id(fs.n.as_ref(), id)
}
Obj::SeqSet(ss) => obj_expr_mentions_bare_id(ss.set.as_ref(), id),
Obj::MatrixSet(ms) => {
obj_expr_mentions_bare_id(ms.set.as_ref(), id)
|| obj_expr_mentions_bare_id(ms.row_len.as_ref(), id)
|| obj_expr_mentions_bare_id(ms.col_len.as_ref(), id)
}
Obj::Atom(AtomObj::IdentifierWithMod(_)) => false,
Obj::AnonymousFn(anon) => {
for g in &anon.body.params_def_with_set {
if obj_expr_mentions_bare_id(&g.set, id) {
return true;
}
}
obj_expr_mentions_bare_id(anon.body.ret_set.as_ref(), id)
|| obj_expr_mentions_bare_id(anon.equal_to.as_ref(), id)
}
Obj::FnObj(_) | Obj::FnSet(_) | Obj::SetBuilder(_) => true,
Obj::Atom(AtomObj::Forall(p)) => p.name == id,
Obj::Atom(AtomObj::Def(p)) => p.name == id,
Obj::Atom(AtomObj::Exist(p)) => p.name == id,
Obj::Atom(AtomObj::SetBuilder(p)) => p.name == id,
Obj::Atom(AtomObj::FnSet(p)) => p.name == id,
Obj::Atom(AtomObj::Induc(p)) => p.name == id,
Obj::Atom(AtomObj::DefAlgo(p)) => p.name == id,
}
}
fn factual_equal_success_by_builtin_reason(
left: &Obj,
right: &Obj,
line_file: LineFile,
reason: &str,
) -> StmtResult {
StmtResult::FactualStmtSuccess(
FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
EqualFact::new(left.clone(), right.clone(), line_file).into(),
reason.to_string(),
Vec::new(),
),
)
}
impl Runtime {
fn try_verify_objs_equal_by_expanding_family(
&mut self,
left: &Obj,
right: &Obj,
line_file: LineFile,
verify_state: &VerifyState,
) -> Result<Option<StmtResult>, RuntimeError> {
if let (Obj::FamilyObj(fl), Obj::FamilyObj(fr)) = (left, right) {
if let (Ok(el), Ok(er)) = (
self.instantiate_family_member_set(fl),
self.instantiate_family_member_set(fr),
) {
let r = self.verify_objs_are_equal(&el, &er, line_file.clone(), verify_state)?;
if r.is_true() {
return Ok(Some(factual_equal_success_by_builtin_reason(
left,
right,
line_file,
"equality: expand family definition (substitute parameters into equal_to)",
)));
}
}
}
if let Obj::FamilyObj(f) = left {
if let Ok(expanded) = self.instantiate_family_member_set(f) {
let r =
self.verify_objs_are_equal(&expanded, right, line_file.clone(), verify_state)?;
if r.is_true() {
return Ok(Some(factual_equal_success_by_builtin_reason(
left,
right,
line_file,
"equality: expand family definition (substitute parameters into equal_to)",
)));
}
}
}
if let Obj::FamilyObj(f) = right {
if let Ok(expanded) = self.instantiate_family_member_set(f) {
let r =
self.verify_objs_are_equal(left, &expanded, line_file.clone(), verify_state)?;
if r.is_true() {
return Ok(Some(factual_equal_success_by_builtin_reason(
left,
right,
line_file,
"equality: expand family definition (substitute parameters into equal_to)",
)));
}
}
}
Ok(None)
}
fn obj_is_builtin_literal_zero(obj: &Obj) -> bool {
match obj {
Obj::Number(n) => matches!(
compare_normalized_number_str_to_zero(&n.normalized_value),
NumberCompareResult::Equal
),
_ => false,
}
}
fn try_verify_zero_equals_subtraction_implies_equal_operands(
&mut self,
left: &Obj,
right: &Obj,
line_file: LineFile,
verify_state: &VerifyState,
) -> Result<Option<StmtResult>, RuntimeError> {
let (x, y) = if Self::obj_is_builtin_literal_zero(left) {
match right {
Obj::Sub(s) => (s.left.as_ref(), s.right.as_ref()),
_ => return Ok(None),
}
} else if Self::obj_is_builtin_literal_zero(right) {
match left {
Obj::Sub(s) => (s.left.as_ref(), s.right.as_ref()),
_ => return Ok(None),
}
} else {
return Ok(None);
};
let inner = self.verify_objs_are_equal(x, y, line_file.clone(), verify_state)?;
if inner.is_true() {
return Ok(Some(factual_equal_success_by_builtin_reason(
left,
right,
line_file,
"equality: 0 = x - y with x = y (known or builtin)",
)));
}
Ok(None)
}
fn try_verify_zero_equals_pow_from_base_zero(
&mut self,
left: &Obj,
right: &Obj,
line_file: LineFile,
verify_state: &VerifyState,
) -> Result<Option<StmtResult>, RuntimeError> {
let pow = if Self::obj_is_builtin_literal_zero(left) {
match right {
Obj::Pow(p) => p,
_ => return Ok(None),
}
} else if Self::obj_is_builtin_literal_zero(right) {
match left {
Obj::Pow(p) => p,
_ => return Ok(None),
}
} else {
return Ok(None);
};
let Obj::Number(exp_num) = pow.exponent.as_ref() else {
return Ok(None);
};
if !is_integer_after_simplification(exp_num) {
return Ok(None);
}
if !matches!(
compare_normalized_number_str_to_zero(&exp_num.normalized_value),
NumberCompareResult::Greater
) {
return Ok(None);
}
let base = pow.base.as_ref();
let zero_side = if Self::obj_is_builtin_literal_zero(left) {
left
} else {
right
};
let inner = self.verify_objs_are_equal(base, zero_side, line_file.clone(), verify_state)?;
if inner.is_true() {
return Ok(Some(factual_equal_success_by_builtin_reason(
left,
right,
line_file,
"equality: 0 = a^n from a = 0, n positive integer literal",
)));
}
Ok(None)
}
fn try_verify_log_identity_equalities(
&mut self,
left: &Obj,
right: &Obj,
line_file: LineFile,
verify_state: &VerifyState,
) -> Result<Option<StmtResult>, RuntimeError> {
let (log, other) = match (left, right) {
(Obj::Log(l), o) => (l, o),
(o, Obj::Log(l)) => (l, o),
_ => return Ok(None),
};
if let Obj::Pow(p) = log.arg.as_ref() {
let base_ok = self.verify_objs_are_equal(
p.base.as_ref(),
log.base.as_ref(),
line_file.clone(),
verify_state,
)?;
if base_ok.is_true() {
let exp_ok = self.verify_objs_are_equal(
p.exponent.as_ref(),
other,
line_file.clone(),
verify_state,
)?;
if exp_ok.is_true() {
return Ok(Some(factual_equal_success_by_builtin_reason(
left,
right,
line_file,
"equality: log(a, a^b) = b",
)));
}
}
}
Ok(None)
}
fn try_verify_log_base_power_rule(
&mut self,
left: &Obj,
right: &Obj,
line_file: LineFile,
verify_state: &VerifyState,
) -> Result<Option<StmtResult>, RuntimeError> {
let (log, other) = match (left, right) {
(Obj::Log(l), o) => (l, o),
(o, Obj::Log(l)) => (l, o),
_ => return Ok(None),
};
let Obj::Pow(p) = log.base.as_ref() else {
return Ok(None);
};
let inner_log: Obj = Log::new((*p.base).clone(), (*log.arg).clone()).into();
let expected: Obj = Div::new(inner_log, (*p.exponent).clone()).into();
let inner =
self.verify_objs_are_equal(other, &expected, line_file.clone(), verify_state)?;
if inner.is_true() {
return Ok(Some(factual_equal_success_by_builtin_reason(
left,
right,
line_file,
"equality: log(a^b, c) = log(a, c) / b",
)));
}
Ok(None)
}
fn try_verify_log_arg_power_rule(
&mut self,
left: &Obj,
right: &Obj,
line_file: LineFile,
verify_state: &VerifyState,
) -> Result<Option<StmtResult>, RuntimeError> {
let (log, other) = match (left, right) {
(Obj::Log(l), o) => (l, o),
(o, Obj::Log(l)) => (l, o),
_ => return Ok(None),
};
let Obj::Pow(p) = log.arg.as_ref() else {
return Ok(None);
};
let inner_log: Obj = Log::new((*log.base).clone(), (*p.base).clone()).into();
let expected1: Obj = Mul::new((*p.exponent).clone(), inner_log.clone()).into();
let expected2: Obj = Mul::new(inner_log, (*p.exponent).clone()).into();
for expected in [expected1, expected2] {
let inner =
self.verify_objs_are_equal(other, &expected, line_file.clone(), verify_state)?;
if inner.is_true() {
return Ok(Some(factual_equal_success_by_builtin_reason(
left,
right,
line_file,
"equality: log(a, x^b) = b * log(a, x)",
)));
}
}
Ok(None)
}
fn try_verify_log_product_rule(
&mut self,
left: &Obj,
right: &Obj,
line_file: LineFile,
verify_state: &VerifyState,
) -> Result<Option<StmtResult>, RuntimeError> {
let (log, other) = match (left, right) {
(Obj::Log(l), o) => (l, o),
(o, Obj::Log(l)) => (l, o),
_ => return Ok(None),
};
let Obj::Mul(m) = log.arg.as_ref() else {
return Ok(None);
};
let l1: Obj = Log::new((*log.base).clone(), (*m.left).clone()).into();
let l2: Obj = Log::new((*log.base).clone(), (*m.right).clone()).into();
let expected1: Obj = Add::new(l1.clone(), l2.clone()).into();
let expected2: Obj = Add::new(l2, l1).into();
for expected in [expected1, expected2] {
let inner =
self.verify_objs_are_equal(other, &expected, line_file.clone(), verify_state)?;
if inner.is_true() {
return Ok(Some(factual_equal_success_by_builtin_reason(
left,
right,
line_file,
"equality: log(a, x*y) = log(a, x) + log(a, y)",
)));
}
}
Ok(None)
}
fn try_verify_log_quotient_rule(
&mut self,
left: &Obj,
right: &Obj,
line_file: LineFile,
verify_state: &VerifyState,
) -> Result<Option<StmtResult>, RuntimeError> {
let (log, other) = match (left, right) {
(Obj::Log(l), o) => (l, o),
(o, Obj::Log(l)) => (l, o),
_ => return Ok(None),
};
let Obj::Div(d) = log.arg.as_ref() else {
return Ok(None);
};
let l1: Obj = Log::new((*log.base).clone(), (*d.left).clone()).into();
let l2: Obj = Log::new((*log.base).clone(), (*d.right).clone()).into();
let expected = Sub::new(l1, l2).into();
let inner =
self.verify_objs_are_equal(other, &expected, line_file.clone(), verify_state)?;
if inner.is_true() {
return Ok(Some(factual_equal_success_by_builtin_reason(
left,
right,
line_file,
"equality: log(a, x/y) = log(a, x) - log(a, y)",
)));
}
Ok(None)
}
fn try_verify_log_algebra_identities(
&mut self,
left: &Obj,
right: &Obj,
line_file: LineFile,
verify_state: &VerifyState,
) -> Result<Option<StmtResult>, RuntimeError> {
if let Some(done) =
self.try_verify_log_base_power_rule(left, right, line_file.clone(), verify_state)?
{
return Ok(Some(done));
}
if let Some(done) =
self.try_verify_log_arg_power_rule(left, right, line_file.clone(), verify_state)?
{
return Ok(Some(done));
}
if let Some(done) =
self.try_verify_log_product_rule(left, right, line_file.clone(), verify_state)?
{
return Ok(Some(done));
}
if let Some(done) =
self.try_verify_log_quotient_rule(left, right, line_file.clone(), verify_state)?
{
return Ok(Some(done));
}
Ok(None)
}
fn try_verify_anonymous_fn_application_equals_other_side(
&mut self,
statement_left: &Obj,
statement_right: &Obj,
application_side: &Obj,
other_side: &Obj,
line_file: LineFile,
verify_state: &VerifyState,
) -> Result<Option<StmtResult>, RuntimeError> {
let Obj::FnObj(fn_obj) = application_side else {
return Ok(None);
};
let FnObjHead::AnonymousFnLiteral(af) = fn_obj.head.as_ref() else {
return Ok(None);
};
if fn_obj.body.is_empty() {
return Ok(None);
}
let param_defs = &af.body.params_def_with_set;
let n_params = ParamGroupWithSet::number_of_params(param_defs);
if n_params == 0 {
return Ok(None);
}
let mut args: Vec<Obj> = Vec::new();
for g in fn_obj.body.iter() {
for b in g.iter() {
args.push((**b).clone());
}
}
if args.len() != n_params {
return Ok(None);
}
let param_to_arg_map = ParamGroupWithSet::param_defs_and_args_to_param_to_arg_map(
param_defs,
&args,
);
let reduced = self.inst_obj(
af.equal_to.as_ref(),
¶m_to_arg_map,
ParamObjType::FnSet,
)?;
let inner = self.verify_objs_are_equal(
&reduced,
other_side,
line_file.clone(),
verify_state,
)?;
if inner.is_true() {
return Ok(Some(factual_equal_success_by_builtin_reason(
statement_left,
statement_right,
line_file,
"equality: anonymous function application — substitute args into the body, equals the other side",
)));
}
Ok(None)
}
fn try_verify_log_equals_by_pow_inverse(
&mut self,
left: &Obj,
right: &Obj,
line_file: LineFile,
verify_state: &VerifyState,
) -> Result<Option<StmtResult>, RuntimeError> {
let (log, other) = match (left, right) {
(Obj::Log(l), o) => (l, o),
(o, Obj::Log(l)) => (l, o),
_ => return Ok(None),
};
let pow_obj: Obj = Pow::new((*log.base).clone(), other.clone()).into();
let inner = self.verify_objs_are_equal(
&pow_obj,
log.arg.as_ref(),
line_file.clone(),
verify_state,
)?;
if inner.is_true() {
return Ok(Some(factual_equal_success_by_builtin_reason(
left,
right,
line_file,
"equality: log(a, b) = c from a^c = b",
)));
}
Ok(None)
}
fn try_verify_sum_additivity(
&mut self,
left: &Obj,
right: &Obj,
line_file: LineFile,
verify_state: &VerifyState,
) -> Result<Option<StmtResult>, RuntimeError> {
if !verify_state.is_round_0() {
return Ok(None);
}
let (sum_m, sum_a, sum_b) = match (left, right) {
(Obj::Sum(m), Obj::Add(a)) => match (a.left.as_ref(), a.right.as_ref()) {
(Obj::Sum(a1), Obj::Sum(a2)) => (m, a1, a2),
_ => return Ok(None),
},
(Obj::Add(a), Obj::Sum(m)) => match (a.left.as_ref(), a.right.as_ref()) {
(Obj::Sum(a1), Obj::Sum(a2)) => (m, a1, a2),
_ => return Ok(None),
},
_ => return Ok(None),
};
let mut require_eq = |a: &Obj, b: &Obj| -> Result<bool, RuntimeError> {
Ok(self
.verify_objs_are_equal(a, b, line_file.clone(), verify_state)?
.is_true())
};
if !require_eq(sum_m.start.as_ref(), sum_a.start.as_ref())? {
return Ok(None);
}
if !require_eq(sum_m.start.as_ref(), sum_b.start.as_ref())? {
return Ok(None);
}
if !require_eq(sum_m.end.as_ref(), sum_a.end.as_ref())? {
return Ok(None);
}
if !require_eq(sum_m.end.as_ref(), sum_b.end.as_ref())? {
return Ok(None);
}
let x_name = self.generate_random_unused_name();
let x_obj = obj_for_bound_param_in_scope(x_name.clone(), ParamObjType::Forall);
let Some(l_inst) =
self.instantiate_unary_anonymous_summand_at(sum_m.func.as_ref(), &x_obj)?
else {
return Ok(None);
};
let Some(a_inst) =
self.instantiate_unary_anonymous_summand_at(sum_a.func.as_ref(), &x_obj)?
else {
return Ok(None);
};
let Some(b_inst) =
self.instantiate_unary_anonymous_summand_at(sum_b.func.as_ref(), &x_obj)?
else {
return Ok(None);
};
let then_fact: ExistOrAndChainAtomicFact = EqualFact::new(
l_inst,
Add::new(a_inst, b_inst).into(),
line_file.clone(),
)
.into();
let dom_lo: Fact = LessEqualFact::new(
(*sum_m.start).clone(),
x_obj.clone(),
line_file.clone(),
)
.into();
let dom_hi: Fact = LessEqualFact::new(
x_obj.clone(),
(*sum_m.end).clone(),
line_file.clone(),
)
.into();
let forall_fact: Fact = ForallFact::new(
ParamDefWithType::new(vec![ParamGroupWithParamType::new(
vec![x_name],
ParamType::Obj(StandardSet::Z.into()),
)]),
vec![dom_lo, dom_hi],
vec![then_fact],
line_file.clone(),
)
.into();
let r = self.verify_fact(&forall_fact, verify_state)?;
if r.is_true() {
return Ok(Some(factual_equal_success_by_builtin_reason(
left,
right,
line_file,
"equality: sum additivity from pointwise equality on the integer index range",
)));
}
Ok(None)
}
fn instantiate_unary_anonymous_summand_at(
&mut self,
func: &Obj,
x: &Obj,
) -> Result<Option<Obj>, RuntimeError> {
let af: &AnonymousFn = match func {
Obj::AnonymousFn(af) => af,
Obj::FnObj(fo) => {
if !fo.body.is_empty() {
return Ok(None);
}
match fo.head.as_ref() {
FnObjHead::AnonymousFnLiteral(a) => a.as_ref(),
_ => return Ok(None),
}
}
_ => return Ok(None),
};
if ParamGroupWithSet::number_of_params(&af.body.params_def_with_set) != 1 {
return Ok(None);
}
let param_defs = &af.body.params_def_with_set;
let args = vec![x.clone()];
let param_to_arg_map =
ParamGroupWithSet::param_defs_and_args_to_param_to_arg_map(param_defs, &args);
Ok(Some(
self.inst_obj(af.equal_to.as_ref(), ¶m_to_arg_map, ParamObjType::FnSet)?,
))
}
fn try_verify_sum_merge_adjacent_ranges(
&mut self,
left: &Obj,
right: &Obj,
line_file: LineFile,
verify_state: &VerifyState,
) -> Result<Option<StmtResult>, RuntimeError> {
if !verify_state.is_round_0() {
return Ok(None);
}
let (add, s3) = match (left, right) {
(Obj::Add(a), Obj::Sum(s)) => (a, s),
(Obj::Sum(s), Obj::Add(a)) => (a, s),
_ => return Ok(None),
};
let (s1, s2) = match (add.left.as_ref(), add.right.as_ref()) {
(Obj::Sum(x), Obj::Sum(y)) => (x, y),
_ => return Ok(None),
};
for (a, b) in [(s1, s2), (s2, s1)] {
if let Some(done) = self.try_verify_sum_merge_ordered_pair(
a,
b,
s3,
left,
right,
line_file.clone(),
verify_state,
)? {
return Ok(Some(done));
}
}
Ok(None)
}
fn try_verify_sum_merge_ordered_pair(
&mut self,
s1: &Sum,
s2: &Sum,
s3: &Sum,
stmt_left: &Obj,
stmt_right: &Obj,
line_file: LineFile,
verify_state: &VerifyState,
) -> Result<Option<StmtResult>, RuntimeError> {
let one: Obj = Number::new("1".to_string()).into();
let gap = Add::new((*s1.end).clone(), one).into();
if !self
.verify_objs_are_equal(&gap, s2.start.as_ref(), line_file.clone(), verify_state)?
.is_true()
{
return Ok(None);
}
if !self
.verify_objs_are_equal(s1.start.as_ref(), s3.start.as_ref(), line_file.clone(), verify_state)?
.is_true()
{
return Ok(None);
}
if !self
.verify_objs_are_equal(s2.end.as_ref(), s3.end.as_ref(), line_file.clone(), verify_state)?
.is_true()
{
return Ok(None);
}
if !self
.verify_objs_are_equal(s1.func.as_ref(), s2.func.as_ref(), line_file.clone(), verify_state)?
.is_true()
{
return Ok(None);
}
if !self
.verify_objs_are_equal(s1.func.as_ref(), s3.func.as_ref(), line_file.clone(), verify_state)?
.is_true()
{
return Ok(None);
}
Ok(Some(factual_equal_success_by_builtin_reason(
stmt_left,
stmt_right,
line_file,
"equality: merge adjacent sum ranges with the same summand",
)))
}
fn try_verify_sum_split_last_term(
&mut self,
left: &Obj,
right: &Obj,
line_file: LineFile,
verify_state: &VerifyState,
) -> Result<Option<StmtResult>, RuntimeError> {
if !verify_state.is_round_0() {
return Ok(None);
}
let one: Obj = Number::new("1".to_string()).into();
for (full_obj, add_obj) in [(left, right), (right, left)] {
let Obj::Sum(s_full) = full_obj else {
continue;
};
let Obj::Add(a) = add_obj else {
continue;
};
for (sum_part, tail) in [
(a.left.as_ref(), a.right.as_ref()),
(a.right.as_ref(), a.left.as_ref()),
] {
let Obj::Sum(s_pre) = sum_part else {
continue;
};
if !self
.verify_objs_are_equal(
s_full.start.as_ref(),
s_pre.start.as_ref(),
line_file.clone(),
verify_state,
)?
.is_true()
{
continue;
}
let end_pre_plus_one: Obj = Add::new((*s_pre.end).clone(), one.clone()).into();
if !self
.verify_objs_are_equal(
s_full.end.as_ref(),
&end_pre_plus_one,
line_file.clone(),
verify_state,
)?
.is_true()
{
continue;
}
if !self
.verify_objs_are_equal(
s_full.func.as_ref(),
s_pre.func.as_ref(),
line_file.clone(),
verify_state,
)?
.is_true()
{
continue;
}
let Some(expected_tail) = self.instantiate_unary_anonymous_summand_at(
s_full.func.as_ref(),
s_full.end.as_ref(),
)?
else {
continue;
};
if !self
.verify_objs_are_equal(&expected_tail, tail, line_file.clone(), verify_state)?
.is_true()
{
continue;
}
return Ok(Some(factual_equal_success_by_builtin_reason(
left,
right,
line_file,
"equality: sum through e equals sum through e-1 plus last summand f(e)",
)));
}
}
Ok(None)
}
fn try_verify_product_split_last_term(
&mut self,
left: &Obj,
right: &Obj,
line_file: LineFile,
verify_state: &VerifyState,
) -> Result<Option<StmtResult>, RuntimeError> {
if !verify_state.is_round_0() {
return Ok(None);
}
let one: Obj = Number::new("1".to_string()).into();
for (full_obj, mul_obj) in [(left, right), (right, left)] {
let Obj::Product(p_full) = full_obj else {
continue;
};
let Obj::Mul(m) = mul_obj else {
continue;
};
for (prod_part, tail) in [
(m.left.as_ref(), m.right.as_ref()),
(m.right.as_ref(), m.left.as_ref()),
] {
let Obj::Product(p_pre) = prod_part else {
continue;
};
if !self
.verify_objs_are_equal(
p_full.start.as_ref(),
p_pre.start.as_ref(),
line_file.clone(),
verify_state,
)?
.is_true()
{
continue;
}
let end_pre_plus_one: Obj = Add::new((*p_pre.end).clone(), one.clone()).into();
if !self
.verify_objs_are_equal(
p_full.end.as_ref(),
&end_pre_plus_one,
line_file.clone(),
verify_state,
)?
.is_true()
{
continue;
}
if !self
.verify_objs_are_equal(
p_full.func.as_ref(),
p_pre.func.as_ref(),
line_file.clone(),
verify_state,
)?
.is_true()
{
continue;
}
let Some(expected_tail) = self.instantiate_unary_anonymous_summand_at(
p_full.func.as_ref(),
p_full.end.as_ref(),
)?
else {
continue;
};
if !self
.verify_objs_are_equal(&expected_tail, tail, line_file.clone(), verify_state)?
.is_true()
{
continue;
}
return Ok(Some(factual_equal_success_by_builtin_reason(
left,
right,
line_file,
"equality: product through e equals product through e-1 times last factor f(e)",
)));
}
}
Ok(None)
}
fn flatten_left_assoc_add_chain(obj: &Obj) -> Vec<&Obj> {
match obj {
Obj::Add(a) => {
let mut v = Self::flatten_left_assoc_add_chain(a.left.as_ref());
v.push(a.right.as_ref());
v
}
_ => vec![obj],
}
}
fn flatten_left_assoc_mul_chain(obj: &Obj) -> Vec<&Obj> {
match obj {
Obj::Mul(m) => {
let mut v = Self::flatten_left_assoc_mul_chain(m.left.as_ref());
v.push(m.right.as_ref());
v
}
_ => vec![obj],
}
}
fn try_verify_sum_partition_adjacent_ranges(
&mut self,
left: &Obj,
right: &Obj,
line_file: LineFile,
verify_state: &VerifyState,
) -> Result<Option<StmtResult>, RuntimeError> {
if !verify_state.is_round_0() {
return Ok(None);
}
let one: Obj = Number::new("1".to_string()).into();
for (full_side, add_side) in [(left, right), (right, left)] {
let Obj::Sum(s_full) = full_side else {
continue;
};
let Obj::Add(_) = add_side else {
continue;
};
let parts = Self::flatten_left_assoc_add_chain(add_side);
if parts.len() < 2 {
continue;
}
let mut sums: Vec<&Sum> = Vec::with_capacity(parts.len());
let mut all_sum = true;
for p in &parts {
if let Obj::Sum(s) = p {
sums.push(s);
} else {
all_sum = false;
break;
}
}
if !all_sum {
continue;
}
if !self
.verify_objs_are_equal(
s_full.start.as_ref(),
sums[0].start.as_ref(),
line_file.clone(),
verify_state,
)?
.is_true()
{
continue;
}
if !self
.verify_objs_are_equal(
s_full.end.as_ref(),
sums[sums.len() - 1].end.as_ref(),
line_file.clone(),
verify_state,
)?
.is_true()
{
continue;
}
let mut gaps_ok = true;
for i in 0..sums.len().saturating_sub(1) {
let gap = Add::new((*sums[i].end).clone(), one.clone()).into();
if !self
.verify_objs_are_equal(
&gap,
sums[i + 1].start.as_ref(),
line_file.clone(),
verify_state,
)?
.is_true()
{
gaps_ok = false;
break;
}
}
if !gaps_ok {
continue;
}
let mut func_ok = true;
for s in &sums {
if !self
.verify_objs_are_equal(
s_full.func.as_ref(),
s.func.as_ref(),
line_file.clone(),
verify_state,
)?
.is_true()
{
func_ok = false;
break;
}
}
if !func_ok {
continue;
}
return Ok(Some(factual_equal_success_by_builtin_reason(
left,
right,
line_file,
"equality: sum partitions closed range into adjacent sub-sums with the same summand",
)));
}
Ok(None)
}
fn try_verify_product_partition_adjacent_ranges(
&mut self,
left: &Obj,
right: &Obj,
line_file: LineFile,
verify_state: &VerifyState,
) -> Result<Option<StmtResult>, RuntimeError> {
if !verify_state.is_round_0() {
return Ok(None);
}
let one: Obj = Number::new("1".to_string()).into();
for (full_side, mul_side) in [(left, right), (right, left)] {
let Obj::Product(p_full) = full_side else {
continue;
};
let Obj::Mul(_) = mul_side else {
continue;
};
let parts = Self::flatten_left_assoc_mul_chain(mul_side);
if parts.len() < 2 {
continue;
}
let mut products: Vec<&Product> = Vec::with_capacity(parts.len());
let mut all_prod = true;
for p in &parts {
if let Obj::Product(pr) = p {
products.push(pr);
} else {
all_prod = false;
break;
}
}
if !all_prod {
continue;
}
if !self
.verify_objs_are_equal(
p_full.start.as_ref(),
products[0].start.as_ref(),
line_file.clone(),
verify_state,
)?
.is_true()
{
continue;
}
if !self
.verify_objs_are_equal(
p_full.end.as_ref(),
products[products.len() - 1].end.as_ref(),
line_file.clone(),
verify_state,
)?
.is_true()
{
continue;
}
let mut gaps_ok = true;
for i in 0..products.len().saturating_sub(1) {
let gap = Add::new((*products[i].end).clone(), one.clone()).into();
if !self
.verify_objs_are_equal(
&gap,
products[i + 1].start.as_ref(),
line_file.clone(),
verify_state,
)?
.is_true()
{
gaps_ok = false;
break;
}
}
if !gaps_ok {
continue;
}
let mut func_ok = true;
for p in &products {
if !self
.verify_objs_are_equal(
p_full.func.as_ref(),
p.func.as_ref(),
line_file.clone(),
verify_state,
)?
.is_true()
{
func_ok = false;
break;
}
}
if !func_ok {
continue;
}
return Ok(Some(factual_equal_success_by_builtin_reason(
left,
right,
line_file,
"equality: product partitions closed range into adjacent sub-products with the same factor",
)));
}
Ok(None)
}
fn try_verify_sum_reindex_shift(
&mut self,
left: &Obj,
right: &Obj,
line_file: LineFile,
verify_state: &VerifyState,
) -> Result<Option<StmtResult>, RuntimeError> {
if !verify_state.is_round_0() {
return Ok(None);
}
for (l_obj, r_obj) in [(left, right), (right, left)] {
let (Obj::Sum(l_sum), Obj::Sum(r_sum)) = (l_obj, r_obj) else {
continue;
};
let k: Obj = Sub::new((*r_sum.start).clone(), (*l_sum.start).clone()).into();
let k_end = Sub::new((*r_sum.end).clone(), (*l_sum.end).clone()).into();
if !self
.verify_objs_are_equal(&k, &k_end, line_file.clone(), verify_state)?
.is_true()
{
continue;
}
let y_name = self.generate_random_unused_name();
let y_obj = obj_for_bound_param_in_scope(y_name.clone(), ParamObjType::Forall);
let index_for_left = Sub::new(y_obj.clone(), k.clone()).into();
let Some(at_l) =
self.instantiate_unary_anonymous_summand_at(l_sum.func.as_ref(), &index_for_left)?
else {
continue;
};
let Some(at_r) =
self.instantiate_unary_anonymous_summand_at(r_sum.func.as_ref(), &y_obj)?
else {
continue;
};
let then_fact: ExistOrAndChainAtomicFact =
EqualFact::new(at_l, at_r, line_file.clone()).into();
let dom_lo: Fact = LessEqualFact::new(
(*r_sum.start).clone(),
y_obj.clone(),
line_file.clone(),
)
.into();
let dom_hi: Fact = LessEqualFact::new(
y_obj.clone(),
(*r_sum.end).clone(),
line_file.clone(),
)
.into();
let forall_fact: Fact = ForallFact::new(
ParamDefWithType::new(vec![ParamGroupWithParamType::new(
vec![y_name],
ParamType::Obj(StandardSet::Z.into()),
)]),
vec![dom_lo, dom_hi],
vec![then_fact],
line_file.clone(),
)
.into();
let r = self.verify_fact(&forall_fact, verify_state)?;
if r.is_true() {
return Ok(Some(factual_equal_success_by_builtin_reason(
left,
right,
line_file,
"equality: sum reindexing (integer shift) from pointwise equality on the range",
)));
}
}
Ok(None)
}
fn try_verify_sum_constant_summand(
&mut self,
left: &Obj,
right: &Obj,
line_file: LineFile,
verify_state: &VerifyState,
) -> Result<Option<StmtResult>, RuntimeError> {
if !verify_state.is_round_0() {
return Ok(None);
}
for (sum_side, other) in [(left, right), (right, left)] {
let Obj::Sum(s) = sum_side else {
continue;
};
let af = match s.func.as_ref() {
Obj::AnonymousFn(af) => af,
Obj::FnObj(fo) if fo.body.is_empty() => match fo.head.as_ref() {
FnObjHead::AnonymousFnLiteral(a) => a.as_ref(),
_ => continue,
},
_ => continue,
};
if ParamGroupWithSet::number_of_params(&af.body.params_def_with_set) != 1 {
continue;
}
let names = ParamGroupWithSet::collect_param_names(&af.body.params_def_with_set);
let pname = match names.first() {
Some(n) => n.as_str(),
None => continue,
};
if obj_expr_mentions_bare_id(af.equal_to.as_ref(), pname) {
continue;
}
let c = (*af.equal_to).clone();
let one: Obj = Number::new("1".to_string()).into();
let count: Obj = Add::new(
Sub::new((*s.end).clone(), (*s.start).clone()).into(),
one,
)
.into();
let m1: Obj = Mul::new(count.clone(), c.clone()).into();
let m2: Obj = Mul::new(c, count).into();
if self
.verify_objs_are_equal(other, &m1, line_file.clone(), verify_state)?
.is_true()
|| self
.verify_objs_are_equal(other, &m2, line_file.clone(), verify_state)?
.is_true()
{
return Ok(Some(factual_equal_success_by_builtin_reason(
left,
right,
line_file,
"equality: sum of a constant summand over a closed integer range",
)));
}
}
Ok(None)
}
fn try_verify_mod_nested_same_modulus_absorption(
&mut self,
left: &Obj,
right: &Obj,
line_file: LineFile,
verify_state: &VerifyState,
) -> Result<Option<StmtResult>, RuntimeError> {
for (side_nested, side_simple) in [(left, right), (right, left)] {
let Obj::Mod(outer) = side_nested else {
continue;
};
let Obj::Mod(inner) = outer.left.as_ref() else {
continue;
};
let Obj::Mod(simple) = side_simple else {
continue;
};
if !self
.verify_objs_are_equal(
outer.right.as_ref(),
inner.right.as_ref(),
line_file.clone(),
verify_state,
)?
.is_true()
{
continue;
}
if !self
.verify_objs_are_equal(
outer.right.as_ref(),
simple.right.as_ref(),
line_file.clone(),
verify_state,
)?
.is_true()
{
continue;
}
if !self
.verify_objs_are_equal(
inner.left.as_ref(),
simple.left.as_ref(),
line_file.clone(),
verify_state,
)?
.is_true()
{
continue;
}
return Ok(Some(factual_equal_success_by_builtin_reason(
left,
right,
line_file,
"equality: nested mod with same modulus absorbs inner mod",
)));
}
Ok(None)
}
fn try_verify_mod_peel_nested_same_modulus(
&mut self,
left: &Obj,
right: &Obj,
line_file: LineFile,
verify_state: &VerifyState,
) -> Result<Option<StmtResult>, RuntimeError> {
let (Obj::Mod(lm), Obj::Mod(rm)) = (left, right) else {
return Ok(None);
};
if !self
.verify_objs_are_equal(
lm.right.as_ref(),
rm.right.as_ref(),
line_file.clone(),
verify_state,
)?
.is_true()
{
return Ok(None);
}
let modulus = lm.right.as_ref();
if let Obj::Mod(r_inner) = rm.left.as_ref() {
if self
.verify_objs_are_equal(
r_inner.right.as_ref(),
modulus,
line_file.clone(),
verify_state,
)?
.is_true()
{
let lhs: Obj = Mod::new((*lm.left).clone(), (*lm.right).clone()).into();
let rhs: Obj = Mod::new((*r_inner.left).clone(), (*lm.right).clone()).into();
if self
.verify_objs_are_equal(&lhs, &rhs, line_file.clone(), verify_state)?
.is_true()
{
return Ok(Some(factual_equal_success_by_builtin_reason(
left,
right,
line_file,
"equality: mod — peel outer nested % m to reuse residue equality (full verify_objs_are_equal)",
)));
}
}
}
if let Obj::Mod(l_inner) = lm.left.as_ref() {
if self
.verify_objs_are_equal(
l_inner.right.as_ref(),
modulus,
line_file.clone(),
verify_state,
)?
.is_true()
{
let lhs: Obj = Mod::new((*l_inner.left).clone(), (*lm.right).clone()).into();
let rhs: Obj = Mod::new((*rm.left).clone(), (*lm.right).clone()).into();
if self
.verify_objs_are_equal(&lhs, &rhs, line_file.clone(), verify_state)?
.is_true()
{
return Ok(Some(factual_equal_success_by_builtin_reason(
left,
right,
line_file,
"equality: mod — peel outer nested % m to reuse residue equality (full verify_objs_are_equal)",
)));
}
}
}
Ok(None)
}
fn try_verify_mod_congruence_from_inner_binary(
&mut self,
left: &Obj,
right: &Obj,
line_file: LineFile,
verify_state: &VerifyState,
) -> Result<Option<StmtResult>, RuntimeError> {
let (Obj::Mod(lm), Obj::Mod(rm)) = (left, right) else {
return Ok(None);
};
if !self
.verify_objs_are_equal(
lm.right.as_ref(),
rm.right.as_ref(),
line_file.clone(),
verify_state,
)?
.is_true()
{
return Ok(None);
}
let mut pair_ok = |a: &Obj, b: &Obj| -> Result<bool, RuntimeError> {
let l: Obj = Mod::new(a.clone(), (*lm.right).clone()).into();
let r: Obj = Mod::new(b.clone(), (*rm.right).clone()).into();
Ok(self
.verify_objs_are_equal(&l, &r, line_file.clone(), verify_state)?
.is_true())
};
let ok = match (lm.left.as_ref(), rm.left.as_ref()) {
(Obj::Add(la), Obj::Add(ra)) => {
pair_ok(la.left.as_ref(), ra.left.as_ref())?
&& pair_ok(la.right.as_ref(), ra.right.as_ref())?
}
(Obj::Sub(ls), Obj::Sub(rs)) => {
pair_ok(ls.left.as_ref(), rs.left.as_ref())?
&& pair_ok(ls.right.as_ref(), rs.right.as_ref())?
}
(Obj::Mul(lx), Obj::Mul(rx)) => {
pair_ok(lx.left.as_ref(), rx.left.as_ref())?
&& pair_ok(lx.right.as_ref(), rx.right.as_ref())?
}
_ => return Ok(None),
};
if !ok {
return Ok(None);
}
Ok(Some(factual_equal_success_by_builtin_reason(
left,
right,
line_file,
"equality: integer congruence — same modulus, residues for matching + / - / *",
)))
}
pub fn verify_equality_by_builtin_rules(
&mut self,
left: &Obj,
right: &Obj,
line_file: LineFile,
verify_state: &VerifyState,
) -> Result<StmtResult, RuntimeError> {
if let Some(done) = self.try_verify_objs_equal_by_expanding_family(
left,
right,
line_file.clone(),
verify_state,
)? {
return Ok(done);
}
if let Some(done) = self.try_verify_zero_equals_subtraction_implies_equal_operands(
left,
right,
line_file.clone(),
verify_state,
)? {
return Ok(done);
}
if let Some(done) = self.try_verify_zero_equals_pow_from_base_zero(
left,
right,
line_file.clone(),
verify_state,
)? {
return Ok(done);
}
if let Some(done) =
self.try_verify_log_identity_equalities(left, right, line_file.clone(), verify_state)?
{
return Ok(done);
}
if let Some(done) =
self.try_verify_log_algebra_identities(left, right, line_file.clone(), verify_state)?
{
return Ok(done);
}
if let Some(done) =
self.try_verify_log_equals_by_pow_inverse(left, right, line_file.clone(), verify_state)?
{
return Ok(done);
}
if let Some(done) =
self.try_verify_sum_additivity(left, right, line_file.clone(), verify_state)?
{
return Ok(done);
}
if let Some(done) =
self.try_verify_sum_merge_adjacent_ranges(left, right, line_file.clone(), verify_state)?
{
return Ok(done);
}
if let Some(done) =
self.try_verify_sum_split_last_term(left, right, line_file.clone(), verify_state)?
{
return Ok(done);
}
if let Some(done) =
self.try_verify_product_split_last_term(left, right, line_file.clone(), verify_state)?
{
return Ok(done);
}
if let Some(done) = self.try_verify_sum_partition_adjacent_ranges(
left,
right,
line_file.clone(),
verify_state,
)? {
return Ok(done);
}
if let Some(done) = self.try_verify_product_partition_adjacent_ranges(
left,
right,
line_file.clone(),
verify_state,
)? {
return Ok(done);
}
if let Some(done) =
self.try_verify_sum_reindex_shift(left, right, line_file.clone(), verify_state)?
{
return Ok(done);
}
if let Some(done) =
self.try_verify_sum_constant_summand(left, right, line_file.clone(), verify_state)?
{
return Ok(done);
}
if let Some(done) = self.try_verify_mod_nested_same_modulus_absorption(
left,
right,
line_file.clone(),
verify_state,
)? {
return Ok(done);
}
if let Some(done) = self.try_verify_mod_peel_nested_same_modulus(
left,
right,
line_file.clone(),
verify_state,
)? {
return Ok(done);
}
if let Some(done) = self.try_verify_mod_congruence_from_inner_binary(
left,
right,
line_file.clone(),
verify_state,
)? {
return Ok(done);
}
let (result, calculated_left, calculated_right) = self
.verify_equality_by_they_are_the_same_and_calculation(
left,
right,
line_file.clone(),
verify_state,
)?;
if result.is_true() {
return Ok(result);
}
if objs_equal_by_rational_expression_evaluation(&left, &right) {
return Ok(
(FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
EqualFact::new(left.clone(), right.clone(), line_file).into(),
"calculation and rational expression simplification".to_string(),
Vec::new(),
))
.into(),
);
}
if objs_equal_by_rational_expression_evaluation(&calculated_left, &calculated_right) {
return Ok(
(FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
EqualFact::new(left.clone(), right.clone(), line_file).into(),
"calculation and rational expression simplification".to_string(),
Vec::new(),
))
.into(),
);
}
if let Some(done) = self.try_verify_anonymous_fn_application_equals_other_side(
left,
right,
left,
right,
line_file.clone(),
verify_state,
)? {
return Ok(done);
}
if let Some(done) = self.try_verify_anonymous_fn_application_equals_other_side(
left,
right,
right,
left,
line_file.clone(),
verify_state,
)? {
return Ok(done);
}
if let (Obj::FnSet(left_fn_set), Obj::FnSet(right_fn_set)) = (left, right) {
return self.verify_fn_set_with_params_equality_by_builtin_rules(
left_fn_set,
right_fn_set,
line_file,
verify_state,
);
}
if let (Obj::AnonymousFn(l), Obj::AnonymousFn(r)) = (left, right) {
if l.to_string() == r.to_string() {
return Ok(
(FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
EqualFact::new(left.clone(), right.clone(), line_file).into(),
"anonymous fn: identical surface syntax (params, dom, ret, body)"
.to_string(),
Vec::new(),
))
.into(),
);
}
}
Ok((StmtUnknown::new()).into())
}
}
impl Runtime {
fn try_verify_equality_pair_by_the_same_then_calculation_then_fn_obj_same_head_known_args(
&mut self,
left: &Obj,
right: &Obj,
line_file: LineFile,
verify_state: &VerifyState,
) -> Result<Option<StmtResult>, RuntimeError> {
let (result, _, _) = self.verify_equality_by_they_are_the_same_and_calculation(
left,
right,
line_file.clone(),
verify_state,
)?;
if result.is_true() {
return Ok(Some(result));
}
if let Some(shape_result) =
self.try_verify_equal_by_same_shape_and_known_equality_args(left, right, line_file)
{
if shape_result.is_true() {
return Ok(Some(shape_result));
}
}
Ok(None)
}
pub fn try_verify_equality_with_known_equalities_by_builtin_rules_only(
&mut self,
left: &Obj,
right: &Obj,
line_file: LineFile,
verify_state: &VerifyState,
known_objs_equal_to_left: Option<&Rc<Vec<Obj>>>,
known_objs_equal_to_right: Option<&Rc<Vec<Obj>>>,
) -> Result<Option<StmtResult>, RuntimeError> {
match (known_objs_equal_to_left, known_objs_equal_to_right) {
(None, None) => Ok(None),
(Some(known_objs_equal_to_left), None) => {
for obj in known_objs_equal_to_left.iter() {
if let Some(result) = self
.try_verify_equality_pair_by_the_same_then_calculation_then_fn_obj_same_head_known_args(
obj,
right,
line_file.clone(),
verify_state,
)?
{
return Ok(Some(result));
}
}
Ok(None)
}
(None, Some(known_objs_equal_to_right)) => {
for obj in known_objs_equal_to_right.iter() {
if let Some(result) = self
.try_verify_equality_pair_by_the_same_then_calculation_then_fn_obj_same_head_known_args(
left,
obj,
line_file.clone(),
verify_state,
)?
{
return Ok(Some(result));
}
}
Ok(None)
}
(Some(known_objs_equal_to_left), Some(known_objs_equal_to_right)) => {
for obj1 in known_objs_equal_to_left.iter() {
for obj2 in known_objs_equal_to_right.iter() {
if let Some(result) = self
.try_verify_equality_pair_by_the_same_then_calculation_then_fn_obj_same_head_known_args(
obj1,
obj2,
line_file.clone(),
verify_state,
)?
{
return Ok(Some(result));
}
}
}
Ok(None)
}
}
}
pub fn objs_have_same_known_equality_rc_in_some_env(&self, left: &Obj, right: &Obj) -> bool {
let left_key: ObjString = left.to_string();
let right_key: ObjString = right.to_string();
for env in self.iter_environments_from_top() {
let left_entry = env.known_equality.get(&left_key);
let right_entry = env.known_equality.get(&right_key);
if let (Some((_, left_rc)), Some((_, right_rc))) = (left_entry, right_entry) {
if Rc::ptr_eq(left_rc, right_rc) {
return true;
}
}
}
false
}
fn arg_pairs_share_known_equality_class(&self, pairs: &[(&Obj, &Obj)]) -> bool {
pairs
.iter()
.all(|(a, b)| self.objs_have_same_known_equality_rc_in_some_env(a, b))
}
fn boxed_obj_vecs_share_known_equality_class(
&self,
left: &[Box<Obj>],
right: &[Box<Obj>],
) -> bool {
if left.len() != right.len() {
return false;
}
left.iter()
.zip(right.iter())
.all(|(a, b)| self.objs_have_same_known_equality_rc_in_some_env(a, b))
}
pub fn try_verify_equal_by_same_shape_and_known_equality_args(
&self,
left: &Obj,
right: &Obj,
line_file: LineFile,
) -> Option<StmtResult> {
let reason = "same shape and paired args share known equality class";
match (left, right) {
(Obj::FnObj(left_fn), Obj::FnObj(right_fn)) => {
let left_head_obj = left_fn.head.as_ref().clone().into();
let right_head_obj = right_fn.head.as_ref().clone().into();
if !verify_equality_by_they_are_the_same(&left_head_obj, &right_head_obj) {
return Some((StmtUnknown::new()).into());
}
if left_fn.body.len() != right_fn.body.len() {
return Some((StmtUnknown::new()).into());
}
for (left_group, right_group) in left_fn.body.iter().zip(right_fn.body.iter()) {
if !self.boxed_obj_vecs_share_known_equality_class(left_group, right_group) {
return Some((StmtUnknown::new()).into());
}
}
Some(factual_equal_success_by_builtin_reason(
left, right, line_file, reason,
))
}
(Obj::Add(l), Obj::Add(r)) => {
if self.arg_pairs_share_known_equality_class(&[
(&l.left, &r.left),
(&l.right, &r.right),
]) {
Some(factual_equal_success_by_builtin_reason(
left, right, line_file, reason,
))
} else {
Some((StmtUnknown::new()).into())
}
}
(Obj::MatrixAdd(l), Obj::MatrixAdd(r)) => {
if self.arg_pairs_share_known_equality_class(&[
(&l.left, &r.left),
(&l.right, &r.right),
]) {
Some(factual_equal_success_by_builtin_reason(
left, right, line_file, reason,
))
} else {
Some((StmtUnknown::new()).into())
}
}
(Obj::MatrixSub(l), Obj::MatrixSub(r)) => {
if self.arg_pairs_share_known_equality_class(&[
(&l.left, &r.left),
(&l.right, &r.right),
]) {
Some(factual_equal_success_by_builtin_reason(
left, right, line_file, reason,
))
} else {
Some((StmtUnknown::new()).into())
}
}
(Obj::MatrixMul(l), Obj::MatrixMul(r)) => {
if self.arg_pairs_share_known_equality_class(&[
(&l.left, &r.left),
(&l.right, &r.right),
]) {
Some(factual_equal_success_by_builtin_reason(
left, right, line_file, reason,
))
} else {
Some((StmtUnknown::new()).into())
}
}
(Obj::MatrixScalarMul(l), Obj::MatrixScalarMul(r)) => {
if self.arg_pairs_share_known_equality_class(&[
(&l.scalar, &r.scalar),
(&l.matrix, &r.matrix),
]) {
Some(factual_equal_success_by_builtin_reason(
left, right, line_file, reason,
))
} else {
Some((StmtUnknown::new()).into())
}
}
(Obj::MatrixPow(l), Obj::MatrixPow(r)) => {
if self.arg_pairs_share_known_equality_class(&[
(&l.base, &r.base),
(&l.exponent, &r.exponent),
]) {
Some(factual_equal_success_by_builtin_reason(
left, right, line_file, reason,
))
} else {
Some((StmtUnknown::new()).into())
}
}
(Obj::Sub(l), Obj::Sub(r)) => {
if self.arg_pairs_share_known_equality_class(&[
(&l.left, &r.left),
(&l.right, &r.right),
]) {
Some(factual_equal_success_by_builtin_reason(
left, right, line_file, reason,
))
} else {
Some((StmtUnknown::new()).into())
}
}
(Obj::Mul(l), Obj::Mul(r)) => {
if self.arg_pairs_share_known_equality_class(&[
(&l.left, &r.left),
(&l.right, &r.right),
]) {
Some(factual_equal_success_by_builtin_reason(
left, right, line_file, reason,
))
} else {
Some((StmtUnknown::new()).into())
}
}
(Obj::Div(l), Obj::Div(r)) => {
if self.arg_pairs_share_known_equality_class(&[
(&l.left, &r.left),
(&l.right, &r.right),
]) {
Some(factual_equal_success_by_builtin_reason(
left, right, line_file, reason,
))
} else {
Some((StmtUnknown::new()).into())
}
}
(Obj::Mod(l), Obj::Mod(r)) => {
if self.arg_pairs_share_known_equality_class(&[
(&l.left, &r.left),
(&l.right, &r.right),
]) {
Some(factual_equal_success_by_builtin_reason(
left, right, line_file, reason,
))
} else {
Some((StmtUnknown::new()).into())
}
}
(Obj::Pow(l), Obj::Pow(r)) => {
if self.arg_pairs_share_known_equality_class(&[
(&l.base, &r.base),
(&l.exponent, &r.exponent),
]) {
Some(factual_equal_success_by_builtin_reason(
left, right, line_file, reason,
))
} else {
Some((StmtUnknown::new()).into())
}
}
(Obj::Log(l), Obj::Log(r)) => {
if self
.arg_pairs_share_known_equality_class(&[(&l.base, &r.base), (&l.arg, &r.arg)])
{
Some(factual_equal_success_by_builtin_reason(
left, right, line_file, reason,
))
} else {
Some((StmtUnknown::new()).into())
}
}
(Obj::Max(l), Obj::Max(r)) => {
if self.arg_pairs_share_known_equality_class(&[
(&l.left, &r.left),
(&l.right, &r.right),
]) {
Some(factual_equal_success_by_builtin_reason(
left, right, line_file, reason,
))
} else {
Some((StmtUnknown::new()).into())
}
}
(Obj::Min(l), Obj::Min(r)) => {
if self.arg_pairs_share_known_equality_class(&[
(&l.left, &r.left),
(&l.right, &r.right),
]) {
Some(factual_equal_success_by_builtin_reason(
left, right, line_file, reason,
))
} else {
Some((StmtUnknown::new()).into())
}
}
(Obj::Union(l), Obj::Union(r)) => {
if self.arg_pairs_share_known_equality_class(&[
(&l.left, &r.left),
(&l.right, &r.right),
]) {
Some(factual_equal_success_by_builtin_reason(
left, right, line_file, reason,
))
} else {
Some((StmtUnknown::new()).into())
}
}
(Obj::Intersect(l), Obj::Intersect(r)) => {
if self.arg_pairs_share_known_equality_class(&[
(&l.left, &r.left),
(&l.right, &r.right),
]) {
Some(factual_equal_success_by_builtin_reason(
left, right, line_file, reason,
))
} else {
Some((StmtUnknown::new()).into())
}
}
(Obj::SetMinus(l), Obj::SetMinus(r)) => {
if self.arg_pairs_share_known_equality_class(&[
(&l.left, &r.left),
(&l.right, &r.right),
]) {
Some(factual_equal_success_by_builtin_reason(
left, right, line_file, reason,
))
} else {
Some((StmtUnknown::new()).into())
}
}
(Obj::SetDiff(l), Obj::SetDiff(r)) => {
if self.arg_pairs_share_known_equality_class(&[
(&l.left, &r.left),
(&l.right, &r.right),
]) {
Some(factual_equal_success_by_builtin_reason(
left, right, line_file, reason,
))
} else {
Some((StmtUnknown::new()).into())
}
}
(Obj::Cup(l), Obj::Cup(r)) => {
if self.objs_have_same_known_equality_rc_in_some_env(&l.left, &r.left) {
Some(factual_equal_success_by_builtin_reason(
left, right, line_file, reason,
))
} else {
Some((StmtUnknown::new()).into())
}
}
(Obj::Cap(l), Obj::Cap(r)) => {
if self.objs_have_same_known_equality_rc_in_some_env(&l.left, &r.left) {
Some(factual_equal_success_by_builtin_reason(
left, right, line_file, reason,
))
} else {
Some((StmtUnknown::new()).into())
}
}
(Obj::PowerSet(l), Obj::PowerSet(r)) => {
if self.objs_have_same_known_equality_rc_in_some_env(&l.set, &r.set) {
Some(factual_equal_success_by_builtin_reason(
left, right, line_file, reason,
))
} else {
Some((StmtUnknown::new()).into())
}
}
(Obj::Choose(l), Obj::Choose(r)) => {
if self.objs_have_same_known_equality_rc_in_some_env(&l.set, &r.set) {
Some(factual_equal_success_by_builtin_reason(
left, right, line_file, reason,
))
} else {
Some((StmtUnknown::new()).into())
}
}
(Obj::CartDim(l), Obj::CartDim(r)) => {
if self.objs_have_same_known_equality_rc_in_some_env(&l.set, &r.set) {
Some(factual_equal_success_by_builtin_reason(
left, right, line_file, reason,
))
} else {
Some((StmtUnknown::new()).into())
}
}
(Obj::TupleDim(l), Obj::TupleDim(r)) => {
if self.objs_have_same_known_equality_rc_in_some_env(&l.arg, &r.arg) {
Some(factual_equal_success_by_builtin_reason(
left, right, line_file, reason,
))
} else {
Some((StmtUnknown::new()).into())
}
}
(Obj::Count(l), Obj::Count(r)) => {
if self.objs_have_same_known_equality_rc_in_some_env(&l.set, &r.set) {
Some(factual_equal_success_by_builtin_reason(
left, right, line_file, reason,
))
} else {
Some((StmtUnknown::new()).into())
}
}
(Obj::Range(l), Obj::Range(r)) => {
if self
.arg_pairs_share_known_equality_class(&[(&l.start, &r.start), (&l.end, &r.end)])
{
Some(factual_equal_success_by_builtin_reason(
left, right, line_file, reason,
))
} else {
Some((StmtUnknown::new()).into())
}
}
(Obj::Sum(l), Obj::Sum(r)) => {
if self.arg_pairs_share_known_equality_class(&[
(&l.start, &r.start),
(&l.end, &r.end),
(&l.func, &r.func),
]) {
Some(factual_equal_success_by_builtin_reason(
left, right, line_file, reason,
))
} else {
Some((StmtUnknown::new()).into())
}
}
(Obj::Product(l), Obj::Product(r)) => {
if self.arg_pairs_share_known_equality_class(&[
(&l.start, &r.start),
(&l.end, &r.end),
(&l.func, &r.func),
]) {
Some(factual_equal_success_by_builtin_reason(
left, right, line_file, reason,
))
} else {
Some((StmtUnknown::new()).into())
}
}
(Obj::ClosedRange(l), Obj::ClosedRange(r)) => {
if self
.arg_pairs_share_known_equality_class(&[(&l.start, &r.start), (&l.end, &r.end)])
{
Some(factual_equal_success_by_builtin_reason(
left, right, line_file, reason,
))
} else {
Some((StmtUnknown::new()).into())
}
}
(Obj::FiniteSeqSet(l), Obj::FiniteSeqSet(r)) => {
if self.arg_pairs_share_known_equality_class(&[(&l.set, &r.set), (&l.n, &r.n)]) {
Some(factual_equal_success_by_builtin_reason(
left, right, line_file, reason,
))
} else {
Some((StmtUnknown::new()).into())
}
}
(Obj::SeqSet(l), Obj::SeqSet(r)) => {
if self.arg_pairs_share_known_equality_class(&[(&l.set, &r.set)]) {
Some(factual_equal_success_by_builtin_reason(
left, right, line_file, reason,
))
} else {
Some((StmtUnknown::new()).into())
}
}
(Obj::MatrixSet(l), Obj::MatrixSet(r)) => {
if self.arg_pairs_share_known_equality_class(&[
(&l.set, &r.set),
(&l.row_len, &r.row_len),
(&l.col_len, &r.col_len),
]) {
Some(factual_equal_success_by_builtin_reason(
left, right, line_file, reason,
))
} else {
Some((StmtUnknown::new()).into())
}
}
(Obj::Proj(l), Obj::Proj(r)) => {
if self.arg_pairs_share_known_equality_class(&[(&l.set, &r.set), (&l.dim, &r.dim)])
{
Some(factual_equal_success_by_builtin_reason(
left, right, line_file, reason,
))
} else {
Some((StmtUnknown::new()).into())
}
}
(Obj::ObjAtIndex(l), Obj::ObjAtIndex(r)) => {
if self
.arg_pairs_share_known_equality_class(&[(&l.obj, &r.obj), (&l.index, &r.index)])
{
Some(factual_equal_success_by_builtin_reason(
left, right, line_file, reason,
))
} else {
Some((StmtUnknown::new()).into())
}
}
(Obj::Tuple(l), Obj::Tuple(r)) => {
if self.boxed_obj_vecs_share_known_equality_class(&l.args, &r.args) {
Some(factual_equal_success_by_builtin_reason(
left, right, line_file, reason,
))
} else {
Some((StmtUnknown::new()).into())
}
}
(Obj::ListSet(l), Obj::ListSet(r)) => {
if self.boxed_obj_vecs_share_known_equality_class(&l.list, &r.list) {
Some(factual_equal_success_by_builtin_reason(
left, right, line_file, reason,
))
} else {
Some((StmtUnknown::new()).into())
}
}
(Obj::Cart(l), Obj::Cart(r)) => {
if self.boxed_obj_vecs_share_known_equality_class(&l.args, &r.args) {
Some(factual_equal_success_by_builtin_reason(
left, right, line_file, reason,
))
} else {
Some((StmtUnknown::new()).into())
}
}
_ => None,
}
}
pub fn verify_equality_by_they_are_the_same_and_calculation(
&mut self,
left: &Obj,
right: &Obj,
line_file: LineFile,
_verify_state: &VerifyState,
) -> Result<(StmtResult, Obj, Obj), RuntimeError> {
if verify_equality_by_they_are_the_same(left, right) {
return Ok((
factual_equal_success_by_builtin_reason(
left,
right,
line_file,
"they are the same",
),
left.clone(),
right.clone(),
));
}
let left_resolved = self.resolve_obj(left);
let right_resolved = self.resolve_obj(right);
if left_resolved.two_objs_can_be_calculated_and_equal_by_calculation(&right_resolved) {
return Ok((
factual_equal_success_by_builtin_reason(left, right, line_file, "calculation"),
left_resolved,
right_resolved,
));
}
Ok((
StmtResult::StmtUnknown(StmtUnknown::new()),
left_resolved,
right_resolved,
))
}
}