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