1use super::order_normalize::normalize_positive_order_atomic_fact;
2use crate::prelude::*;
3
4impl Runtime {
5 pub fn verify_order_atomic_fact_numeric_builtin_only(
20 &mut self,
21 atomic_fact: &AtomicFact,
22 ) -> Result<StmtResult, RuntimeError> {
23 let vs = VerifyState::new(0, true);
24 if let Some(result) =
25 self.try_verify_order_nonnegative_from_membership_in_n(atomic_fact, &vs)?
26 {
27 return Ok(result);
28 }
29 if let Some(result) =
30 self.try_verify_order_one_le_from_membership_in_n_pos(atomic_fact, &vs)?
31 {
32 return Ok(result);
33 }
34 if let Some(result) =
35 self.try_verify_order_one_le_from_membership_in_n_and_nonzero(atomic_fact, &vs)?
36 {
37 return Ok(result);
38 }
39 if let Some(result) = self.try_verify_order_opposite_sign_mul_minus_one(atomic_fact, &vs)? {
40 return Ok(result);
41 }
42 if let Some(result) = self.verify_order_from_known_negated_complement(atomic_fact)? {
43 return Ok(result);
44 }
45 if let Some(result) = self.verify_negated_order_from_known_equivalent_order(atomic_fact)? {
46 return Ok(result);
47 }
48 if let Some(result) = self.verify_order_algebra_structural_builtin_rule(atomic_fact)? {
49 return Ok(result);
50 }
51 if let Some(result) = self.verify_zero_le_abs_builtin_rule(atomic_fact)? {
52 return Ok(result);
53 }
54 if let Some(result) = self.verify_abs_order_builtin_rule(atomic_fact)? {
55 return Ok(result);
56 }
57 if let Some(result) =
58 self.verify_zero_order_on_sub_from_two_sided_order_builtin_rule(atomic_fact)?
59 {
60 return Ok(result);
61 }
62 if let Some(result) =
63 self.verify_zero_le_add_from_known_atomic_facts_builtin_rule(atomic_fact)?
64 {
65 return Ok(result);
66 }
67 if let Some(result) =
68 self.verify_zero_lt_add_from_known_atomic_facts_builtin_rule(atomic_fact)?
69 {
70 return Ok(result);
71 }
72 if let Some(result) = self.verify_zero_le_even_integer_pow_builtin_rule(atomic_fact)? {
73 return Ok(result);
74 }
75 if let Some(result) =
76 self.verify_zero_lt_even_integer_pow_from_base_nonzero_builtin_rule(atomic_fact)?
77 {
78 return Ok(result);
79 }
80 if let Some(result) =
81 self.verify_zero_lt_pow_from_positive_base_real_exp_builtin_rule(atomic_fact)?
82 {
83 return Ok(result);
84 }
85 if let Some(result) =
86 self.verify_zero_le_pow_from_positive_base_real_exp_builtin_rule(atomic_fact)?
87 {
88 return Ok(result);
89 }
90 if let Some(result) =
91 self.verify_zero_le_pow_integer_exponent_from_nonneg_base_builtin_rule(atomic_fact)?
92 {
93 return Ok(result);
94 }
95 if let Some(result) =
96 self.verify_zero_le_mul_from_known_atomic_facts_builtin_rule(atomic_fact)?
97 {
98 return Ok(result);
99 }
100 if let Some(result) =
101 self.verify_zero_lt_mul_from_known_atomic_facts_builtin_rule(atomic_fact)?
102 {
103 return Ok(result);
104 }
105 if let Some(result) =
106 self.verify_zero_le_div_from_known_atomic_facts_builtin_rule(atomic_fact)?
107 {
108 return Ok(result);
109 }
110 if let Some(result) =
111 self.verify_zero_lt_div_from_known_atomic_facts_builtin_rule(atomic_fact)?
112 {
113 return Ok(result);
114 }
115
116 if let AtomicFact::LessEqualFact(less_equal_fact) = atomic_fact {
117 if less_equal_fact.left.to_string() == less_equal_fact.right.to_string() {
118 return Ok(StmtResult::FactualStmtSuccess(
119 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
120 less_equal_fact.clone().into(),
121 "less_equal_fact_equal".to_string(),
122 Vec::new(),
123 ),
124 ));
125 }
126 let strict_fact: Fact = LessFact::new(
127 less_equal_fact.left.clone(),
128 less_equal_fact.right.clone(),
129 less_equal_fact.line_file.clone(),
130 )
131 .into();
132 let strict_key = strict_fact.to_string();
133 let (cache_ok, _) = self.cache_known_facts_contains(&strict_key);
134 if cache_ok {
135 return Ok(StmtResult::FactualStmtSuccess(
136 FactualStmtSuccess::new_with_verified_by_known_fact(
137 less_equal_fact.clone().into(),
138 VerifiedByResult::cited_fact(
139 less_equal_fact.clone().into(),
140 strict_fact,
141 None,
142 ),
143 Vec::new(),
144 ),
145 ));
146 }
147 }
148 if let AtomicFact::GreaterEqualFact(greater_equal_fact) = atomic_fact {
149 if greater_equal_fact.left.to_string() == greater_equal_fact.right.to_string() {
150 return Ok(StmtResult::FactualStmtSuccess(
151 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
152 greater_equal_fact.clone().into(),
153 "greater_equal_fact_equal".to_string(),
154 Vec::new(),
155 ),
156 ));
157 }
158 }
159 if let Some(true) = self.verify_number_comparison_builtin_rule(atomic_fact) {
160 Ok(StmtResult::FactualStmtSuccess(
161 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
162 atomic_fact.clone().into(),
163 "number comparison".to_string(),
164 Vec::new(),
165 ),
166 ))
167 } else {
168 Ok(StmtResult::StmtUnknown(StmtUnknown::new()))
169 }
170 }
171
172 pub fn verify_number_comparison_builtin_rule(&self, atomic_fact: &AtomicFact) -> Option<bool> {
173 let normalized = normalize_positive_order_atomic_fact(atomic_fact)?;
174 match normalized {
175 AtomicFact::LessFact(less_fact) => {
176 if let Some(calculated_number_string_pair) =
177 self.calculate_obj_pair_to_number_strings(&less_fact.left, &less_fact.right)
178 {
179 return Some(matches!(
180 compare_number_strings(
181 &calculated_number_string_pair.0,
182 &calculated_number_string_pair.1
183 ),
184 NumberCompareResult::Less
185 ));
186 }
187 self.try_verify_numeric_order_via_div_elimination(
188 &less_fact.left,
189 &less_fact.right,
190 false,
191 )
192 }
193 AtomicFact::LessEqualFact(less_equal_fact) => {
194 if let Some(calculated_number_string_pair) = self
195 .calculate_obj_pair_to_number_strings(
196 &less_equal_fact.left,
197 &less_equal_fact.right,
198 )
199 {
200 let compare_result = compare_number_strings(
201 &calculated_number_string_pair.0,
202 &calculated_number_string_pair.1,
203 );
204 return Some(matches!(
205 compare_result,
206 NumberCompareResult::Less | NumberCompareResult::Equal
207 ));
208 }
209 self.try_verify_numeric_order_via_div_elimination(
210 &less_equal_fact.left,
211 &less_equal_fact.right,
212 true,
213 )
214 }
215 _ => None,
216 }
217 }
218}
219
220pub enum NumberCompareResult {
221 Less,
222 Equal,
223 Greater,
224}
225
226pub fn compare_normalized_number_str_to_zero(number_value: &str) -> NumberCompareResult {
228 compare_number_strings(number_value.trim(), "0")
229}
230
231fn parse_number_parts_for_comparison(number_value: &str) -> (bool, Vec<u8>, Vec<u8>) {
232 let trimmed_number_value = number_value.trim();
233 let (is_negative, magnitude_string) = if trimmed_number_value.starts_with('-') {
234 (true, trimmed_number_value[1..].trim())
235 } else {
236 (false, trimmed_number_value)
237 };
238
239 let (integer_part_string, fractional_part_string) = match magnitude_string.find('.') {
240 Some(dot_index) => (
241 &magnitude_string[..dot_index],
242 &magnitude_string[dot_index + 1..],
243 ),
244 None => (magnitude_string, ""),
245 };
246
247 let mut integer_digits: Vec<u8> = Vec::new();
248 if integer_part_string.is_empty() {
249 integer_digits.push(0);
250 } else {
251 for current_char in integer_part_string.chars() {
252 if current_char.is_ascii_digit() {
253 integer_digits.push(current_char as u8 - b'0');
254 }
255 }
256 if integer_digits.is_empty() {
257 integer_digits.push(0);
258 }
259 }
260
261 let mut fractional_digits: Vec<u8> = Vec::new();
262 for current_char in fractional_part_string.chars() {
263 if current_char.is_ascii_digit() {
264 fractional_digits.push(current_char as u8 - b'0');
265 }
266 }
267
268 (is_negative, integer_digits, fractional_digits)
269}
270
271fn digits_are_all_zero(digits: &[u8]) -> bool {
272 for digit in digits {
273 if *digit != 0 {
274 return false;
275 }
276 }
277 true
278}
279
280fn normalized_decimal_string_is_integer(number_value: &str) -> bool {
281 let (_, _integer_digits, fractional_digits) = parse_number_parts_for_comparison(number_value);
282 digits_are_all_zero(&fractional_digits)
283}
284
285pub(crate) fn normalized_decimal_string_is_even_integer(number_value: &str) -> bool {
286 if !normalized_decimal_string_is_integer(number_value) {
287 return false;
288 }
289 let (_is_negative, integer_digits, _fractional_digits) =
290 parse_number_parts_for_comparison(number_value);
291 let last_digit = integer_digits.last().copied().unwrap_or(0);
292 last_digit % 2 == 0
293}
294
295fn first_non_zero_integer_digit_index(integer_digits: &[u8]) -> usize {
296 let mut current_index = 0;
297 while current_index + 1 < integer_digits.len() && integer_digits[current_index] == 0 {
298 current_index += 1;
299 }
300 current_index
301}
302
303fn compare_non_negative_decimal_parts(
304 left_integer_digits: &[u8],
305 left_fractional_digits: &[u8],
306 right_integer_digits: &[u8],
307 right_fractional_digits: &[u8],
308) -> NumberCompareResult {
309 let left_integer_start_index = first_non_zero_integer_digit_index(left_integer_digits);
310 let right_integer_start_index = first_non_zero_integer_digit_index(right_integer_digits);
311
312 let left_effective_integer_len = left_integer_digits.len() - left_integer_start_index;
313 let right_effective_integer_len = right_integer_digits.len() - right_integer_start_index;
314 if left_effective_integer_len < right_effective_integer_len {
315 return NumberCompareResult::Less;
316 }
317 if left_effective_integer_len > right_effective_integer_len {
318 return NumberCompareResult::Greater;
319 }
320
321 let mut integer_index = 0;
322 while integer_index < left_effective_integer_len {
323 let left_digit = left_integer_digits[left_integer_start_index + integer_index];
324 let right_digit = right_integer_digits[right_integer_start_index + integer_index];
325 if left_digit < right_digit {
326 return NumberCompareResult::Less;
327 }
328 if left_digit > right_digit {
329 return NumberCompareResult::Greater;
330 }
331 integer_index += 1;
332 }
333
334 let fractional_compare_len = if left_fractional_digits.len() > right_fractional_digits.len() {
335 left_fractional_digits.len()
336 } else {
337 right_fractional_digits.len()
338 };
339 let mut fractional_index = 0;
340 while fractional_index < fractional_compare_len {
341 let left_digit = match left_fractional_digits.get(fractional_index) {
342 Some(digit) => *digit,
343 None => 0,
344 };
345 let right_digit = match right_fractional_digits.get(fractional_index) {
346 Some(digit) => *digit,
347 None => 0,
348 };
349 if left_digit < right_digit {
350 return NumberCompareResult::Less;
351 }
352 if left_digit > right_digit {
353 return NumberCompareResult::Greater;
354 }
355 fractional_index += 1;
356 }
357
358 NumberCompareResult::Equal
359}
360
361pub fn compare_number_strings(
362 left_number_value: &str,
363 right_number_value: &str,
364) -> NumberCompareResult {
365 let (left_is_negative, left_integer_digits, left_fractional_digits) =
366 parse_number_parts_for_comparison(left_number_value);
367 let (right_is_negative, right_integer_digits, right_fractional_digits) =
368 parse_number_parts_for_comparison(right_number_value);
369
370 let left_is_zero =
371 digits_are_all_zero(&left_integer_digits) && digits_are_all_zero(&left_fractional_digits);
372 let right_is_zero =
373 digits_are_all_zero(&right_integer_digits) && digits_are_all_zero(&right_fractional_digits);
374 if left_is_zero && right_is_zero {
375 return NumberCompareResult::Equal;
376 }
377
378 if left_is_negative && !left_is_zero && !right_is_negative {
379 return NumberCompareResult::Less;
380 }
381 if right_is_negative && !right_is_zero && !left_is_negative {
382 return NumberCompareResult::Greater;
383 }
384
385 let non_negative_compare_result = compare_non_negative_decimal_parts(
386 &left_integer_digits,
387 &left_fractional_digits,
388 &right_integer_digits,
389 &right_fractional_digits,
390 );
391 if left_is_negative && !left_is_zero && right_is_negative && !right_is_zero {
392 return match non_negative_compare_result {
393 NumberCompareResult::Less => NumberCompareResult::Greater,
394 NumberCompareResult::Equal => NumberCompareResult::Equal,
395 NumberCompareResult::Greater => NumberCompareResult::Less,
396 };
397 }
398
399 non_negative_compare_result
400}
401
402impl Runtime {
403 pub(crate) fn verify_non_equational_known_then_builtin_rules_only(
407 &mut self,
408 atomic_fact: &AtomicFact,
409 verify_state: &VerifyState,
410 ) -> Result<StmtResult, RuntimeError> {
411 let r = self.verify_non_equational_atomic_fact_with_known_atomic_facts(atomic_fact)?;
412 if r.is_true() {
413 return Ok(r);
414 }
415 self.verify_non_equational_atomic_fact_with_builtin_rules(atomic_fact, verify_state)
416 }
417
418 fn verify_zero_order_on_sub_expr(
419 &mut self,
420 zero: &Obj,
421 sub_expr: &Obj,
422 weak: bool,
423 line_file: &LineFile,
424 ) -> Result<StmtResult, RuntimeError> {
425 let fact: AtomicFact = if weak {
426 LessEqualFact::new(zero.clone(), sub_expr.clone(), line_file.clone()).into()
427 } else {
428 LessFact::new(zero.clone(), sub_expr.clone(), line_file.clone()).into()
429 };
430 let mut result = self.verify_non_equational_atomic_fact_with_known_atomic_facts(&fact)?;
431 if !result.is_true() {
432 result = self.verify_order_atomic_fact_numeric_builtin_only(&fact)?;
433 }
434 Ok(result)
435 }
436
437 fn try_verify_order_nonnegative_from_membership_in_n(
439 &mut self,
440 atomic_fact: &AtomicFact,
441 verify_state: &VerifyState,
442 ) -> Result<Option<StmtResult>, RuntimeError> {
443 let (n, line_file) = match atomic_fact {
444 AtomicFact::GreaterEqualFact(f) => {
445 let Some(z) = self.resolve_obj_to_number(&f.right) else {
446 return Ok(None);
447 };
448 if !matches!(
449 compare_normalized_number_str_to_zero(&z.normalized_value),
450 NumberCompareResult::Equal
451 ) {
452 return Ok(None);
453 }
454 (f.left.clone(), f.line_file.clone())
455 }
456 AtomicFact::LessEqualFact(f) => {
457 let Some(z) = self.resolve_obj_to_number(&f.left) else {
458 return Ok(None);
459 };
460 if !matches!(
461 compare_normalized_number_str_to_zero(&z.normalized_value),
462 NumberCompareResult::Equal
463 ) {
464 return Ok(None);
465 }
466 (f.right.clone(), f.line_file.clone())
467 }
468 _ => return Ok(None),
469 };
470 let in_n: AtomicFact = InFact::new(n, StandardSet::N.into(), line_file.clone()).into();
471 if self
472 .verify_non_equational_known_then_builtin_rules_only(&in_n, verify_state)?
473 .is_true()
474 {
475 return Ok(Some(StmtResult::FactualStmtSuccess(
476 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
477 atomic_fact.clone().into(),
478 "n >= 0 from n $in N".to_string(),
479 Vec::new(),
480 ),
481 )));
482 }
483 Ok(None)
484 }
485
486 fn try_verify_order_one_le_from_membership_in_n_pos(
488 &mut self,
489 atomic_fact: &AtomicFact,
490 verify_state: &VerifyState,
491 ) -> Result<Option<StmtResult>, RuntimeError> {
492 let (n, line_file) = match atomic_fact {
493 AtomicFact::GreaterEqualFact(f) => {
494 let Some(one) = self.resolve_obj_to_number(&f.right) else {
495 return Ok(None);
496 };
497 if !matches!(
498 compare_number_strings(&one.normalized_value, "1"),
499 NumberCompareResult::Equal
500 ) {
501 return Ok(None);
502 }
503 (f.left.clone(), f.line_file.clone())
504 }
505 AtomicFact::LessEqualFact(f) => {
506 let Some(one) = self.resolve_obj_to_number(&f.left) else {
507 return Ok(None);
508 };
509 if !matches!(
510 compare_number_strings(&one.normalized_value, "1"),
511 NumberCompareResult::Equal
512 ) {
513 return Ok(None);
514 }
515 (f.right.clone(), f.line_file.clone())
516 }
517 _ => return Ok(None),
518 };
519 let in_n_pos: AtomicFact =
520 InFact::new(n, StandardSet::NPos.into(), line_file.clone()).into();
521 if self
522 .verify_non_equational_known_then_builtin_rules_only(&in_n_pos, verify_state)?
523 .is_true()
524 {
525 return Ok(Some(StmtResult::FactualStmtSuccess(
526 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
527 atomic_fact.clone().into(),
528 "n >= 1 from n $in N_pos".to_string(),
529 Vec::new(),
530 ),
531 )));
532 }
533 Ok(None)
534 }
535
536 fn try_verify_order_one_le_from_membership_in_n_and_nonzero(
539 &mut self,
540 atomic_fact: &AtomicFact,
541 verify_state: &VerifyState,
542 ) -> Result<Option<StmtResult>, RuntimeError> {
543 let (n, line_file) = match atomic_fact {
544 AtomicFact::GreaterEqualFact(f) => {
545 let Some(one) = self.resolve_obj_to_number(&f.right) else {
546 return Ok(None);
547 };
548 if !matches!(
549 compare_number_strings(&one.normalized_value, "1"),
550 NumberCompareResult::Equal
551 ) {
552 return Ok(None);
553 }
554 (f.left.clone(), f.line_file.clone())
555 }
556 AtomicFact::LessEqualFact(f) => {
557 let Some(one) = self.resolve_obj_to_number(&f.left) else {
558 return Ok(None);
559 };
560 if !matches!(
561 compare_number_strings(&one.normalized_value, "1"),
562 NumberCompareResult::Equal
563 ) {
564 return Ok(None);
565 }
566 (f.right.clone(), f.line_file.clone())
567 }
568 _ => return Ok(None),
569 };
570 let zero_obj: Obj = Number::new("0".to_string()).into();
571 let in_n: AtomicFact =
572 InFact::new(n.clone(), StandardSet::N.into(), line_file.clone()).into();
573 let nonzero: AtomicFact = NotEqualFact::new(n, zero_obj, line_file.clone()).into();
574 if !self
575 .verify_non_equational_known_then_builtin_rules_only(&in_n, verify_state)?
576 .is_true()
577 {
578 return Ok(None);
579 }
580 if !self
581 .verify_non_equational_atomic_fact_with_known_atomic_facts(&nonzero)?
582 .is_true()
583 {
584 return Ok(None);
585 }
586 Ok(Some(StmtResult::FactualStmtSuccess(
587 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
588 atomic_fact.clone().into(),
589 "1 <= n from n $in N and n != 0".to_string(),
590 Vec::new(),
591 ),
592 )))
593 }
594
595 fn verify_zero_le_abs_builtin_rule(
596 &mut self,
597 atomic_fact: &AtomicFact,
598 ) -> Result<Option<StmtResult>, RuntimeError> {
599 let Some(norm) = normalize_positive_order_atomic_fact(atomic_fact) else {
600 return Ok(None);
601 };
602 let AtomicFact::LessEqualFact(f) = &norm else {
603 return Ok(None);
604 };
605 if f.left.to_string() != "0" {
606 return Ok(None);
607 }
608 if !matches!(&f.right, Obj::Abs(_)) {
609 return Ok(None);
610 }
611 Ok(Some(StmtResult::FactualStmtSuccess(
612 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
613 atomic_fact.clone().into(),
614 "0 <= abs(x) for x in R".to_string(),
615 Vec::new(),
616 ),
617 )))
618 }
619
620 fn try_verify_order_opposite_sign_mul_minus_one(
623 &mut self,
624 atomic_fact: &AtomicFact,
625 verify_state: &VerifyState,
626 ) -> Result<Option<StmtResult>, RuntimeError> {
627 let z: Obj = Number::new("0".to_string()).into();
628 let success = |msg: &'static str| {
629 Ok(Some(StmtResult::FactualStmtSuccess(
630 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
631 atomic_fact.clone().into(),
632 msg.to_string(),
633 Vec::new(),
634 ),
635 )))
636 };
637 match atomic_fact {
638 AtomicFact::GreaterEqualFact(f) if self.obj_is_resolved_zero(&f.right) => {
639 if let Some(x) = self.peel_mul_by_literal_neg_one(&f.left) {
640 let le: AtomicFact =
641 LessEqualFact::new(x.clone(), z.clone(), f.line_file.clone()).into();
642 if self
643 .verify_non_equational_known_then_builtin_rules_only(&le, verify_state)?
644 .is_true()
645 {
646 return success("order: (-1)*x >= 0 from x <= 0");
647 }
648 let lt: AtomicFact = LessFact::new(x, z.clone(), f.line_file.clone()).into();
649 if self
650 .verify_non_equational_known_then_builtin_rules_only(<, verify_state)?
651 .is_true()
652 {
653 return success("order: (-1)*x >= 0 from x < 0");
654 }
655 }
656 Ok(None)
657 }
658 AtomicFact::GreaterFact(f) if self.obj_is_resolved_zero(&f.right) => {
659 if let Some(x) = self.peel_mul_by_literal_neg_one(&f.left) {
660 let lt: AtomicFact = LessFact::new(x, z.clone(), f.line_file.clone()).into();
661 if self
662 .verify_non_equational_known_then_builtin_rules_only(<, verify_state)?
663 .is_true()
664 {
665 return success("order: (-1)*x > 0 from x < 0");
666 }
667 }
668 Ok(None)
669 }
670 AtomicFact::LessEqualFact(f) if self.obj_is_resolved_zero(&f.right) => {
671 if let Some(x) = self.peel_mul_by_literal_neg_one(&f.left) {
672 let ge: AtomicFact =
673 GreaterEqualFact::new(x.clone(), z.clone(), f.line_file.clone()).into();
674 if self
675 .verify_non_equational_known_then_builtin_rules_only(&ge, verify_state)?
676 .is_true()
677 {
678 return success("order: (-1)*x <= 0 from x >= 0");
679 }
680 let gt: AtomicFact = GreaterFact::new(x, z.clone(), f.line_file.clone()).into();
681 if self
682 .verify_non_equational_known_then_builtin_rules_only(>, verify_state)?
683 .is_true()
684 {
685 return success("order: (-1)*x <= 0 from x > 0");
686 }
687 }
688 Ok(None)
689 }
690 AtomicFact::LessFact(f) if self.obj_is_resolved_zero(&f.right) => {
691 if let Some(x) = self.peel_mul_by_literal_neg_one(&f.left) {
692 let gt: AtomicFact = GreaterFact::new(x, z.clone(), f.line_file.clone()).into();
693 if self
694 .verify_non_equational_known_then_builtin_rules_only(>, verify_state)?
695 .is_true()
696 {
697 return success("order: (-1)*x < 0 from x > 0");
698 }
699 }
700 Ok(None)
701 }
702 AtomicFact::LessEqualFact(f) if self.obj_is_resolved_zero(&f.left) => {
703 if let Some(x) = self.peel_mul_by_literal_neg_one(&f.right) {
704 let le: AtomicFact =
705 LessEqualFact::new(x.clone(), z.clone(), f.line_file.clone()).into();
706 if self
707 .verify_non_equational_known_then_builtin_rules_only(&le, verify_state)?
708 .is_true()
709 {
710 return success("order: 0 <= (-1)*x from x <= 0");
711 }
712 let lt: AtomicFact = LessFact::new(x, z.clone(), f.line_file.clone()).into();
713 if self
714 .verify_non_equational_known_then_builtin_rules_only(<, verify_state)?
715 .is_true()
716 {
717 return success("order: 0 <= (-1)*x from x < 0");
718 }
719 }
720 Ok(None)
721 }
722 AtomicFact::LessFact(f) if self.obj_is_resolved_zero(&f.left) => {
723 if let Some(x) = self.peel_mul_by_literal_neg_one(&f.right) {
724 let lt: AtomicFact = LessFact::new(x, z.clone(), f.line_file.clone()).into();
725 if self
726 .verify_non_equational_known_then_builtin_rules_only(<, verify_state)?
727 .is_true()
728 {
729 return success("order: 0 < (-1)*x from x < 0");
730 }
731 }
732 Ok(None)
733 }
734 AtomicFact::GreaterEqualFact(f) if self.obj_is_resolved_zero(&f.left) => {
735 if let Some(x) = self.peel_mul_by_literal_neg_one(&f.right) {
736 let ge: AtomicFact =
737 GreaterEqualFact::new(x.clone(), z.clone(), f.line_file.clone()).into();
738 if self
739 .verify_non_equational_known_then_builtin_rules_only(&ge, verify_state)?
740 .is_true()
741 {
742 return success("order: 0 >= (-1)*x from x >= 0");
743 }
744 let gt: AtomicFact = GreaterFact::new(x, z.clone(), f.line_file.clone()).into();
745 if self
746 .verify_non_equational_known_then_builtin_rules_only(>, verify_state)?
747 .is_true()
748 {
749 return success("order: 0 >= (-1)*x from x > 0");
750 }
751 }
752 Ok(None)
753 }
754 AtomicFact::GreaterFact(f) if self.obj_is_resolved_zero(&f.left) => {
755 if let Some(x) = self.peel_mul_by_literal_neg_one(&f.right) {
756 let gt: AtomicFact = GreaterFact::new(x, z.clone(), f.line_file.clone()).into();
757 if self
758 .verify_non_equational_known_then_builtin_rules_only(>, verify_state)?
759 .is_true()
760 {
761 return success("order: 0 > (-1)*x from x > 0");
762 }
763 }
764 Ok(None)
765 }
766 _ => Ok(None),
767 }
768 }
769
770 fn verify_order_from_known_negated_complement(
772 &mut self,
773 atomic_fact: &AtomicFact,
774 ) -> Result<Option<StmtResult>, RuntimeError> {
775 let negated: Option<AtomicFact> = match atomic_fact {
776 AtomicFact::GreaterFact(f) => Some(
777 NotLessEqualFact::new(f.left.clone(), f.right.clone(), f.line_file.clone()).into(),
778 ),
779 AtomicFact::LessFact(f) => Some(
780 NotGreaterEqualFact::new(f.left.clone(), f.right.clone(), f.line_file.clone())
781 .into(),
782 ),
783 AtomicFact::GreaterEqualFact(f) => {
784 Some(NotLessFact::new(f.left.clone(), f.right.clone(), f.line_file.clone()).into())
785 }
786 AtomicFact::LessEqualFact(f) => Some(
787 NotGreaterFact::new(f.left.clone(), f.right.clone(), f.line_file.clone()).into(),
788 ),
789 _ => None,
790 };
791 let Some(neg) = negated else {
792 return Ok(None);
793 };
794 let sub = self.verify_non_equational_atomic_fact_with_known_atomic_facts(&neg)?;
795 if sub.is_true() {
796 return Ok(Some(
797 FactualStmtSuccess::new_with_verified_by_builtin_rules_label_and_steps(
798 atomic_fact.clone().into(),
799 InferResult::new(),
800 "order_from_known_negated_complement".to_string(),
801 vec![sub],
802 )
803 .into(),
804 ));
805 }
806 Ok(None)
807 }
808
809 fn verify_negated_order_from_known_equivalent_order(
811 &mut self,
812 atomic_fact: &AtomicFact,
813 ) -> Result<Option<StmtResult>, RuntimeError> {
814 let candidates: Vec<AtomicFact> = match atomic_fact {
815 AtomicFact::NotLessFact(f) => {
816 let lf = f.line_file.clone();
817 vec![
818 LessEqualFact::new(f.right.clone(), f.left.clone(), lf.clone()).into(),
819 GreaterEqualFact::new(f.left.clone(), f.right.clone(), lf).into(),
820 ]
821 }
822 AtomicFact::NotGreaterFact(f) => {
823 let lf = f.line_file.clone();
824 vec![
825 LessEqualFact::new(f.left.clone(), f.right.clone(), lf.clone()).into(),
826 GreaterEqualFact::new(f.right.clone(), f.left.clone(), lf).into(),
827 ]
828 }
829 AtomicFact::NotLessEqualFact(f) => {
830 let lf = f.line_file.clone();
831 vec![
832 LessFact::new(f.right.clone(), f.left.clone(), lf.clone()).into(),
833 GreaterFact::new(f.left.clone(), f.right.clone(), lf).into(),
834 ]
835 }
836 AtomicFact::NotGreaterEqualFact(f) => {
837 let lf = f.line_file.clone();
838 vec![
839 LessFact::new(f.left.clone(), f.right.clone(), lf.clone()).into(),
840 GreaterFact::new(f.right.clone(), f.left.clone(), lf).into(),
841 ]
842 }
843 _ => return Ok(None),
844 };
845 for candidate in candidates {
846 let sub = self.verify_non_equational_atomic_fact_with_known_atomic_facts(&candidate)?;
847 if sub.is_true() {
848 return Ok(Some(
849 FactualStmtSuccess::new_with_verified_by_builtin_rules_label_and_steps(
850 atomic_fact.clone().into(),
851 InferResult::new(),
852 "negated_order_from_known_equivalent_order".to_string(),
853 vec![sub],
854 )
855 .into(),
856 ));
857 }
858 }
859 Ok(None)
860 }
861
862 fn verify_zero_order_on_sub_from_two_sided_order_builtin_rule(
864 &mut self,
865 atomic_fact: &AtomicFact,
866 ) -> Result<Option<StmtResult>, RuntimeError> {
867 let Some(norm) = normalize_positive_order_atomic_fact(atomic_fact) else {
868 return Ok(None);
869 };
870 match &norm {
871 AtomicFact::LessEqualFact(f) if f.left.to_string() == "0" => {
872 let Obj::Sub(sub) = &f.right else {
873 return Ok(None);
874 };
875 let derived: AtomicFact = LessEqualFact::new(
876 sub.right.as_ref().clone(),
877 sub.left.as_ref().clone(),
878 f.line_file.clone(),
879 )
880 .into();
881 let mut result =
882 self.verify_non_equational_atomic_fact_with_known_atomic_facts(&derived)?;
883 if !result.is_true() {
884 result = self.verify_order_atomic_fact_numeric_builtin_only(&derived)?;
885 }
886 if result.is_true() {
887 Ok(Some(StmtResult::FactualStmtSuccess(
888 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
889 atomic_fact.clone().into(),
890 "0 <= u - v from v <= u".to_string(),
891 vec![result],
892 ),
893 )))
894 } else {
895 Ok(None)
896 }
897 }
898 AtomicFact::LessFact(f) if f.left.to_string() == "0" => {
899 let Obj::Sub(sub) = &f.right else {
900 return Ok(None);
901 };
902 let derived: AtomicFact = LessFact::new(
903 sub.right.as_ref().clone(),
904 sub.left.as_ref().clone(),
905 f.line_file.clone(),
906 )
907 .into();
908 let mut result =
909 self.verify_non_equational_atomic_fact_with_known_atomic_facts(&derived)?;
910 if !result.is_true() {
911 result = self.verify_order_atomic_fact_numeric_builtin_only(&derived)?;
912 }
913 if result.is_true() {
914 Ok(Some(StmtResult::FactualStmtSuccess(
915 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
916 atomic_fact.clone().into(),
917 "0 < u - v from v < u".to_string(),
918 vec![result],
919 ),
920 )))
921 } else {
922 Ok(None)
923 }
924 }
925 _ => Ok(None),
926 }
927 }
928
929 fn verify_zero_le_add_from_known_atomic_facts_builtin_rule(
930 &mut self,
931 atomic_fact: &AtomicFact,
932 ) -> Result<Option<StmtResult>, RuntimeError> {
933 let Some(normalized_fact) = normalize_positive_order_atomic_fact(atomic_fact) else {
934 return Ok(None);
935 };
936 let AtomicFact::LessEqualFact(less_equal_fact) = normalized_fact else {
937 return Ok(None);
938 };
939 if less_equal_fact.left.to_string() != "0" {
940 return Ok(None);
941 }
942 let Obj::Add(add_obj) = &less_equal_fact.right else {
943 return Ok(None);
944 };
945
946 let zero = &less_equal_fact.left;
947 let line_file = &less_equal_fact.line_file;
948 let left_verify_result =
949 self.verify_zero_order_on_sub_expr(zero, add_obj.left.as_ref(), true, line_file)?;
950 if !left_verify_result.is_true() {
951 return Ok(None);
952 }
953 let right_verify_result =
954 self.verify_zero_order_on_sub_expr(zero, add_obj.right.as_ref(), true, line_file)?;
955 if !right_verify_result.is_true() {
956 return Ok(None);
957 }
958
959 Ok(Some(StmtResult::FactualStmtSuccess(
960 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
961 atomic_fact.clone().into(),
962 "0 <= a + b from known atomic facts 0 <= a and 0 <= b".to_string(),
963 vec![left_verify_result, right_verify_result],
964 ),
965 )))
966 }
967
968 fn verify_zero_lt_add_from_known_atomic_facts_builtin_rule(
969 &mut self,
970 atomic_fact: &AtomicFact,
971 ) -> Result<Option<StmtResult>, RuntimeError> {
972 let Some(normalized_fact) = normalize_positive_order_atomic_fact(atomic_fact) else {
973 return Ok(None);
974 };
975 let AtomicFact::LessFact(less_fact) = normalized_fact else {
976 return Ok(None);
977 };
978 if less_fact.left.to_string() != "0" {
979 return Ok(None);
980 }
981 let Obj::Add(add_obj) = &less_fact.right else {
982 return Ok(None);
983 };
984
985 let zero = &less_fact.left;
986 let line_file = &less_fact.line_file;
987
988 let strict_then_weak = |this: &mut Self| -> Result<Option<StmtResult>, RuntimeError> {
989 let left_result =
990 this.verify_zero_order_on_sub_expr(zero, add_obj.left.as_ref(), false, line_file)?;
991 if !left_result.is_true() {
992 return Ok(None);
993 }
994 let right_result =
995 this.verify_zero_order_on_sub_expr(zero, add_obj.right.as_ref(), true, line_file)?;
996 if !right_result.is_true() {
997 return Ok(None);
998 }
999 Ok(Some(StmtResult::FactualStmtSuccess(
1000 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
1001 atomic_fact.clone().into(),
1002 "0 < a + b from (0 < a and 0 <= b)".to_string(),
1003 vec![left_result, right_result],
1004 ),
1005 )))
1006 };
1007 let weak_then_strict = |this: &mut Self| -> Result<Option<StmtResult>, RuntimeError> {
1008 let left_result =
1009 this.verify_zero_order_on_sub_expr(zero, add_obj.left.as_ref(), true, line_file)?;
1010 if !left_result.is_true() {
1011 return Ok(None);
1012 }
1013 let right_result =
1014 this.verify_zero_order_on_sub_expr(zero, add_obj.right.as_ref(), false, line_file)?;
1015 if !right_result.is_true() {
1016 return Ok(None);
1017 }
1018 Ok(Some(StmtResult::FactualStmtSuccess(
1019 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
1020 atomic_fact.clone().into(),
1021 "0 < a + b from (0 <= a and 0 < b)".to_string(),
1022 vec![left_result, right_result],
1023 ),
1024 )))
1025 };
1026
1027 if let Some(success) = strict_then_weak(self)? {
1028 return Ok(Some(success));
1029 }
1030 weak_then_strict(self)
1031 }
1032
1033 fn verify_zero_le_even_integer_pow_builtin_rule(
1034 &mut self,
1035 atomic_fact: &AtomicFact,
1036 ) -> Result<Option<StmtResult>, RuntimeError> {
1037 let Some(normalized_fact) = normalize_positive_order_atomic_fact(atomic_fact) else {
1038 return Ok(None);
1039 };
1040 let AtomicFact::LessEqualFact(less_equal_fact) = normalized_fact else {
1041 return Ok(None);
1042 };
1043 if less_equal_fact.left.to_string() != "0" {
1044 return Ok(None);
1045 }
1046 let right = &less_equal_fact.right;
1047 let is_equal_factors_mul = match right {
1048 Obj::Mul(mul_obj) => mul_obj.left.to_string() == mul_obj.right.to_string(),
1049 _ => false,
1050 };
1051 let is_even_pow = match right {
1052 Obj::Pow(pow_obj) => match pow_obj.exponent.as_ref() {
1053 Obj::Number(n) => normalized_decimal_string_is_even_integer(&n.normalized_value),
1054 _ => false,
1055 },
1056 _ => false,
1057 };
1058 if !is_equal_factors_mul && !is_even_pow {
1059 return Ok(None);
1060 }
1061 let msg = if is_equal_factors_mul {
1062 "0 <= a * a from even integer exponent (here 2) (forall a R)".to_string()
1063 } else {
1064 "0 <= a^n for even integer n (forall a R)".to_string()
1065 };
1066 Ok(Some(StmtResult::FactualStmtSuccess(
1067 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
1068 atomic_fact.clone().into(),
1069 msg,
1070 Vec::new(),
1071 ),
1072 )))
1073 }
1074
1075 fn verify_zero_lt_even_integer_pow_from_base_nonzero_builtin_rule(
1076 &mut self,
1077 atomic_fact: &AtomicFact,
1078 ) -> Result<Option<StmtResult>, RuntimeError> {
1079 let Some(normalized_fact) = normalize_positive_order_atomic_fact(atomic_fact) else {
1080 return Ok(None);
1081 };
1082 let AtomicFact::LessFact(less_fact) = normalized_fact else {
1083 return Ok(None);
1084 };
1085 if less_fact.left.to_string() != "0" {
1086 return Ok(None);
1087 }
1088 let Obj::Pow(pow_obj) = &less_fact.right else {
1089 return Ok(None);
1090 };
1091 let Obj::Number(exp_num) = pow_obj.exponent.as_ref() else {
1092 return Ok(None);
1093 };
1094 if !normalized_decimal_string_is_even_integer(&exp_num.normalized_value) {
1095 return Ok(None);
1096 }
1097
1098 let line_file = less_fact.line_file.clone();
1099 let base = pow_obj.base.as_ref().clone();
1100 let zero_obj: Obj = Number::new("0".to_string()).into();
1101 let base_neq_zero: AtomicFact = NotEqualFact::new(base, zero_obj, line_file.clone()).into();
1102
1103 let neq_result = self.verify_non_equational_known_then_builtin_rules_only(
1104 &base_neq_zero,
1105 &VerifyState::new(0, true),
1106 )?;
1107 if !neq_result.is_true() {
1108 return Ok(None);
1109 }
1110
1111 Ok(Some(StmtResult::FactualStmtSuccess(
1112 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
1113 atomic_fact.clone().into(),
1114 "0 < a^n for even integer n from a != 0".to_string(),
1115 vec![neq_result],
1116 ),
1117 )))
1118 }
1119
1120 fn verify_zero_lt_pow_from_positive_base_real_exp_builtin_rule(
1122 &mut self,
1123 atomic_fact: &AtomicFact,
1124 ) -> Result<Option<StmtResult>, RuntimeError> {
1125 let Some(normalized_fact) = normalize_positive_order_atomic_fact(atomic_fact) else {
1126 return Ok(None);
1127 };
1128 let AtomicFact::LessFact(less_fact) = normalized_fact else {
1129 return Ok(None);
1130 };
1131 if less_fact.left.to_string() != "0" {
1132 return Ok(None);
1133 }
1134 let Obj::Pow(pow_obj) = &less_fact.right else {
1135 return Ok(None);
1136 };
1137 let zero = &less_fact.left;
1138 let line_file = &less_fact.line_file;
1139 let base = pow_obj.base.as_ref();
1140 let base_result = self.verify_zero_order_on_sub_expr(zero, base, false, line_file)?;
1141 if !base_result.is_true() {
1142 return Ok(None);
1143 }
1144 let in_r: AtomicFact = InFact::new(
1145 (*pow_obj.exponent).clone(),
1146 StandardSet::R.into(),
1147 line_file.clone(),
1148 )
1149 .into();
1150 let in_r_result = self.verify_non_equational_known_then_builtin_rules_only(
1151 &in_r,
1152 &VerifyState::new(0, true),
1153 )?;
1154 if !in_r_result.is_true() {
1155 return Ok(None);
1156 }
1157 Ok(Some(StmtResult::FactualStmtSuccess(
1158 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
1159 atomic_fact.clone().into(),
1160 "0 < a^b from 0 < a and b in R".to_string(),
1161 vec![base_result, in_r_result],
1162 ),
1163 )))
1164 }
1165
1166 fn verify_zero_le_pow_from_positive_base_real_exp_builtin_rule(
1169 &mut self,
1170 atomic_fact: &AtomicFact,
1171 ) -> Result<Option<StmtResult>, RuntimeError> {
1172 let Some(normalized_fact) = normalize_positive_order_atomic_fact(atomic_fact) else {
1173 return Ok(None);
1174 };
1175 let AtomicFact::LessEqualFact(less_equal_fact) = normalized_fact else {
1176 return Ok(None);
1177 };
1178 if less_equal_fact.left.to_string() != "0" {
1179 return Ok(None);
1180 }
1181 let Obj::Pow(pow_obj) = &less_equal_fact.right else {
1182 return Ok(None);
1183 };
1184 let zero = &less_equal_fact.left;
1185 let line_file = &less_equal_fact.line_file;
1186 let base = pow_obj.base.as_ref();
1187 let base_result = self.verify_zero_order_on_sub_expr(zero, base, false, line_file)?;
1188 if !base_result.is_true() {
1189 return Ok(None);
1190 }
1191 let in_r: AtomicFact = InFact::new(
1192 (*pow_obj.exponent).clone(),
1193 StandardSet::R.into(),
1194 line_file.clone(),
1195 )
1196 .into();
1197 let in_r_result = self.verify_non_equational_known_then_builtin_rules_only(
1198 &in_r,
1199 &VerifyState::new(0, true),
1200 )?;
1201 if !in_r_result.is_true() {
1202 return Ok(None);
1203 }
1204 Ok(Some(StmtResult::FactualStmtSuccess(
1205 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
1206 atomic_fact.clone().into(),
1207 "0 <= a^b from 0 < a and b in R".to_string(),
1208 vec![base_result, in_r_result],
1209 ),
1210 )))
1211 }
1212
1213 fn verify_zero_le_pow_integer_exponent_from_nonneg_base_builtin_rule(
1214 &mut self,
1215 atomic_fact: &AtomicFact,
1216 ) -> Result<Option<StmtResult>, RuntimeError> {
1217 let Some(normalized_fact) = normalize_positive_order_atomic_fact(atomic_fact) else {
1218 return Ok(None);
1219 };
1220 let AtomicFact::LessEqualFact(less_equal_fact) = normalized_fact else {
1221 return Ok(None);
1222 };
1223 if less_equal_fact.left.to_string() != "0" {
1224 return Ok(None);
1225 }
1226 let Obj::Pow(pow_obj) = &less_equal_fact.right else {
1227 return Ok(None);
1228 };
1229 let Obj::Number(exp_num) = pow_obj.exponent.as_ref() else {
1230 return Ok(None);
1231 };
1232 if !normalized_decimal_string_is_integer(&exp_num.normalized_value) {
1233 return Ok(None);
1234 }
1235
1236 let zero = &less_equal_fact.left;
1237 let line_file = &less_equal_fact.line_file;
1238 let base = pow_obj.base.as_ref();
1239
1240 let exponent_vs_zero = compare_normalized_number_str_to_zero(&exp_num.normalized_value);
1241 let base_result = match exponent_vs_zero {
1242 NumberCompareResult::Less => {
1243 self.verify_zero_order_on_sub_expr(zero, base, false, line_file)?
1244 }
1245 NumberCompareResult::Equal | NumberCompareResult::Greater => {
1246 self.verify_zero_order_on_sub_expr(zero, base, true, line_file)?
1247 }
1248 };
1249 if !base_result.is_true() {
1250 return Ok(None);
1251 }
1252
1253 let msg = match exponent_vs_zero {
1254 NumberCompareResult::Less => "0 <= a^n from 0 < a and integer n < 0".to_string(),
1255 _ => "0 <= a^n from 0 <= a and integer n".to_string(),
1256 };
1257
1258 Ok(Some(StmtResult::FactualStmtSuccess(
1259 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
1260 atomic_fact.clone().into(),
1261 msg,
1262 vec![base_result],
1263 ),
1264 )))
1265 }
1266
1267 fn verify_zero_le_mul_from_known_atomic_facts_builtin_rule(
1268 &mut self,
1269 atomic_fact: &AtomicFact,
1270 ) -> Result<Option<StmtResult>, RuntimeError> {
1271 let Some(normalized_fact) = normalize_positive_order_atomic_fact(atomic_fact) else {
1272 return Ok(None);
1273 };
1274 let AtomicFact::LessEqualFact(less_equal_fact) = normalized_fact else {
1275 return Ok(None);
1276 };
1277 if less_equal_fact.left.to_string() != "0" {
1278 return Ok(None);
1279 }
1280 let Obj::Mul(mul_obj) = &less_equal_fact.right else {
1281 return Ok(None);
1282 };
1283
1284 let zero = &less_equal_fact.left;
1285 let line_file = &less_equal_fact.line_file;
1286 let left_verify_result =
1287 self.verify_zero_order_on_sub_expr(zero, mul_obj.left.as_ref(), true, line_file)?;
1288 if !left_verify_result.is_true() {
1289 return Ok(None);
1290 }
1291 let right_verify_result =
1292 self.verify_zero_order_on_sub_expr(zero, mul_obj.right.as_ref(), true, line_file)?;
1293 if !right_verify_result.is_true() {
1294 return Ok(None);
1295 }
1296
1297 Ok(Some(StmtResult::FactualStmtSuccess(
1298 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
1299 atomic_fact.clone().into(),
1300 "0 <= a * b from 0 <= a and 0 <= b".to_string(),
1301 vec![left_verify_result, right_verify_result],
1302 ),
1303 )))
1304 }
1305
1306 fn verify_zero_lt_mul_from_known_atomic_facts_builtin_rule(
1307 &mut self,
1308 atomic_fact: &AtomicFact,
1309 ) -> Result<Option<StmtResult>, RuntimeError> {
1310 let Some(normalized_fact) = normalize_positive_order_atomic_fact(atomic_fact) else {
1311 return Ok(None);
1312 };
1313 let AtomicFact::LessFact(less_fact) = normalized_fact else {
1314 return Ok(None);
1315 };
1316 if less_fact.left.to_string() != "0" {
1317 return Ok(None);
1318 }
1319 let Obj::Mul(mul_obj) = &less_fact.right else {
1320 return Ok(None);
1321 };
1322
1323 let zero = &less_fact.left;
1324 let line_file = &less_fact.line_file;
1325 let left_verify_result =
1326 self.verify_zero_order_on_sub_expr(zero, mul_obj.left.as_ref(), false, line_file)?;
1327 if !left_verify_result.is_true() {
1328 return Ok(None);
1329 }
1330 let right_verify_result =
1331 self.verify_zero_order_on_sub_expr(zero, mul_obj.right.as_ref(), false, line_file)?;
1332 if !right_verify_result.is_true() {
1333 return Ok(None);
1334 }
1335
1336 Ok(Some(StmtResult::FactualStmtSuccess(
1337 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
1338 atomic_fact.clone().into(),
1339 "0 < a * b from 0 < a and 0 < b".to_string(),
1340 vec![left_verify_result, right_verify_result],
1341 ),
1342 )))
1343 }
1344
1345 fn verify_zero_le_div_from_known_atomic_facts_builtin_rule(
1346 &mut self,
1347 atomic_fact: &AtomicFact,
1348 ) -> Result<Option<StmtResult>, RuntimeError> {
1349 let Some(normalized_fact) = normalize_positive_order_atomic_fact(atomic_fact) else {
1350 return Ok(None);
1351 };
1352 let AtomicFact::LessEqualFact(less_equal_fact) = normalized_fact else {
1353 return Ok(None);
1354 };
1355 if less_equal_fact.left.to_string() != "0" {
1356 return Ok(None);
1357 }
1358 let Obj::Div(div_obj) = &less_equal_fact.right else {
1359 return Ok(None);
1360 };
1361
1362 let zero = &less_equal_fact.left;
1363 let line_file = &less_equal_fact.line_file;
1364 let numer_result =
1365 self.verify_zero_order_on_sub_expr(zero, div_obj.left.as_ref(), true, line_file)?;
1366 if !numer_result.is_true() {
1367 return Ok(None);
1368 }
1369 let denom_result =
1370 self.verify_zero_order_on_sub_expr(zero, div_obj.right.as_ref(), false, line_file)?;
1371 if !denom_result.is_true() {
1372 return Ok(None);
1373 }
1374
1375 Ok(Some(StmtResult::FactualStmtSuccess(
1376 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
1377 atomic_fact.clone().into(),
1378 "0 <= a / b from 0 <= a and 0 < b".to_string(),
1379 vec![numer_result, denom_result],
1380 ),
1381 )))
1382 }
1383
1384 fn verify_zero_lt_div_from_known_atomic_facts_builtin_rule(
1385 &mut self,
1386 atomic_fact: &AtomicFact,
1387 ) -> Result<Option<StmtResult>, RuntimeError> {
1388 let Some(normalized_fact) = normalize_positive_order_atomic_fact(atomic_fact) else {
1389 return Ok(None);
1390 };
1391 let AtomicFact::LessFact(less_fact) = normalized_fact else {
1392 return Ok(None);
1393 };
1394 if less_fact.left.to_string() != "0" {
1395 return Ok(None);
1396 }
1397 let Obj::Div(div_obj) = &less_fact.right else {
1398 return Ok(None);
1399 };
1400
1401 let zero = &less_fact.left;
1402 let line_file = &less_fact.line_file;
1403 let numer_result =
1404 self.verify_zero_order_on_sub_expr(zero, div_obj.left.as_ref(), false, line_file)?;
1405 if !numer_result.is_true() {
1406 return Ok(None);
1407 }
1408 let denom_result =
1409 self.verify_zero_order_on_sub_expr(zero, div_obj.right.as_ref(), false, line_file)?;
1410 if !denom_result.is_true() {
1411 return Ok(None);
1412 }
1413
1414 Ok(Some(StmtResult::FactualStmtSuccess(
1415 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
1416 atomic_fact.clone().into(),
1417 "0 < a / b from 0 < a and 0 < b".to_string(),
1418 vec![numer_result, denom_result],
1419 ),
1420 )))
1421 }
1422
1423 fn calculate_obj_pair_to_number_strings(
1424 &self,
1425 left_obj: &Obj,
1426 right_obj: &Obj,
1427 ) -> Option<(String, String)> {
1428 let left_number = self.resolve_obj_to_number_resolved(left_obj)?;
1429 let right_number = self.resolve_obj_to_number_resolved(right_obj)?;
1430 Some((left_number.normalized_value, right_number.normalized_value))
1431 }
1432}