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