Skip to main content

litex/verify/verify_builtin_rules/
in_fact_builtin.rs

1use crate::infer::obj_eligible_for_known_objs_in_fn_sets;
2use crate::prelude::*;
3use crate::verify::{
4    number_is_in_n, number_is_in_n_pos, number_is_in_q_neg, number_is_in_q_nz, number_is_in_q_pos,
5    number_is_in_r_neg, number_is_in_r_nz, number_is_in_r_pos, number_is_in_z, number_is_in_z_neg,
6    number_is_in_z_nz, verify_equality_by_builtin_rules::verify_equality_by_they_are_the_same,
7    verify_number_in_standard_set::is_integer_after_simplification, VerifyState,
8};
9fn number_in_set_verified_by_builtin_rules_result(in_fact: &InFact, reason: &str) -> StmtResult {
10    StmtResult::FactualStmtSuccess(
11        FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
12            in_fact.clone().into(),
13            reason.to_string(),
14            Vec::new(),
15        ),
16    )
17}
18
19fn not_in_fact_verified_by_builtin_rules_result(
20    not_in_fact: &NotInFact,
21    reason: &str,
22) -> StmtResult {
23    StmtResult::FactualStmtSuccess(
24        FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
25            not_in_fact.clone().into(),
26            reason.to_string(),
27            Vec::new(),
28        ),
29    )
30}
31
32fn arithmetic_obj_in_r_verified_by_builtin_rules_result(in_fact: &InFact) -> StmtResult {
33    StmtResult::FactualStmtSuccess(
34        FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
35            in_fact.clone().into(),
36            "arithmetic expression is in R".to_string(),
37            Vec::new(),
38        ),
39    )
40}
41
42fn builtin_in_fact_result_for_evaluated_number_in_standard_set(
43    in_fact: &InFact,
44    evaluated_number: &Number,
45    standard_set: &StandardSet,
46) -> StmtResult {
47    match standard_set {
48        StandardSet::R => number_in_set_verified_by_builtin_rules_result(in_fact, "number in R"),
49        StandardSet::RPos => {
50            if number_is_in_r_pos(evaluated_number) {
51                number_in_set_verified_by_builtin_rules_result(in_fact, "number in R_pos")
52            } else {
53                StmtResult::StmtUnknown(StmtUnknown::new())
54            }
55        }
56        StandardSet::RNeg => {
57            if number_is_in_r_neg(evaluated_number) {
58                number_in_set_verified_by_builtin_rules_result(in_fact, "number in R_neg")
59            } else {
60                StmtResult::StmtUnknown(StmtUnknown::new())
61            }
62        }
63        StandardSet::RNz => {
64            if number_is_in_r_nz(evaluated_number) {
65                number_in_set_verified_by_builtin_rules_result(in_fact, "number in R_nz")
66            } else {
67                StmtResult::StmtUnknown(StmtUnknown::new())
68            }
69        }
70        StandardSet::Q => number_in_set_verified_by_builtin_rules_result(in_fact, "number in Q"),
71        StandardSet::QPos => {
72            if number_is_in_q_pos(evaluated_number) {
73                number_in_set_verified_by_builtin_rules_result(in_fact, "number in Q_pos")
74            } else {
75                StmtResult::StmtUnknown(StmtUnknown::new())
76            }
77        }
78        StandardSet::QNeg => {
79            if number_is_in_q_neg(evaluated_number) {
80                number_in_set_verified_by_builtin_rules_result(in_fact, "number in Q_neg")
81            } else {
82                StmtResult::StmtUnknown(StmtUnknown::new())
83            }
84        }
85        StandardSet::QNz => {
86            if number_is_in_q_nz(evaluated_number) {
87                number_in_set_verified_by_builtin_rules_result(in_fact, "number in Q_nz")
88            } else {
89                StmtResult::StmtUnknown(StmtUnknown::new())
90            }
91        }
92        StandardSet::Z => {
93            if number_is_in_z(evaluated_number) {
94                number_in_set_verified_by_builtin_rules_result(in_fact, "number in Z")
95            } else {
96                StmtResult::StmtUnknown(StmtUnknown::new())
97            }
98        }
99        StandardSet::ZNeg => {
100            if number_is_in_z_neg(evaluated_number) {
101                number_in_set_verified_by_builtin_rules_result(in_fact, "number in Z_neg")
102            } else {
103                StmtResult::StmtUnknown(StmtUnknown::new())
104            }
105        }
106        StandardSet::ZNz => {
107            if number_is_in_z_nz(evaluated_number) {
108                number_in_set_verified_by_builtin_rules_result(in_fact, "number in Z_nz")
109            } else {
110                StmtResult::StmtUnknown(StmtUnknown::new())
111            }
112        }
113        StandardSet::N => {
114            if number_is_in_n(evaluated_number) {
115                number_in_set_verified_by_builtin_rules_result(in_fact, "number in N")
116            } else {
117                StmtResult::StmtUnknown(StmtUnknown::new())
118            }
119        }
120        StandardSet::NPos => {
121            if number_is_in_n_pos(evaluated_number) {
122                number_in_set_verified_by_builtin_rules_result(in_fact, "number in N_pos")
123            } else {
124                StmtResult::StmtUnknown(StmtUnknown::new())
125            }
126        }
127    }
128}
129
130fn builtin_not_in_fact_result_for_evaluated_number_in_standard_set(
131    not_in_fact: &NotInFact,
132    evaluated_number: &Number,
133    standard_set: &StandardSet,
134) -> StmtResult {
135    match standard_set {
136        StandardSet::R | StandardSet::Q => StmtResult::StmtUnknown(StmtUnknown::new()),
137        StandardSet::RPos => {
138            if !number_is_in_r_pos(evaluated_number) {
139                not_in_fact_verified_by_builtin_rules_result(not_in_fact, "number not in R_pos")
140            } else {
141                StmtResult::StmtUnknown(StmtUnknown::new())
142            }
143        }
144        StandardSet::RNeg => {
145            if !number_is_in_r_neg(evaluated_number) {
146                not_in_fact_verified_by_builtin_rules_result(not_in_fact, "number not in R_neg")
147            } else {
148                StmtResult::StmtUnknown(StmtUnknown::new())
149            }
150        }
151        StandardSet::RNz => {
152            if !number_is_in_r_nz(evaluated_number) {
153                not_in_fact_verified_by_builtin_rules_result(not_in_fact, "number not in R_nz")
154            } else {
155                StmtResult::StmtUnknown(StmtUnknown::new())
156            }
157        }
158        StandardSet::QPos => {
159            if !number_is_in_q_pos(evaluated_number) {
160                not_in_fact_verified_by_builtin_rules_result(not_in_fact, "number not in Q_pos")
161            } else {
162                StmtResult::StmtUnknown(StmtUnknown::new())
163            }
164        }
165        StandardSet::QNeg => {
166            if !number_is_in_q_neg(evaluated_number) {
167                not_in_fact_verified_by_builtin_rules_result(not_in_fact, "number not in Q_neg")
168            } else {
169                StmtResult::StmtUnknown(StmtUnknown::new())
170            }
171        }
172        StandardSet::QNz => {
173            if !number_is_in_q_nz(evaluated_number) {
174                not_in_fact_verified_by_builtin_rules_result(not_in_fact, "number not in Q_nz")
175            } else {
176                StmtResult::StmtUnknown(StmtUnknown::new())
177            }
178        }
179        StandardSet::Z => {
180            if !number_is_in_z(evaluated_number) {
181                not_in_fact_verified_by_builtin_rules_result(not_in_fact, "number not in Z")
182            } else {
183                StmtResult::StmtUnknown(StmtUnknown::new())
184            }
185        }
186        StandardSet::ZNeg => {
187            if !number_is_in_z_neg(evaluated_number) {
188                not_in_fact_verified_by_builtin_rules_result(not_in_fact, "number not in Z_neg")
189            } else {
190                StmtResult::StmtUnknown(StmtUnknown::new())
191            }
192        }
193        StandardSet::ZNz => {
194            if !number_is_in_z_nz(evaluated_number) {
195                not_in_fact_verified_by_builtin_rules_result(not_in_fact, "number not in Z_nz")
196            } else {
197                StmtResult::StmtUnknown(StmtUnknown::new())
198            }
199        }
200        StandardSet::N => {
201            if !number_is_in_n(evaluated_number) {
202                not_in_fact_verified_by_builtin_rules_result(not_in_fact, "number not in N")
203            } else {
204                StmtResult::StmtUnknown(StmtUnknown::new())
205            }
206        }
207        StandardSet::NPos => {
208            if !number_is_in_n_pos(evaluated_number) {
209                not_in_fact_verified_by_builtin_rules_result(not_in_fact, "number not in N_pos")
210            } else {
211                StmtResult::StmtUnknown(StmtUnknown::new())
212            }
213        }
214    }
215}
216
217impl Runtime {
218    pub fn verify_not_in_fact_with_builtin_rules(
219        &mut self,
220        not_in_fact: &NotInFact,
221        _verify_state: &VerifyState,
222    ) -> Result<StmtResult, RuntimeError> {
223        if let Obj::StandardSet(standard_set) = &not_in_fact.set {
224            if !matches!(&not_in_fact.element, Obj::Number(_)) {
225                if let Some(evaluated_number) =
226                    not_in_fact.element.evaluate_to_normalized_decimal_number()
227                {
228                    return Ok(
229                        builtin_not_in_fact_result_for_evaluated_number_in_standard_set(
230                            not_in_fact,
231                            &evaluated_number,
232                            standard_set,
233                        ),
234                    );
235                }
236            }
237        }
238        match (&not_in_fact.element, &not_in_fact.set) {
239            (Obj::Number(num), Obj::StandardSet(standard_set)) => Ok(
240                builtin_not_in_fact_result_for_evaluated_number_in_standard_set(
241                    not_in_fact,
242                    num,
243                    standard_set,
244                ),
245            ),
246            _ => Ok((StmtUnknown::new()).into()),
247        }
248    }
249
250    fn maybe_verify_in_fact_max_min_pair_closed_standard_set(
251        &mut self,
252        in_fact: &InFact,
253        verify_state: &VerifyState,
254    ) -> Result<Option<StmtResult>, RuntimeError> {
255        let (left, right, set) = match (&in_fact.element, &in_fact.set) {
256            (Obj::Max(m), Obj::StandardSet(s)) => (m.left.as_ref(), m.right.as_ref(), s.clone()),
257            (Obj::Min(m), Obj::StandardSet(s)) => (m.left.as_ref(), m.right.as_ref(), s.clone()),
258            _ => return Ok(None),
259        };
260        if !matches!(
261            set,
262            StandardSet::RPos
263                | StandardSet::QPos
264                | StandardSet::RNeg
265                | StandardSet::QNeg
266                | StandardSet::ZNeg
267                | StandardSet::N
268                | StandardSet::NPos
269        ) {
270            return Ok(None);
271        }
272        let reason = format!("max/min: both operands in {}", set);
273        let set_obj: Obj = set.into();
274        let lf = in_fact.line_file.clone();
275        for operand in [left, right] {
276            let f: AtomicFact = InFact::new(operand.clone(), set_obj.clone(), lf.clone()).into();
277            if !self.non_equational_atomic_fact_holds_by_full_verify_pipeline(&f, verify_state)? {
278                return Ok(Some((StmtUnknown::new()).into()));
279            }
280        }
281        Ok(Some(number_in_set_verified_by_builtin_rules_result(
282            in_fact,
283            reason.as_str(),
284        )))
285    }
286
287    // Finite `sum` / `product` over a closed integer range: if the object is well-defined, its value
288    // is a real (used e.g. for `+` on real-valued operands).
289    fn verify_in_fact_sum_or_product_in_r(
290        &mut self,
291        in_fact: &InFact,
292        verify_state: &VerifyState,
293        reason: &str,
294    ) -> Result<StmtResult, RuntimeError> {
295        if self
296            .verify_obj_well_defined_and_store_cache(&in_fact.element, verify_state)
297            .is_ok()
298        {
299            Ok(number_in_set_verified_by_builtin_rules_result(
300                in_fact, reason,
301            ))
302        } else {
303            Ok((StmtUnknown::new()).into())
304        }
305    }
306
307    fn iterated_op_func_ret_set(&self, func: &Obj) -> Option<Obj> {
308        match func {
309            Obj::AnonymousFn(anon) => Some((*anon.body.ret_set).clone()),
310            Obj::FnObj(fn_obj) if fn_obj.body.is_empty() => match fn_obj.head.as_ref() {
311                FnObjHead::AnonymousFnLiteral(anon) => Some((*anon.body.ret_set).clone()),
312                _ => {
313                    let function_name_obj: Obj = (*fn_obj.head).clone().into();
314                    self.get_object_in_fn_set_or_restrict(&function_name_obj)
315                        .map(|fn_set_body| (*fn_set_body.ret_set).clone())
316                }
317            },
318            _ => self
319                .get_object_in_fn_set_or_restrict(func)
320                .map(|fn_set_body| (*fn_set_body.ret_set).clone()),
321        }
322    }
323
324    // `sum(start, end, f)` / `product(start, end, f)` in `Z` when the iterand's declared return
325    // set is `Z` or `N_pos` (positive naturals are integers) and the whole iterated object is
326    // well-defined on the integer interval.
327    // Example: `sum(1, n, 'Z(x){x}) $in Z`, `product(1, a, 'N_pos(x){x}) $in Z`.
328    fn verify_in_fact_sum_or_product_in_z_by_iterand_ret_set(
329        &mut self,
330        in_fact: &InFact,
331        func: &Obj,
332        verify_state: &VerifyState,
333        op: &str,
334    ) -> Result<StmtResult, RuntimeError> {
335        if self
336            .verify_obj_well_defined_and_store_cache(&in_fact.element, verify_state)
337            .is_err()
338        {
339            return Ok((StmtUnknown::new()).into());
340        }
341        let Some(ret_set) = self.iterated_op_func_ret_set(func) else {
342            return Ok((StmtUnknown::new()).into());
343        };
344        let z_obj: Obj = StandardSet::Z.into();
345        let n_pos_obj: Obj = StandardSet::NPos.into();
346        let reason = if verify_equality_by_they_are_the_same(&ret_set, &z_obj) {
347            format!("{op}: iterand return set is Z")
348        } else if verify_equality_by_they_are_the_same(&ret_set, &n_pos_obj) {
349            format!("{op}: iterand return set is N_pos (subset of Z)")
350        } else {
351            return Ok((StmtUnknown::new()).into());
352        };
353        Ok(number_in_set_verified_by_builtin_rules_result(
354            in_fact,
355            reason.as_str(),
356        ))
357    }
358
359    // `sum(start, end, f)` / `product(start, end, f)` in `Q` when the iterand's declared return
360    // set is `Q` and the whole iterated object is well-defined on the integer interval.
361    fn verify_in_fact_sum_or_product_in_q_by_iterand_ret_set(
362        &mut self,
363        in_fact: &InFact,
364        func: &Obj,
365        verify_state: &VerifyState,
366        op: &str,
367    ) -> Result<StmtResult, RuntimeError> {
368        if self
369            .verify_obj_well_defined_and_store_cache(&in_fact.element, verify_state)
370            .is_err()
371        {
372            return Ok((StmtUnknown::new()).into());
373        }
374        let Some(ret_set) = self.iterated_op_func_ret_set(func) else {
375            return Ok((StmtUnknown::new()).into());
376        };
377        let q_obj: Obj = StandardSet::Q.into();
378        if !verify_equality_by_they_are_the_same(&ret_set, &q_obj) {
379            return Ok((StmtUnknown::new()).into());
380        }
381        let reason = format!("{op}: iterand return set is Q");
382        Ok(number_in_set_verified_by_builtin_rules_result(
383            in_fact,
384            reason.as_str(),
385        ))
386    }
387
388    // `sum(start, end, f)` / `product(start, end, f)` in `N_pos` when the iterand's declared
389    // return set is `N_pos` and the whole iterated object is well-defined on the integer interval.
390    // Example: `product(1, a, 'N_pos(x){x}) $in N_pos`.
391    fn verify_in_fact_sum_or_product_in_n_pos_by_iterand_ret_set(
392        &mut self,
393        in_fact: &InFact,
394        func: &Obj,
395        verify_state: &VerifyState,
396        op: &str,
397    ) -> Result<StmtResult, RuntimeError> {
398        if self
399            .verify_obj_well_defined_and_store_cache(&in_fact.element, verify_state)
400            .is_err()
401        {
402            return Ok((StmtUnknown::new()).into());
403        }
404        let Some(ret_set) = self.iterated_op_func_ret_set(func) else {
405            return Ok((StmtUnknown::new()).into());
406        };
407        let n_pos_obj: Obj = StandardSet::NPos.into();
408        if !verify_equality_by_they_are_the_same(&ret_set, &n_pos_obj) {
409            return Ok((StmtUnknown::new()).into());
410        }
411        let reason = format!("{op}: iterand return set is N_pos");
412        Ok(number_in_set_verified_by_builtin_rules_result(
413            in_fact,
414            reason.as_str(),
415        ))
416    }
417
418    /// `f(args) $in S` when `S` agrees with the head's typing return set and the application is
419    /// well-defined in the current environment (e.g. domain facts already assumed).
420    fn verify_in_fact_fn_application_in_typed_return_set(
421        &mut self,
422        fn_obj: &FnObj,
423        in_fact: &InFact,
424        verify_state: &VerifyState,
425    ) -> Result<StmtResult, RuntimeError> {
426        let typed_ret = match fn_obj.head.as_ref() {
427            FnObjHead::AnonymousFnLiteral(a) => (*a.body.ret_set).clone(),
428            _ => {
429                let head_obj: Obj = (*fn_obj.head.clone()).into();
430                let Some(fn_set) = self.get_cloned_object_in_fn_set(&head_obj) else {
431                    return Ok((StmtUnknown::new()).into());
432                };
433                (*fn_set.ret_set).clone()
434            }
435        };
436        let target = &in_fact.set;
437        let ret_matches = verify_equality_by_they_are_the_same(target, &typed_ret)
438            || self
439                .verify_equal_fact(
440                    &EqualFact::new(target.clone(), typed_ret.clone(), in_fact.line_file.clone()),
441                    verify_state,
442                )?
443                .is_true();
444        if !ret_matches {
445            return Ok((StmtUnknown::new()).into());
446        }
447        if self
448            .verify_obj_well_defined_and_store_cache(&Obj::FnObj(fn_obj.clone()), verify_state)
449            .is_err()
450        {
451            return Ok((StmtUnknown::new()).into());
452        }
453        Ok(
454            FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
455                in_fact.clone().into(),
456                "fn application in declared return set (well-defined under typing)".to_string(),
457                Vec::new(),
458            )
459            .into(),
460        )
461    }
462
463    pub fn verify_in_fact_with_builtin_rules(
464        &mut self,
465        in_fact: &InFact,
466        verify_state: &VerifyState,
467    ) -> Result<StmtResult, RuntimeError> {
468        if let Obj::StandardSet(standard_set) = &in_fact.set {
469            if !matches!(&in_fact.element, Obj::Number(_)) {
470                if let Some(evaluated_number) =
471                    in_fact.element.evaluate_to_normalized_decimal_number()
472                {
473                    let evaluation_membership_result =
474                        builtin_in_fact_result_for_evaluated_number_in_standard_set(
475                            in_fact,
476                            &evaluated_number,
477                            standard_set,
478                        );
479                    return Ok(evaluation_membership_result);
480                }
481            }
482        }
483        if let Some(result) =
484            self.maybe_verify_in_fact_max_min_pair_closed_standard_set(in_fact, verify_state)?
485        {
486            return Ok(result);
487        }
488        match (&in_fact.element, &in_fact.set) {
489            (Obj::Tuple(tuple), Obj::Cart(cart)) => {
490                return self.verify_in_fact_by_left_is_tuple_right_is_cart(
491                    in_fact,
492                    tuple,
493                    cart,
494                    verify_state,
495                );
496            }
497            (Obj::Number(num), Obj::StandardSet(standard_set)) => {
498                Ok(builtin_in_fact_result_for_evaluated_number_in_standard_set(
499                    in_fact,
500                    num,
501                    standard_set,
502                ))
503            }
504            (Obj::Sum(sum), Obj::StandardSet(StandardSet::NPos)) => self
505                .verify_in_fact_sum_or_product_in_n_pos_by_iterand_ret_set(
506                    in_fact,
507                    sum.func.as_ref(),
508                    verify_state,
509                    "sum",
510                ),
511            (Obj::Product(product), Obj::StandardSet(StandardSet::NPos)) => self
512                .verify_in_fact_sum_or_product_in_n_pos_by_iterand_ret_set(
513                    in_fact,
514                    product.func.as_ref(),
515                    verify_state,
516                    "product",
517                ),
518            (Obj::Add(add), Obj::StandardSet(StandardSet::N)) => {
519                self.verify_in_fact_add_in_n_from_summands_in_n(in_fact, add, verify_state)
520            }
521            (Obj::Mul(mul), Obj::StandardSet(StandardSet::N)) => {
522                self.verify_in_fact_mul_in_n_from_factors_in_n(in_fact, mul, verify_state)
523            }
524            (Obj::Add(add), Obj::StandardSet(StandardSet::NPos)) => {
525                self.verify_in_fact_add_in_n_pos_from_n_pos_and_n(in_fact, add, verify_state)
526            }
527            (Obj::Mul(mul), Obj::StandardSet(StandardSet::NPos)) => {
528                self.verify_in_fact_mul_in_n_pos_from_factors_in_n_pos(in_fact, mul, verify_state)
529            }
530            (_, Obj::StandardSet(StandardSet::NPos)) => {
531                self.verify_in_fact_n_pos_by_zero_less_and_in_z_or_n(in_fact, verify_state)
532            }
533            (_, Obj::ClosedRange(closed_range)) => self
534                .verify_in_fact_closed_range_by_order_bounds(in_fact, closed_range, verify_state),
535            (_, Obj::Range(range)) => {
536                self.verify_in_fact_open_range_by_order_bounds(in_fact, range, verify_state)
537            }
538            (
539                Obj::Add(_)
540                | Obj::Sub(_)
541                | Obj::Mul(_)
542                | Obj::Mod(_)
543                | Obj::Pow(_)
544                | Obj::Max(_)
545                | Obj::Min(_)
546                | Obj::Abs(_),
547                Obj::StandardSet(StandardSet::Z),
548            ) => self.verify_in_fact_arithmetic_expression_in_z(in_fact, verify_state),
549            (
550                Obj::Add(_)
551                | Obj::Sub(_)
552                | Obj::Mul(_)
553                | Obj::Div(_)
554                | Obj::Pow(_)
555                | Obj::Max(_)
556                | Obj::Min(_)
557                | Obj::Abs(_),
558                Obj::StandardSet(StandardSet::Q),
559            ) => self.verify_in_fact_arithmetic_expression_in_q(in_fact, verify_state),
560            (
561                Obj::Add(_)
562                | Obj::Sub(_)
563                | Obj::Mul(_)
564                | Obj::Div(_)
565                | Obj::Mod(_)
566                | Obj::Pow(_)
567                | Obj::Max(_)
568                | Obj::Min(_),
569                Obj::StandardSet(StandardSet::RNeg),
570            ) => self.verify_in_fact_arithmetic_expression_in_standard_negative_set(
571                in_fact,
572                verify_state,
573                StandardSet::RNeg,
574            ),
575            (
576                Obj::Add(_)
577                | Obj::Sub(_)
578                | Obj::Mul(_)
579                | Obj::Div(_)
580                | Obj::Mod(_)
581                | Obj::Pow(_)
582                | Obj::Max(_)
583                | Obj::Min(_),
584                Obj::StandardSet(StandardSet::QNeg),
585            ) => self.verify_in_fact_arithmetic_expression_in_standard_negative_set(
586                in_fact,
587                verify_state,
588                StandardSet::QNeg,
589            ),
590            (
591                Obj::Add(_)
592                | Obj::Sub(_)
593                | Obj::Mul(_)
594                | Obj::Div(_)
595                | Obj::Mod(_)
596                | Obj::Pow(_)
597                | Obj::Max(_)
598                | Obj::Min(_),
599                Obj::StandardSet(StandardSet::ZNeg),
600            ) => self.verify_in_fact_arithmetic_expression_in_standard_negative_set(
601                in_fact,
602                verify_state,
603                StandardSet::ZNeg,
604            ),
605            (
606                Obj::Add(_)
607                | Obj::Sub(_)
608                | Obj::Mul(_)
609                | Obj::Div(_)
610                | Obj::Mod(_)
611                | Obj::Pow(_)
612                | Obj::Max(_)
613                | Obj::Min(_)
614                | Obj::Abs(_)
615                | Obj::Log(_),
616                Obj::StandardSet(StandardSet::R),
617            ) => Ok(arithmetic_obj_in_r_verified_by_builtin_rules_result(
618                in_fact,
619            )),
620            (Obj::Sum(_), Obj::StandardSet(StandardSet::R)) => self
621                .verify_in_fact_sum_or_product_in_r(
622                    in_fact,
623                    verify_state,
624                    "sum: well-defined on an integer range, in R",
625                ),
626            (Obj::Product(_), Obj::StandardSet(StandardSet::R)) => self
627                .verify_in_fact_sum_or_product_in_r(
628                    in_fact,
629                    verify_state,
630                    "product: well-defined on an integer range, in R",
631                ),
632            (Obj::Sum(sum), Obj::StandardSet(StandardSet::Z)) => self
633                .verify_in_fact_sum_or_product_in_z_by_iterand_ret_set(
634                    in_fact,
635                    sum.func.as_ref(),
636                    verify_state,
637                    "sum",
638                ),
639            (Obj::Product(product), Obj::StandardSet(StandardSet::Z)) => self
640                .verify_in_fact_sum_or_product_in_z_by_iterand_ret_set(
641                    in_fact,
642                    product.func.as_ref(),
643                    verify_state,
644                    "product",
645                ),
646            (Obj::Sum(sum), Obj::StandardSet(StandardSet::Q)) => self
647                .verify_in_fact_sum_or_product_in_q_by_iterand_ret_set(
648                    in_fact,
649                    sum.func.as_ref(),
650                    verify_state,
651                    "sum",
652                ),
653            (Obj::Product(product), Obj::StandardSet(StandardSet::Q)) => self
654                .verify_in_fact_sum_or_product_in_q_by_iterand_ret_set(
655                    in_fact,
656                    product.func.as_ref(),
657                    verify_state,
658                    "product",
659                ),
660            (Obj::ListSet(list_set), Obj::PowerSet(power_set)) => self
661                .verify_in_fact_list_set_in_power_set_defines_membership(
662                    in_fact,
663                    list_set,
664                    power_set,
665                    verify_state,
666                ),
667            (Obj::SetBuilder(set_builder), Obj::PowerSet(power_set)) => self
668                .verify_in_fact_set_builder_in_power_set_via_param_subset(
669                    in_fact,
670                    set_builder,
671                    power_set,
672                    verify_state,
673                ),
674            (Obj::Choose(choose), where_is_obj) => {
675                let choose_from = choose.set.clone();
676                let equal_fact = EqualFact::new(
677                    *choose_from,
678                    where_is_obj.clone(),
679                    in_fact.line_file.clone(),
680                )
681                .into();
682                let equal_fact_verify_result =
683                    self.verify_atomic_fact(&equal_fact, verify_state)?;
684                if equal_fact_verify_result.is_true() {
685                    return Ok((FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
686                            in_fact.clone().into(),
687                            "By ZFC, we can choose an element from a nonempty set whose elements are all nonempty.".to_string(),
688                            Vec::new(),
689                        )).into());
690                } else {
691                    return Ok((StmtUnknown::new()).into());
692                }
693            }
694            (_, Obj::ListSet(list_set)) => self.verify_in_fact_by_equal_to_one_element_in_list_set(
695                in_fact,
696                list_set,
697                verify_state,
698            ),
699            (Obj::AnonymousFn(anon), Obj::FnSet(expected_fn_set)) => self
700                .verify_in_fact_anonymous_fn_signature_matches_fn_set(
701                    anon,
702                    expected_fn_set,
703                    in_fact,
704                    verify_state,
705                ),
706            (element, Obj::FnSet(expected_fn_set))
707                if obj_eligible_for_known_objs_in_fn_sets(element) =>
708            {
709                self.verify_in_fact_element_in_fn_set_by_stored_definition(
710                    element,
711                    expected_fn_set,
712                    in_fact,
713                )
714            }
715            (_, Obj::FamilyObj(family_ty)) => {
716                self.verify_obj_satisfies_family(in_fact.element.clone(), family_ty, verify_state)
717            }
718            (Obj::FiniteSeqListObj(list), Obj::FiniteSeqSet(fs)) => {
719                let lf = in_fact.line_file.clone();
720                let len_obj: Obj = Number::new(list.objs.len().to_string()).into();
721                let len_eq_n: AtomicFact =
722                    EqualFact::new(len_obj, (*fs.n).clone(), lf.clone()).into();
723                if !self.verify_atomic_fact(&len_eq_n, verify_state)?.is_true() {
724                    return Ok((StmtUnknown::new()).into());
725                }
726                for o in list.objs.iter() {
727                    let f: AtomicFact =
728                        InFact::new((**o).clone(), (*fs.set).clone(), lf.clone()).into();
729                    if !self.non_equational_atomic_fact_holds_by_full_verify_pipeline(
730                        &f,
731                        verify_state,
732                    )? {
733                        return Ok((StmtUnknown::new()).into());
734                    }
735                }
736                Ok(number_in_set_verified_by_builtin_rules_result(
737                    in_fact,
738                    "finite_seq list: length equals n and each entry in co-domain",
739                ))
740            }
741            (Obj::MatrixListObj(list), Obj::MatrixSet(ms)) => {
742                let lf = in_fact.line_file.clone();
743                let n_rows_obj: Obj = Number::new(list.rows.len().to_string()).into();
744                let row_eq: AtomicFact =
745                    EqualFact::new(n_rows_obj, (*ms.row_len).clone(), lf.clone()).into();
746                if !self.verify_atomic_fact(&row_eq, verify_state)?.is_true() {
747                    return Ok((StmtUnknown::new()).into());
748                }
749                for row in list.rows.iter() {
750                    let n_col_obj: Obj = Number::new(row.len().to_string()).into();
751                    let col_eq: AtomicFact =
752                        EqualFact::new(n_col_obj, (*ms.col_len).clone(), lf.clone()).into();
753                    if !self.verify_atomic_fact(&col_eq, verify_state)?.is_true() {
754                        return Ok((StmtUnknown::new()).into());
755                    }
756                    for o in row.iter() {
757                        let f: AtomicFact =
758                            InFact::new((**o).clone(), (*ms.set).clone(), lf.clone()).into();
759                        if !self.non_equational_atomic_fact_holds_by_full_verify_pipeline(
760                            &f,
761                            verify_state,
762                        )? {
763                            return Ok((StmtUnknown::new()).into());
764                        }
765                    }
766                }
767                Ok(number_in_set_verified_by_builtin_rules_result(
768                    in_fact,
769                    "matrix literal: shape matches matrix(...) and each entry in co-domain",
770                ))
771            }
772            (_, Obj::FiniteSeqSet(fs)) => {
773                let fn_set = self.finite_seq_set_to_fn_set(fs, in_fact.line_file.clone());
774                let expanded = InFact::new(
775                    in_fact.element.clone(),
776                    fn_set.into(),
777                    in_fact.line_file.clone(),
778                );
779                self.verify_atomic_fact(&expanded.into(), verify_state)
780            }
781            (_, Obj::SeqSet(ss)) => {
782                let fn_set = self.seq_set_to_fn_set(ss, in_fact.line_file.clone());
783                let expanded = InFact::new(
784                    in_fact.element.clone(),
785                    fn_set.into(),
786                    in_fact.line_file.clone(),
787                );
788                self.verify_atomic_fact(&expanded.into(), verify_state)
789            }
790            (_, Obj::MatrixSet(ms)) => {
791                let fn_set = self.matrix_set_to_fn_set(ms, in_fact.line_file.clone());
792                let expanded = InFact::new(
793                    in_fact.element.clone(),
794                    fn_set.into(),
795                    in_fact.line_file.clone(),
796                );
797                self.verify_atomic_fact(&expanded.into(), verify_state)
798            }
799            (_, target_set_obj) => {
800                if let Obj::FnObj(fn_obj) = &in_fact.element {
801                    let fn_try = self.verify_in_fact_fn_application_in_typed_return_set(
802                        fn_obj,
803                        in_fact,
804                        verify_state,
805                    )?;
806                    if fn_try.is_true() {
807                        return Ok(fn_try);
808                    }
809                }
810                self.verify_in_fact_by_known_standard_subset_membership(in_fact, target_set_obj)
811            }
812        }
813    }
814
815    // `a + b $in N` when `a $in N` and `b $in N` (closure under addition).
816    // Example: `forall a, b N: a + b $in N`.
817    fn verify_in_fact_add_in_n_from_summands_in_n(
818        &mut self,
819        in_fact: &InFact,
820        add: &Add,
821        verify_state: &VerifyState,
822    ) -> Result<StmtResult, RuntimeError> {
823        if let Some(evaluated_number) = in_fact.element.evaluate_to_normalized_decimal_number() {
824            return Ok(builtin_in_fact_result_for_evaluated_number_in_standard_set(
825                in_fact,
826                &evaluated_number,
827                &StandardSet::N,
828            ));
829        }
830        let n: Obj = StandardSet::N.into();
831        let lf = in_fact.line_file.clone();
832        let f_left: AtomicFact =
833            InFact::new(add.left.as_ref().clone(), n.clone(), lf.clone()).into();
834        let f_right: AtomicFact = InFact::new(add.right.as_ref().clone(), n, lf.clone()).into();
835        let r_left = self.verify_non_equational_atomic_fact(&f_left, verify_state, true)?;
836        if !r_left.is_true() {
837            return Ok((StmtUnknown::new()).into());
838        }
839        let r_right = self.verify_non_equational_atomic_fact(&f_right, verify_state, true)?;
840        if !r_right.is_true() {
841            return Ok((StmtUnknown::new()).into());
842        }
843        Ok(
844            FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
845                in_fact.clone().into(),
846                "N: a + b from a in N and b in N".to_string(),
847                vec![r_left, r_right],
848            )
849            .into(),
850        )
851    }
852
853    // `a * b $in N` when `a $in N` and `b $in N` (closure under multiplication).
854    // Example: `forall a, b N: a * b $in N`.
855    fn verify_in_fact_mul_in_n_from_factors_in_n(
856        &mut self,
857        in_fact: &InFact,
858        mul: &Mul,
859        verify_state: &VerifyState,
860    ) -> Result<StmtResult, RuntimeError> {
861        if let Some(evaluated_number) = in_fact.element.evaluate_to_normalized_decimal_number() {
862            return Ok(builtin_in_fact_result_for_evaluated_number_in_standard_set(
863                in_fact,
864                &evaluated_number,
865                &StandardSet::N,
866            ));
867        }
868        let n: Obj = StandardSet::N.into();
869        let lf = in_fact.line_file.clone();
870        let f_left: AtomicFact =
871            InFact::new(mul.left.as_ref().clone(), n.clone(), lf.clone()).into();
872        let f_right: AtomicFact = InFact::new(mul.right.as_ref().clone(), n, lf.clone()).into();
873        let r_left = self.verify_non_equational_atomic_fact(&f_left, verify_state, true)?;
874        if !r_left.is_true() {
875            return Ok((StmtUnknown::new()).into());
876        }
877        let r_right = self.verify_non_equational_atomic_fact(&f_right, verify_state, true)?;
878        if !r_right.is_true() {
879            return Ok((StmtUnknown::new()).into());
880        }
881        Ok(
882            FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
883                in_fact.clone().into(),
884                "N: a * b from a in N and b in N".to_string(),
885                vec![r_left, r_right],
886            )
887            .into(),
888        )
889    }
890
891    // `a + b $in N_pos` when both summands are in `N_pos`, or one summand is in
892    // `N_pos` and the other is in `N`.
893    // Example: `forall a, b N_pos: a + b $in N_pos`.
894    fn verify_in_fact_add_in_n_pos_from_n_pos_and_n(
895        &mut self,
896        in_fact: &InFact,
897        add: &Add,
898        verify_state: &VerifyState,
899    ) -> Result<StmtResult, RuntimeError> {
900        if let Some(evaluated_number) = in_fact.element.evaluate_to_normalized_decimal_number() {
901            return Ok(builtin_in_fact_result_for_evaluated_number_in_standard_set(
902                in_fact,
903                &evaluated_number,
904                &StandardSet::NPos,
905            ));
906        }
907        let n_pos: Obj = StandardSet::NPos.into();
908        let n: Obj = StandardSet::N.into();
909        let lf = in_fact.line_file.clone();
910
911        let left_n_pos: AtomicFact =
912            InFact::new(add.left.as_ref().clone(), n_pos.clone(), lf.clone()).into();
913        let right_n_pos_for_pair: AtomicFact =
914            InFact::new(add.right.as_ref().clone(), n_pos.clone(), lf.clone()).into();
915        let r_left_n_pos_for_pair =
916            self.verify_non_equational_atomic_fact(&left_n_pos, verify_state, true)?;
917        if r_left_n_pos_for_pair.is_true() {
918            let r_right_n_pos_for_pair =
919                self.verify_non_equational_atomic_fact(&right_n_pos_for_pair, verify_state, true)?;
920            if r_right_n_pos_for_pair.is_true() {
921                return Ok(
922                    FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
923                        in_fact.clone().into(),
924                        "N_pos: a + b from a in N_pos and b in N_pos".to_string(),
925                        vec![r_left_n_pos_for_pair, r_right_n_pos_for_pair],
926                    )
927                    .into(),
928                );
929            }
930        }
931
932        let right_n: AtomicFact =
933            InFact::new(add.right.as_ref().clone(), n.clone(), lf.clone()).into();
934        let r_left_n_pos =
935            self.verify_non_equational_atomic_fact(&left_n_pos, verify_state, true)?;
936        if r_left_n_pos.is_true() {
937            let r_right_n = self.verify_non_equational_atomic_fact(&right_n, verify_state, true)?;
938            if r_right_n.is_true() {
939                return Ok(
940                    FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
941                        in_fact.clone().into(),
942                        "N_pos: a + b from a in N_pos and b in N".to_string(),
943                        vec![r_left_n_pos, r_right_n],
944                    )
945                    .into(),
946                );
947            }
948        }
949
950        let left_n: AtomicFact =
951            InFact::new(add.left.as_ref().clone(), n.clone(), lf.clone()).into();
952        let right_n_pos: AtomicFact =
953            InFact::new(add.right.as_ref().clone(), n_pos, lf.clone()).into();
954        let r_left_n = self.verify_non_equational_atomic_fact(&left_n, verify_state, true)?;
955        if !r_left_n.is_true() {
956            return Ok((StmtUnknown::new()).into());
957        }
958        let r_right_n_pos =
959            self.verify_non_equational_atomic_fact(&right_n_pos, verify_state, true)?;
960        if !r_right_n_pos.is_true() {
961            return Ok((StmtUnknown::new()).into());
962        }
963        Ok(
964            FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
965                in_fact.clone().into(),
966                "N_pos: a + b from a in N and b in N_pos".to_string(),
967                vec![r_left_n, r_right_n_pos],
968            )
969            .into(),
970        )
971    }
972
973    // `a * b $in N_pos` when `a $in N_pos` and `b $in N_pos` (positive naturals are closed under multiplication).
974    // Example: `forall a, b N_pos: a * b $in N_pos`.
975    fn verify_in_fact_mul_in_n_pos_from_factors_in_n_pos(
976        &mut self,
977        in_fact: &InFact,
978        mul: &Mul,
979        verify_state: &VerifyState,
980    ) -> Result<StmtResult, RuntimeError> {
981        if let Some(evaluated_number) = in_fact.element.evaluate_to_normalized_decimal_number() {
982            return Ok(builtin_in_fact_result_for_evaluated_number_in_standard_set(
983                in_fact,
984                &evaluated_number,
985                &StandardSet::NPos,
986            ));
987        }
988        let n_pos: Obj = StandardSet::NPos.into();
989        let lf = in_fact.line_file.clone();
990        let f_left: AtomicFact =
991            InFact::new(mul.left.as_ref().clone(), n_pos.clone(), lf.clone()).into();
992        let f_right: AtomicFact = InFact::new(mul.right.as_ref().clone(), n_pos, lf.clone()).into();
993        let r_left = self.verify_non_equational_atomic_fact(&f_left, verify_state, true)?;
994        if !r_left.is_true() {
995            return Ok((StmtUnknown::new()).into());
996        }
997        let r_right = self.verify_non_equational_atomic_fact(&f_right, verify_state, true)?;
998        if !r_right.is_true() {
999            return Ok((StmtUnknown::new()).into());
1000        }
1001        Ok(
1002            FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
1003                in_fact.clone().into(),
1004                "N_pos: a * b from a in N_pos and b in N_pos".to_string(),
1005                vec![r_left, r_right],
1006            )
1007            .into(),
1008        )
1009    }
1010
1011    // `N_pos` = positive integers: from `0 < x` and (`x $in Z` or `x $in N`).
1012    fn verify_in_fact_n_pos_by_zero_less_and_in_z_or_n(
1013        &mut self,
1014        in_fact: &InFact,
1015        verify_state: &VerifyState,
1016    ) -> Result<StmtResult, RuntimeError> {
1017        let elem = &in_fact.element;
1018        let lf = in_fact.line_file.clone();
1019        let zero: Obj = Number::new("0".to_string()).into();
1020        let zero_lt_elem = LessFact::new(zero, elem.clone(), lf.clone()).into();
1021        if !self
1022            .non_equational_atomic_fact_holds_by_full_verify_pipeline(&zero_lt_elem, verify_state)?
1023        {
1024            return Ok((StmtUnknown::new()).into());
1025        }
1026
1027        let in_z = InFact::new(elem.clone(), StandardSet::Z.into(), lf.clone()).into();
1028        if self.non_equational_atomic_fact_holds_by_full_verify_pipeline(&in_z, verify_state)? {
1029            return Ok(number_in_set_verified_by_builtin_rules_result(
1030                in_fact,
1031                "N_pos: 0 < x and x in Z",
1032            ));
1033        }
1034
1035        let in_n = InFact::new(elem.clone(), StandardSet::N.into(), lf.clone()).into();
1036        if self.non_equational_atomic_fact_holds_by_full_verify_pipeline(&in_n, verify_state)? {
1037            return Ok(number_in_set_verified_by_builtin_rules_result(
1038                in_fact,
1039                "N_pos: 0 < x and x in N",
1040            ));
1041        }
1042
1043        Ok((StmtUnknown::new()).into())
1044    }
1045
1046    fn verify_in_fact_closed_range_by_order_bounds(
1047        &mut self,
1048        in_fact: &InFact,
1049        closed_range: &ClosedRange,
1050        verify_state: &VerifyState,
1051    ) -> Result<StmtResult, RuntimeError> {
1052        let elem = &in_fact.element;
1053        let lf = in_fact.line_file.clone();
1054        if !self.order_lower_bound_from_literals(
1055            elem,
1056            closed_range.start.as_ref(),
1057            &lf,
1058            verify_state,
1059        )? {
1060            return Ok((StmtUnknown::new()).into());
1061        }
1062        if !self.order_upper_bound_closed_from_literals(
1063            elem,
1064            closed_range.end.as_ref(),
1065            &lf,
1066            verify_state,
1067        )? {
1068            return Ok((StmtUnknown::new()).into());
1069        }
1070        Ok(number_in_set_verified_by_builtin_rules_result(
1071            in_fact,
1072            "in closed_range: a <= i and i <= b",
1073        ))
1074    }
1075
1076    fn verify_in_fact_open_range_by_order_bounds(
1077        &mut self,
1078        in_fact: &InFact,
1079        range: &Range,
1080        verify_state: &VerifyState,
1081    ) -> Result<StmtResult, RuntimeError> {
1082        let elem = &in_fact.element;
1083        let lf = in_fact.line_file.clone();
1084        if !self.order_lower_bound_from_literals(elem, range.start.as_ref(), &lf, verify_state)? {
1085            return Ok((StmtUnknown::new()).into());
1086        }
1087        if !self.order_upper_bound_open_from_literals(
1088            elem,
1089            range.end.as_ref(),
1090            &lf,
1091            verify_state,
1092        )? {
1093            return Ok((StmtUnknown::new()).into());
1094        }
1095        Ok(number_in_set_verified_by_builtin_rules_result(
1096            in_fact,
1097            "in range: a <= i and i < b",
1098        ))
1099    }
1100
1101    // When `x $in Z` and endpoints are integer literals: `lo <= x` iff `lo - 1 < x` (discrete lower).
1102    // Example: dom `1 < i` with `i $in Z` proves the lower side of `i $in range(2, 6)` / `closed_range(2, 5)`.
1103    fn order_lower_bound_from_literals(
1104        &mut self,
1105        elem: &Obj,
1106        lower: &Obj,
1107        lf: &LineFile,
1108        verify_state: &VerifyState,
1109    ) -> Result<bool, RuntimeError> {
1110        let weak: AtomicFact = LessEqualFact::new(lower.clone(), elem.clone(), lf.clone()).into();
1111        if self.non_equational_atomic_fact_holds_by_full_verify_pipeline(&weak, verify_state)? {
1112            return Ok(true);
1113        }
1114        let in_z: AtomicFact = InFact::new(elem.clone(), StandardSet::Z.into(), lf.clone()).into();
1115        if !self.non_equational_atomic_fact_holds_by_full_verify_pipeline(&in_z, verify_state)? {
1116            return Ok(false);
1117        }
1118        let Some(lower_num) = self.resolve_obj_to_number_resolved(lower) else {
1119            return Ok(false);
1120        };
1121        if !is_integer_after_simplification(&lower_num) {
1122            return Ok(false);
1123        }
1124        let pred = Obj::Sub(Sub::new(lower.clone(), Number::new("1".to_string()).into()));
1125        let Some(pred_n) = pred.evaluate_to_normalized_decimal_number() else {
1126            return Ok(false);
1127        };
1128        let strict: AtomicFact = LessFact::new(pred_n.into(), elem.clone(), lf.clone()).into();
1129        self.non_equational_atomic_fact_holds_by_full_verify_pipeline(&strict, verify_state)
1130    }
1131
1132    // When `x $in Z` and `hi` is an integer literal: `x < hi` iff `x <= hi - 1`.
1133    // Example: `i <= 5` and `i $in Z` gives the upper side of `i $in range(2, 6)`.
1134    fn order_upper_bound_open_from_literals(
1135        &mut self,
1136        elem: &Obj,
1137        upper: &Obj,
1138        lf: &LineFile,
1139        verify_state: &VerifyState,
1140    ) -> Result<bool, RuntimeError> {
1141        let strict: AtomicFact = LessFact::new(elem.clone(), upper.clone(), lf.clone()).into();
1142        if self.non_equational_atomic_fact_holds_by_full_verify_pipeline(&strict, verify_state)? {
1143            return Ok(true);
1144        }
1145        let in_z: AtomicFact = InFact::new(elem.clone(), StandardSet::Z.into(), lf.clone()).into();
1146        if !self.non_equational_atomic_fact_holds_by_full_verify_pipeline(&in_z, verify_state)? {
1147            return Ok(false);
1148        }
1149        let Some(upper_num) = self.resolve_obj_to_number_resolved(upper) else {
1150            return Ok(false);
1151        };
1152        if !is_integer_after_simplification(&upper_num) {
1153            return Ok(false);
1154        }
1155        let upper_minus_one =
1156            Obj::Sub(Sub::new(upper.clone(), Number::new("1".to_string()).into()));
1157        let Some(um) = upper_minus_one.evaluate_to_normalized_decimal_number() else {
1158            return Ok(false);
1159        };
1160        let weak: AtomicFact = LessEqualFact::new(elem.clone(), um.into(), lf.clone()).into();
1161        self.non_equational_atomic_fact_holds_by_full_verify_pipeline(&weak, verify_state)
1162    }
1163
1164    // When `x $in Z` and `hi` is an integer literal: `x <= hi` iff `x < hi + 1`.
1165    fn order_upper_bound_closed_from_literals(
1166        &mut self,
1167        elem: &Obj,
1168        upper: &Obj,
1169        lf: &LineFile,
1170        verify_state: &VerifyState,
1171    ) -> Result<bool, RuntimeError> {
1172        let weak: AtomicFact = LessEqualFact::new(elem.clone(), upper.clone(), lf.clone()).into();
1173        if self.non_equational_atomic_fact_holds_by_full_verify_pipeline(&weak, verify_state)? {
1174            return Ok(true);
1175        }
1176        let in_z: AtomicFact = InFact::new(elem.clone(), StandardSet::Z.into(), lf.clone()).into();
1177        if !self.non_equational_atomic_fact_holds_by_full_verify_pipeline(&in_z, verify_state)? {
1178            return Ok(false);
1179        }
1180        let Some(upper_num) = self.resolve_obj_to_number_resolved(upper) else {
1181            return Ok(false);
1182        };
1183        if !is_integer_after_simplification(&upper_num) {
1184            return Ok(false);
1185        }
1186        let hi_plus_one = Obj::Add(Add::new(upper.clone(), Number::new("1".to_string()).into()));
1187        let Some(hp) = hi_plus_one.evaluate_to_normalized_decimal_number() else {
1188            return Ok(false);
1189        };
1190        let strict: AtomicFact = LessFact::new(elem.clone(), hp.into(), lf.clone()).into();
1191        self.non_equational_atomic_fact_holds_by_full_verify_pipeline(&strict, verify_state)
1192    }
1193
1194    // Builtin closure of `Z` under `+`, `-`, `*`, `mod`, and `^` when direct operands are in `Z`
1195    // (`Pow` checks `base` and `exponent`; if the power normalizes to a decimal, the numeric branch above applies).
1196    fn verify_in_fact_arithmetic_expression_in_z(
1197        &mut self,
1198        in_fact: &InFact,
1199        verify_state: &VerifyState,
1200    ) -> Result<StmtResult, RuntimeError> {
1201        if let Some(evaluated_number) = in_fact.element.evaluate_to_normalized_decimal_number() {
1202            return Ok(builtin_in_fact_result_for_evaluated_number_in_standard_set(
1203                in_fact,
1204                &evaluated_number,
1205                &StandardSet::Z,
1206            ));
1207        }
1208        let z_obj: Obj = StandardSet::Z.into();
1209        let lf = in_fact.line_file.clone();
1210
1211        let mut require_in_z = |o: &Obj| -> Result<bool, RuntimeError> {
1212            let f = InFact::new(o.clone(), z_obj.clone(), lf.clone()).into();
1213            self.non_equational_atomic_fact_holds_by_full_verify_pipeline(&f, verify_state)
1214        };
1215
1216        let ok = match &in_fact.element {
1217            Obj::Add(a) => require_in_z(&a.left)? && require_in_z(&a.right)?,
1218            Obj::Sub(s) => require_in_z(&s.left)? && require_in_z(&s.right)?,
1219            Obj::Mul(m) => require_in_z(&m.left)? && require_in_z(&m.right)?,
1220            Obj::Mod(m) => require_in_z(&m.left)? && require_in_z(&m.right)?,
1221            Obj::Pow(p) => require_in_z(&p.base)? && require_in_z(&p.exponent)?,
1222            Obj::Max(m) => require_in_z(&m.left)? && require_in_z(&m.right)?,
1223            Obj::Min(m) => require_in_z(&m.left)? && require_in_z(&m.right)?,
1224            Obj::Abs(a) => require_in_z(a.arg.as_ref())?,
1225            _ => false,
1226        };
1227
1228        if !ok {
1229            return Ok((StmtUnknown::new()).into());
1230        }
1231
1232        Ok(
1233            (FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
1234                in_fact.clone().into(),
1235                "Z closure: operands in Z".to_string(),
1236                Vec::new(),
1237            ))
1238            .into(),
1239        )
1240    }
1241
1242    // Builtin closure of `Q` under `+`, `-`, `*`, `/` when both operands are in `Q`. For `^`, require
1243    // `base` in `Q` and `exponent` in `Z` (rational base with integer exponent stays in `Q`).
1244    fn verify_in_fact_arithmetic_expression_in_q(
1245        &mut self,
1246        in_fact: &InFact,
1247        verify_state: &VerifyState,
1248    ) -> Result<StmtResult, RuntimeError> {
1249        if let Some(evaluated_number) = in_fact.element.evaluate_to_normalized_decimal_number() {
1250            return Ok(builtin_in_fact_result_for_evaluated_number_in_standard_set(
1251                in_fact,
1252                &evaluated_number,
1253                &StandardSet::Q,
1254            ));
1255        }
1256        let q_obj: Obj = StandardSet::Q.into();
1257        let z_obj: Obj = StandardSet::Z.into();
1258        let lf = in_fact.line_file.clone();
1259
1260        let in_q = |slf: &mut Self, o: &Obj| -> Result<bool, RuntimeError> {
1261            let f = InFact::new(o.clone(), q_obj.clone(), lf.clone()).into();
1262            slf.non_equational_atomic_fact_holds_by_full_verify_pipeline(&f, verify_state)
1263        };
1264        let in_z = |slf: &mut Self, o: &Obj| -> Result<bool, RuntimeError> {
1265            let f = InFact::new(o.clone(), z_obj.clone(), lf.clone()).into();
1266            slf.non_equational_atomic_fact_holds_by_full_verify_pipeline(&f, verify_state)
1267        };
1268
1269        let ok = match &in_fact.element {
1270            Obj::Add(a) => in_q(self, &a.left)? && in_q(self, &a.right)?,
1271            Obj::Sub(s) => in_q(self, &s.left)? && in_q(self, &s.right)?,
1272            Obj::Mul(m) => in_q(self, &m.left)? && in_q(self, &m.right)?,
1273            Obj::Div(d) => in_q(self, &d.left)? && in_q(self, &d.right)?,
1274            Obj::Pow(p) => in_q(self, &p.base)? && in_z(self, &p.exponent)?,
1275            Obj::Max(m) => in_q(self, &m.left)? && in_q(self, &m.right)?,
1276            Obj::Min(m) => in_q(self, &m.left)? && in_q(self, &m.right)?,
1277            Obj::Abs(a) => in_q(self, a.arg.as_ref())?,
1278            _ => false,
1279        };
1280
1281        if !ok {
1282            return Ok((StmtUnknown::new()).into());
1283        }
1284
1285        Ok(
1286            (FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
1287                in_fact.clone().into(),
1288                "Q closure: +-*/ operands in Q; pow base in Q and exponent in Z".to_string(),
1289                Vec::new(),
1290            ))
1291            .into(),
1292        )
1293    }
1294
1295    fn verify_in_fact_arithmetic_expression_in_standard_negative_set(
1296        &mut self,
1297        in_fact: &InFact,
1298        verify_state: &VerifyState,
1299        target_negative_standard_set: StandardSet,
1300    ) -> Result<StmtResult, RuntimeError> {
1301        if let Some(evaluated_number) = in_fact.element.evaluate_to_normalized_decimal_number() {
1302            return Ok(builtin_in_fact_result_for_evaluated_number_in_standard_set(
1303                in_fact,
1304                &evaluated_number,
1305                &target_negative_standard_set,
1306            ));
1307        }
1308        let mul = match &in_fact.element {
1309            Obj::Mul(mul) => mul,
1310            _ => return Ok((StmtUnknown::new()).into()),
1311        };
1312        let product_in_r_fact = InFact::new(
1313            in_fact.element.clone(),
1314            StandardSet::R.into(),
1315            in_fact.line_file.clone(),
1316        )
1317        .into();
1318        if !self.non_equational_atomic_fact_holds_by_full_verify_pipeline(
1319            &product_in_r_fact,
1320            verify_state,
1321        )? {
1322            return Ok((StmtUnknown::new()).into());
1323        }
1324        if !self
1325            .mul_product_negative_when_factors_have_strict_opposite_sign_by_non_equational_verify(
1326                &mul.left,
1327                &mul.right,
1328                in_fact.line_file.clone(),
1329                verify_state,
1330            )?
1331        {
1332            return Ok((StmtUnknown::new()).into());
1333        }
1334        match target_negative_standard_set {
1335            StandardSet::RNeg => Ok(
1336                (FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
1337                    in_fact.clone().into(),
1338                    "mul_opposite_signs_product_in_R_neg".to_string(),
1339                    Vec::new(),
1340                ))
1341                .into(),
1342            ),
1343            StandardSet::QNeg => {
1344                let product_in_q_fact = InFact::new(
1345                    in_fact.element.clone(),
1346                    StandardSet::Q.into(),
1347                    in_fact.line_file.clone(),
1348                )
1349                .into();
1350                if self.non_equational_atomic_fact_holds_by_full_verify_pipeline(
1351                    &product_in_q_fact,
1352                    verify_state,
1353                )? {
1354                    Ok(
1355                        (FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
1356                            in_fact.clone().into(),
1357                            "mul_opposite_signs_product_in_Q_neg".to_string(),
1358                            Vec::new(),
1359                        ))
1360                        .into(),
1361                    )
1362                } else {
1363                    Ok((StmtUnknown::new()).into())
1364                }
1365            }
1366            StandardSet::ZNeg => {
1367                let product_in_z_fact = InFact::new(
1368                    in_fact.element.clone(),
1369                    StandardSet::Z.into(),
1370                    in_fact.line_file.clone(),
1371                )
1372                .into();
1373                if self.non_equational_atomic_fact_holds_by_full_verify_pipeline(
1374                    &product_in_z_fact,
1375                    verify_state,
1376                )? {
1377                    Ok(
1378                        (FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
1379                            in_fact.clone().into(),
1380                            "mul_opposite_signs_product_in_Z_neg".to_string(),
1381                            Vec::new(),
1382                        ))
1383                        .into(),
1384                    )
1385                } else {
1386                    Ok((StmtUnknown::new()).into())
1387                }
1388            }
1389            _ => Ok((StmtUnknown::new()).into()),
1390        }
1391    }
1392
1393    // `{x S : …} ⊆ S` always. If `S ⊆ T` then `{x S : …} ⊆ T`, so `{x S : …} ∈ 𝒫(T)`.
1394    // Example: from `N $subset Z`, deduce `{x N: x = x} $in power_set(Z)` once that subset is known.
1395    fn verify_in_fact_set_builder_in_power_set_via_param_subset(
1396        &mut self,
1397        in_fact: &InFact,
1398        set_builder: &SetBuilder,
1399        power_set: &PowerSet,
1400        verify_state: &VerifyState,
1401    ) -> Result<StmtResult, RuntimeError> {
1402        let base_set = power_set.set.as_ref();
1403        let subset_fact = SubsetFact::new(
1404            (*set_builder.param_set).clone(),
1405            base_set.clone(),
1406            in_fact.line_file.clone(),
1407        )
1408        .into();
1409        let verify_subset_result = self.verify_atomic_fact(&subset_fact, verify_state)?;
1410        if !verify_subset_result.is_true() {
1411            return Ok((StmtUnknown::new()).into());
1412        }
1413        let mut infer_result = InferResult::new();
1414        match verify_subset_result {
1415            StmtResult::FactualStmtSuccess(factual_success) => {
1416                infer_result.new_infer_result_inside(factual_success.infers.clone());
1417            }
1418            StmtResult::NonFactualStmtSuccess(non_factual_success) => {
1419                infer_result.new_infer_result_inside(non_factual_success.infers.clone());
1420            }
1421            StmtResult::StmtUnknown(_) => {
1422                return Ok((StmtUnknown::new()).into());
1423            }
1424        }
1425        let stmt = in_fact.clone().into();
1426        infer_result.new_fact(&stmt);
1427        Ok((FactualStmtSuccess::new_with_verified_by_builtin_rules(
1428            stmt,
1429            infer_result,
1430            VerifiedByResult::BuiltinRules(
1431                "set_builder in power_set: param_set subset of base implies builder defines a subset of base"
1432                    .to_string(),
1433            ),
1434        ))
1435        .into())
1436    }
1437
1438    fn verify_in_fact_list_set_in_power_set_defines_membership(
1439        &mut self,
1440        in_fact: &InFact,
1441        list_set: &ListSet,
1442        power_set: &PowerSet,
1443        verify_state: &VerifyState,
1444    ) -> Result<StmtResult, RuntimeError> {
1445        let base_set = power_set.set.as_ref();
1446        let mut infer_result = InferResult::new();
1447        for element_box in list_set.list.iter() {
1448            let element_obj = *element_box.clone();
1449            let element_in_base_fact =
1450                InFact::new(element_obj, base_set.clone(), in_fact.line_file.clone()).into();
1451            let verify_one_element_result =
1452                self.verify_atomic_fact(&element_in_base_fact, verify_state)?;
1453            if !verify_one_element_result.is_true() {
1454                return Ok((StmtUnknown::new()).into());
1455            }
1456            match verify_one_element_result {
1457                StmtResult::FactualStmtSuccess(factual_success) => {
1458                    infer_result.new_infer_result_inside(factual_success.infers.clone());
1459                }
1460                StmtResult::NonFactualStmtSuccess(non_factual_success) => {
1461                    infer_result.new_infer_result_inside(non_factual_success.infers.clone());
1462                }
1463                StmtResult::StmtUnknown(_) => {
1464                    return Ok((StmtUnknown::new()).into());
1465                }
1466            }
1467        }
1468        let stmt = in_fact.clone().into();
1469        infer_result.new_fact(&stmt);
1470        Ok((FactualStmtSuccess::new_with_verified_by_builtin_rules(
1471            stmt,
1472            infer_result,
1473            VerifiedByResult::BuiltinRules(
1474                "list_set in power_set: each element is in the base set".to_string(),
1475            ),
1476        ))
1477        .into())
1478    }
1479
1480    fn verify_in_fact_by_equal_to_one_element_in_list_set(
1481        &mut self,
1482        in_fact: &InFact,
1483        list_set: &ListSet,
1484        verify_state: &VerifyState,
1485    ) -> Result<StmtResult, RuntimeError> {
1486        for current_element_in_list_set in list_set.list.iter() {
1487            let equal_fact = EqualFact::new(
1488                in_fact.element.clone(),
1489                *current_element_in_list_set.clone(),
1490                in_fact.line_file.clone(),
1491            )
1492            .into();
1493            let equal_fact_verify_result = self.verify_atomic_fact(&equal_fact, verify_state)?;
1494            if equal_fact_verify_result.is_true() {
1495                return Ok(
1496                    (FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
1497                        in_fact.clone().into(),
1498                        format!(
1499                            "{} equals one element in list_set {}",
1500                            in_fact.element, in_fact.set
1501                        ),
1502                        Vec::new(),
1503                    ))
1504                    .into(),
1505                );
1506            }
1507        }
1508        Ok((StmtUnknown::new()).into())
1509    }
1510
1511    fn standard_subset_set_objs_for_target_set(target_set_obj: &Obj) -> Option<Vec<Obj>> {
1512        match target_set_obj {
1513            Obj::StandardSet(StandardSet::NPos) => Some(vec![]),
1514            Obj::StandardSet(StandardSet::N) => Some(vec![StandardSet::NPos.into()]),
1515            Obj::StandardSet(StandardSet::ZNeg) => Some(vec![]),
1516            Obj::StandardSet(StandardSet::ZNz) => {
1517                Some(vec![StandardSet::NPos.into(), StandardSet::ZNeg.into()])
1518            }
1519            Obj::StandardSet(StandardSet::Z) => Some(vec![
1520                StandardSet::NPos.into(),
1521                StandardSet::N.into(),
1522                StandardSet::ZNeg.into(),
1523                StandardSet::ZNz.into(),
1524            ]),
1525            Obj::StandardSet(StandardSet::QPos) => Some(vec![StandardSet::NPos.into()]),
1526            Obj::StandardSet(StandardSet::QNeg) => Some(vec![StandardSet::ZNeg.into()]),
1527            Obj::StandardSet(StandardSet::QNz) => Some(vec![
1528                StandardSet::NPos.into(),
1529                StandardSet::ZNeg.into(),
1530                StandardSet::ZNz.into(),
1531                StandardSet::QPos.into(),
1532                StandardSet::QNeg.into(),
1533            ]),
1534            Obj::StandardSet(StandardSet::Q) => Some(vec![
1535                StandardSet::NPos.into(),
1536                StandardSet::N.into(),
1537                StandardSet::ZNeg.into(),
1538                StandardSet::ZNz.into(),
1539                StandardSet::Z.into(),
1540                StandardSet::QPos.into(),
1541                StandardSet::QNeg.into(),
1542                StandardSet::QNz.into(),
1543            ]),
1544            Obj::StandardSet(StandardSet::RPos) => {
1545                Some(vec![StandardSet::NPos.into(), StandardSet::QPos.into()])
1546            }
1547            Obj::StandardSet(StandardSet::RNeg) => {
1548                Some(vec![StandardSet::ZNeg.into(), StandardSet::QNeg.into()])
1549            }
1550            Obj::StandardSet(StandardSet::RNz) => Some(vec![
1551                StandardSet::NPos.into(),
1552                StandardSet::ZNeg.into(),
1553                StandardSet::ZNz.into(),
1554                StandardSet::QPos.into(),
1555                StandardSet::QNeg.into(),
1556                StandardSet::QNz.into(),
1557                StandardSet::RPos.into(),
1558                StandardSet::RNeg.into(),
1559            ]),
1560            Obj::StandardSet(StandardSet::R) => Some(vec![
1561                StandardSet::NPos.into(),
1562                StandardSet::N.into(),
1563                StandardSet::ZNeg.into(),
1564                StandardSet::ZNz.into(),
1565                StandardSet::Z.into(),
1566                StandardSet::QPos.into(),
1567                StandardSet::QNeg.into(),
1568                StandardSet::QNz.into(),
1569                StandardSet::Q.into(),
1570                StandardSet::RPos.into(),
1571                StandardSet::RNeg.into(),
1572                StandardSet::RNz.into(),
1573            ]),
1574            _ => None,
1575        }
1576    }
1577
1578    // If the env already has `element $in fn_def` (from `known_objs_in_fn_sets`), compare to the RHS `fn ...`.
1579    fn verify_in_fact_element_in_fn_set_by_stored_definition(
1580        &mut self,
1581        element: &Obj,
1582        expected_fn_set: &FnSet,
1583        in_fact: &InFact,
1584    ) -> Result<StmtResult, RuntimeError> {
1585        let Some(stored_fn_set) = self.get_cloned_object_in_fn_set(element) else {
1586            return Ok((StmtUnknown::new()).into());
1587        };
1588        if stored_fn_set.to_string() == expected_fn_set.to_string() {
1589            return Ok(
1590                (FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
1591                    in_fact.clone().into(),
1592                    "fn membership: stored fn signature matches RHS".to_string(),
1593                    Vec::new(),
1594                ))
1595                .into(),
1596            );
1597        }
1598        let flat_stored =
1599            ParamGroupWithSet::collect_param_names(&stored_fn_set.params_def_with_set);
1600        let flat_expected =
1601            ParamGroupWithSet::collect_param_names(&expected_fn_set.body.params_def_with_set);
1602        if flat_stored.len() != flat_expected.len() {
1603            return Ok((StmtUnknown::new()).into());
1604        }
1605        let shared_names = self.generate_random_unused_names(flat_stored.len());
1606        let stored_norm =
1607            self.fn_set_alpha_renamed_for_display_compare(&stored_fn_set, &shared_names)?;
1608        let expected_norm =
1609            self.fn_set_alpha_renamed_for_display_compare(&expected_fn_set.body, &shared_names)?;
1610        if stored_norm.to_string() == expected_norm.to_string() {
1611            return Ok(
1612                (FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
1613                    in_fact.clone().into(),
1614                    "fn membership: stored fn signature matches RHS (alpha-renamed parameters)"
1615                        .to_string(),
1616                    Vec::new(),
1617                ))
1618                .into(),
1619            );
1620        }
1621        Ok((StmtUnknown::new()).into())
1622    }
1623
1624    /// `anon $in S` when `S` is a function space [`FnSet`] and the anonymous function's
1625    /// [`FnSetBody`] (params, dom facts, return set) matches `S` (same as comparing `S` to a
1626    /// [`FnSet`] built from the anon's body without the braced `equal_to`).
1627    fn verify_in_fact_anonymous_fn_signature_matches_fn_set(
1628        &mut self,
1629        anon: &AnonymousFn,
1630        expected_fn_set: &FnSet,
1631        in_fact: &InFact,
1632        verify_state: &VerifyState,
1633    ) -> Result<StmtResult, RuntimeError> {
1634        let _ = verify_state;
1635        let signature_from_anon = FnSet::new(
1636            anon.body.params_def_with_set.clone(),
1637            anon.body.dom_facts.clone(),
1638            (*anon.body.ret_set).clone(),
1639        )?;
1640        if signature_from_anon.to_string() == expected_fn_set.to_string() {
1641            return Ok(
1642                (FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
1643                    in_fact.clone().into(),
1644                    "anonymous function: signature (params, dom, co-domain) matches `fn` set"
1645                        .to_string(),
1646                    Vec::new(),
1647                ))
1648                .into(),
1649            );
1650        }
1651        let flat_a =
1652            ParamGroupWithSet::collect_param_names(&signature_from_anon.body.params_def_with_set);
1653        let flat_e =
1654            ParamGroupWithSet::collect_param_names(&expected_fn_set.body.params_def_with_set);
1655        if flat_a.len() != flat_e.len() {
1656            return Ok((StmtUnknown::new()).into());
1657        }
1658        let shared_names = self.generate_random_unused_names(flat_a.len());
1659        let a_norm = self
1660            .fn_set_alpha_renamed_for_display_compare(&signature_from_anon.body, &shared_names)?;
1661        let e_norm =
1662            self.fn_set_alpha_renamed_for_display_compare(&expected_fn_set.body, &shared_names)?;
1663        if a_norm.to_string() == e_norm.to_string() {
1664            return Ok(
1665                (FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
1666                    in_fact.clone().into(),
1667                    "anonymous function: signature matches `fn` set (alpha-renamed parameters)"
1668                        .to_string(),
1669                    Vec::new(),
1670                ))
1671                .into(),
1672            );
1673        }
1674        Ok((StmtUnknown::new()).into())
1675    }
1676
1677    fn verify_in_fact_by_known_standard_subset_membership(
1678        &mut self,
1679        in_fact: &InFact,
1680        target_set_obj: &Obj,
1681    ) -> Result<StmtResult, RuntimeError> {
1682        let standard_subset_set_objs =
1683            match Self::standard_subset_set_objs_for_target_set(target_set_obj) {
1684                Some(standard_subset_set_objs) => standard_subset_set_objs,
1685                None => return Ok((StmtUnknown::new()).into()),
1686            };
1687        for standard_subset_set_obj in standard_subset_set_objs.iter() {
1688            let in_fact_into_standard_subset = InFact::new(
1689                in_fact.element.clone(),
1690                standard_subset_set_obj.clone(),
1691                in_fact.line_file.clone(),
1692            )
1693            .into();
1694            let verify_result = self.verify_non_equational_atomic_fact_with_known_atomic_facts(
1695                &in_fact_into_standard_subset,
1696            )?;
1697            if verify_result.is_true() {
1698                return Ok(
1699                    (FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
1700                        in_fact.clone().into(),
1701                        format!(
1702                            "{} in {} implies in {} (standard subset relation)",
1703                            in_fact.element, standard_subset_set_obj, target_set_obj
1704                        ),
1705                        Vec::new(),
1706                    ))
1707                    .into(),
1708                );
1709            }
1710        }
1711        Ok((StmtUnknown::new()).into())
1712    }
1713
1714    fn verify_in_fact_by_left_is_tuple_right_is_cart(
1715        &mut self,
1716        in_fact: &InFact,
1717        tuple: &Tuple,
1718        cart: &Cart,
1719        verify_state: &VerifyState,
1720    ) -> Result<StmtResult, RuntimeError> {
1721        if tuple.args.len() < 2 {
1722            return Ok((StmtUnknown::new()).into());
1723        }
1724        if tuple.args.len() != cart.args.len() {
1725            return Ok((StmtUnknown::new()).into());
1726        }
1727
1728        for component_index in 0..tuple.args.len() {
1729            let tuple_component_obj = (*tuple.args[component_index]).clone();
1730            let cart_component_obj = (*cart.args[component_index]).clone();
1731            let component_in_fact = InFact::new(
1732                tuple_component_obj,
1733                cart_component_obj,
1734                in_fact.line_file.clone(),
1735            )
1736            .into();
1737            let component_verify_result =
1738                self.verify_atomic_fact(&component_in_fact, verify_state)?;
1739            if !component_verify_result.is_true() {
1740                return Ok((StmtUnknown::new()).into());
1741            }
1742        }
1743
1744        Ok(
1745            (FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
1746                in_fact.clone().into(),
1747                "tuple in cart: each component is in the corresponding cart factor".to_string(),
1748                Vec::new(),
1749            ))
1750            .into(),
1751        )
1752    }
1753}