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
69fn 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 fn verify_zero_order_on_sub_expr(
188 &mut self,
189 zero: &Obj,
190 sub_expr: &Obj,
191 weak: bool,
192 line_file: &LineFile,
193 ) -> Result<StmtResult, RuntimeError> {
194 let fact: AtomicFact = if weak {
195 LessEqualFact::new(zero.clone(), sub_expr.clone(), line_file.clone()).into()
196 } else {
197 LessFact::new(zero.clone(), sub_expr.clone(), line_file.clone()).into()
198 };
199 let mut result = self.verify_non_equational_atomic_fact_with_known_atomic_facts(&fact)?;
200 if !result.is_true() {
201 result = self.verify_order_atomic_fact_numeric_builtin_only(&fact)?;
202 }
203 Ok(result)
204 }
205
206 fn verify_zero_le_abs_builtin_rule(
207 &mut self,
208 atomic_fact: &AtomicFact,
209 ) -> Result<Option<StmtResult>, RuntimeError> {
210 let Some(norm) = normalize_positive_order_atomic_fact(atomic_fact) else {
211 return Ok(None);
212 };
213 let AtomicFact::LessEqualFact(f) = &norm else {
214 return Ok(None);
215 };
216 if f.left.to_string() != "0" {
217 return Ok(None);
218 }
219 if !matches!(&f.right, Obj::Abs(_)) {
220 return Ok(None);
221 }
222 Ok(Some(StmtResult::FactualStmtSuccess(
223 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
224 atomic_fact.clone().into(),
225 "0 <= abs(x) for x in R".to_string(),
226 Vec::new(),
227 ),
228 )))
229 }
230
231 pub fn verify_order_atomic_fact_numeric_builtin_only(
246 &mut self,
247 atomic_fact: &AtomicFact,
248 ) -> Result<StmtResult, RuntimeError> {
249 if let Some(result) = self.verify_order_from_known_negated_complement(atomic_fact)? {
250 return Ok(result);
251 }
252 if let Some(result) = self.verify_negated_order_from_known_equivalent_order(atomic_fact)? {
253 return Ok(result);
254 }
255 if let Some(result) = self.verify_order_algebra_structural_builtin_rule(atomic_fact)? {
256 return Ok(result);
257 }
258 if let Some(result) = self.verify_zero_le_abs_builtin_rule(atomic_fact)? {
259 return Ok(result);
260 }
261 if let Some(result) =
262 self.verify_zero_order_on_sub_from_two_sided_order_builtin_rule(atomic_fact)?
263 {
264 return Ok(result);
265 }
266 if let Some(result) =
267 self.verify_zero_le_add_from_known_atomic_facts_builtin_rule(atomic_fact)?
268 {
269 return Ok(result);
270 }
271 if let Some(result) =
272 self.verify_zero_lt_add_from_known_atomic_facts_builtin_rule(atomic_fact)?
273 {
274 return Ok(result);
275 }
276 if let Some(result) = self.verify_zero_le_even_integer_pow_builtin_rule(atomic_fact)? {
277 return Ok(result);
278 }
279 if let Some(result) =
280 self.verify_zero_lt_even_integer_pow_from_base_nonzero_builtin_rule(atomic_fact)?
281 {
282 return Ok(result);
283 }
284 if let Some(result) =
285 self.verify_zero_lt_pow_from_positive_base_real_exp_builtin_rule(atomic_fact)?
286 {
287 return Ok(result);
288 }
289 if let Some(result) =
290 self.verify_zero_le_pow_integer_exponent_from_nonneg_base_builtin_rule(atomic_fact)?
291 {
292 return Ok(result);
293 }
294 if let Some(result) =
295 self.verify_zero_le_mul_from_known_atomic_facts_builtin_rule(atomic_fact)?
296 {
297 return Ok(result);
298 }
299 if let Some(result) =
300 self.verify_zero_lt_mul_from_known_atomic_facts_builtin_rule(atomic_fact)?
301 {
302 return Ok(result);
303 }
304 if let Some(result) =
305 self.verify_zero_le_div_from_known_atomic_facts_builtin_rule(atomic_fact)?
306 {
307 return Ok(result);
308 }
309 if let Some(result) =
310 self.verify_zero_lt_div_from_known_atomic_facts_builtin_rule(atomic_fact)?
311 {
312 return Ok(result);
313 }
314
315 if let AtomicFact::LessEqualFact(less_equal_fact) = atomic_fact {
316 if less_equal_fact.left.to_string() == less_equal_fact.right.to_string() {
317 return Ok(StmtResult::FactualStmtSuccess(
318 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
319 less_equal_fact.clone().into(),
320 "less_equal_fact_equal".to_string(),
321 Vec::new(),
322 ),
323 ));
324 }
325 let strict_fact: Fact = LessFact::new(
326 less_equal_fact.left.clone(),
327 less_equal_fact.right.clone(),
328 less_equal_fact.line_file.clone(),
329 )
330 .into();
331 let strict_key = strict_fact.to_string();
332 let (cache_ok, cache_line_file) = self.cache_known_facts_contains(&strict_key);
333 if cache_ok {
334 return Ok(StmtResult::FactualStmtSuccess(
335 FactualStmtSuccess::new_with_verified_by_known_fact_source_recording_facts(
336 less_equal_fact.clone().into(),
337 strict_key,
338 Some(strict_fact),
339 Some(cache_line_file),
340 Vec::new(),
341 ),
342 ));
343 }
344 }
345 if let AtomicFact::GreaterEqualFact(greater_equal_fact) = atomic_fact {
346 if greater_equal_fact.left.to_string() == greater_equal_fact.right.to_string() {
347 return Ok(StmtResult::FactualStmtSuccess(
348 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
349 greater_equal_fact.clone().into(),
350 "greater_equal_fact_equal".to_string(),
351 Vec::new(),
352 ),
353 ));
354 }
355 }
356 if let Some(true) = self.verify_number_comparison_builtin_rule(atomic_fact) {
357 Ok(StmtResult::FactualStmtSuccess(
358 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
359 atomic_fact.clone().into(),
360 "number comparison".to_string(),
361 Vec::new(),
362 ),
363 ))
364 } else {
365 Ok(StmtResult::StmtUnknown(StmtUnknown::new()))
366 }
367 }
368
369 fn verify_order_from_known_negated_complement(
371 &mut self,
372 atomic_fact: &AtomicFact,
373 ) -> Result<Option<StmtResult>, RuntimeError> {
374 let negated: Option<AtomicFact> = match atomic_fact {
375 AtomicFact::GreaterFact(f) => {
376 Some(NotLessEqualFact::new(f.left.clone(), f.right.clone(), f.line_file.clone()).into())
377 }
378 AtomicFact::LessFact(f) => Some(
379 NotGreaterEqualFact::new(f.left.clone(), f.right.clone(), f.line_file.clone()).into(),
380 ),
381 AtomicFact::GreaterEqualFact(f) => {
382 Some(NotLessFact::new(f.left.clone(), f.right.clone(), f.line_file.clone()).into())
383 }
384 AtomicFact::LessEqualFact(f) => {
385 Some(NotGreaterFact::new(f.left.clone(), f.right.clone(), f.line_file.clone()).into())
386 }
387 _ => None,
388 };
389 let Some(neg) = negated else {
390 return Ok(None);
391 };
392 let sub = self.verify_non_equational_atomic_fact_with_known_atomic_facts(&neg)?;
393 if sub.is_true() {
394 return Ok(Some(
395 FactualStmtSuccess::new_with_verified_by_builtin_rules(
396 atomic_fact.clone().into(),
397 InferResult::new(),
398 "order_from_known_negated_complement".to_string(),
399 vec![sub],
400 )
401 .into(),
402 ));
403 }
404 Ok(None)
405 }
406
407 fn verify_negated_order_from_known_equivalent_order(
409 &mut self,
410 atomic_fact: &AtomicFact,
411 ) -> Result<Option<StmtResult>, RuntimeError> {
412 let candidates: Vec<AtomicFact> = match atomic_fact {
413 AtomicFact::NotLessFact(f) => {
414 let lf = f.line_file.clone();
415 vec![
416 LessEqualFact::new(f.right.clone(), f.left.clone(), lf.clone()).into(),
417 GreaterEqualFact::new(f.left.clone(), f.right.clone(), lf).into(),
418 ]
419 }
420 AtomicFact::NotGreaterFact(f) => {
421 let lf = f.line_file.clone();
422 vec![
423 LessEqualFact::new(f.left.clone(), f.right.clone(), lf.clone()).into(),
424 GreaterEqualFact::new(f.right.clone(), f.left.clone(), lf).into(),
425 ]
426 }
427 AtomicFact::NotLessEqualFact(f) => {
428 let lf = f.line_file.clone();
429 vec![
430 LessFact::new(f.right.clone(), f.left.clone(), lf.clone()).into(),
431 GreaterFact::new(f.left.clone(), f.right.clone(), lf).into(),
432 ]
433 }
434 AtomicFact::NotGreaterEqualFact(f) => {
435 let lf = f.line_file.clone();
436 vec![
437 LessFact::new(f.left.clone(), f.right.clone(), lf.clone()).into(),
438 GreaterFact::new(f.right.clone(), f.left.clone(), lf).into(),
439 ]
440 }
441 _ => return Ok(None),
442 };
443 for candidate in candidates {
444 let sub = self.verify_non_equational_atomic_fact_with_known_atomic_facts(&candidate)?;
445 if sub.is_true() {
446 return Ok(Some(
447 FactualStmtSuccess::new_with_verified_by_builtin_rules(
448 atomic_fact.clone().into(),
449 InferResult::new(),
450 "negated_order_from_known_equivalent_order".to_string(),
451 vec![sub],
452 )
453 .into(),
454 ));
455 }
456 }
457 Ok(None)
458 }
459
460 fn verify_zero_order_on_sub_from_two_sided_order_builtin_rule(
462 &mut self,
463 atomic_fact: &AtomicFact,
464 ) -> Result<Option<StmtResult>, RuntimeError> {
465 let Some(norm) = normalize_positive_order_atomic_fact(atomic_fact) else {
466 return Ok(None);
467 };
468 match &norm {
469 AtomicFact::LessEqualFact(f) if f.left.to_string() == "0" => {
470 let Obj::Sub(sub) = &f.right else {
471 return Ok(None);
472 };
473 let derived: AtomicFact = LessEqualFact::new(
474 sub.right.as_ref().clone(),
475 sub.left.as_ref().clone(),
476 f.line_file.clone(),
477 )
478 .into();
479 let mut result =
480 self.verify_non_equational_atomic_fact_with_known_atomic_facts(&derived)?;
481 if !result.is_true() {
482 result = self.verify_order_atomic_fact_numeric_builtin_only(&derived)?;
483 }
484 if result.is_true() {
485 Ok(Some(StmtResult::FactualStmtSuccess(
486 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
487 atomic_fact.clone().into(),
488 "0 <= u - v from v <= u".to_string(),
489 vec![result],
490 ),
491 )))
492 } else {
493 Ok(None)
494 }
495 }
496 AtomicFact::LessFact(f) if f.left.to_string() == "0" => {
497 let Obj::Sub(sub) = &f.right else {
498 return Ok(None);
499 };
500 let derived: AtomicFact = LessFact::new(
501 sub.right.as_ref().clone(),
502 sub.left.as_ref().clone(),
503 f.line_file.clone(),
504 )
505 .into();
506 let mut result =
507 self.verify_non_equational_atomic_fact_with_known_atomic_facts(&derived)?;
508 if !result.is_true() {
509 result = self.verify_order_atomic_fact_numeric_builtin_only(&derived)?;
510 }
511 if result.is_true() {
512 Ok(Some(StmtResult::FactualStmtSuccess(
513 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
514 atomic_fact.clone().into(),
515 "0 < u - v from v < u".to_string(),
516 vec![result],
517 ),
518 )))
519 } else {
520 Ok(None)
521 }
522 }
523 _ => Ok(None),
524 }
525 }
526
527 fn verify_zero_le_add_from_known_atomic_facts_builtin_rule(
528 &mut self,
529 atomic_fact: &AtomicFact,
530 ) -> Result<Option<StmtResult>, RuntimeError> {
531 let Some(normalized_fact) = normalize_positive_order_atomic_fact(atomic_fact) else {
532 return Ok(None);
533 };
534 let AtomicFact::LessEqualFact(less_equal_fact) = normalized_fact else {
535 return Ok(None);
536 };
537 if less_equal_fact.left.to_string() != "0" {
538 return Ok(None);
539 }
540 let Obj::Add(add_obj) = &less_equal_fact.right else {
541 return Ok(None);
542 };
543
544 let zero = &less_equal_fact.left;
545 let line_file = &less_equal_fact.line_file;
546 let left_verify_result =
547 self.verify_zero_order_on_sub_expr(zero, add_obj.left.as_ref(), true, line_file)?;
548 if !left_verify_result.is_true() {
549 return Ok(None);
550 }
551 let right_verify_result =
552 self.verify_zero_order_on_sub_expr(zero, add_obj.right.as_ref(), true, line_file)?;
553 if !right_verify_result.is_true() {
554 return Ok(None);
555 }
556
557 Ok(Some(StmtResult::FactualStmtSuccess(
558 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
559 atomic_fact.clone().into(),
560 "0 <= a + b from known atomic facts 0 <= a and 0 <= b".to_string(),
561 vec![left_verify_result, right_verify_result],
562 ),
563 )))
564 }
565
566 fn verify_zero_lt_add_from_known_atomic_facts_builtin_rule(
567 &mut self,
568 atomic_fact: &AtomicFact,
569 ) -> Result<Option<StmtResult>, RuntimeError> {
570 let Some(normalized_fact) = normalize_positive_order_atomic_fact(atomic_fact) else {
571 return Ok(None);
572 };
573 let AtomicFact::LessFact(less_fact) = normalized_fact else {
574 return Ok(None);
575 };
576 if less_fact.left.to_string() != "0" {
577 return Ok(None);
578 }
579 let Obj::Add(add_obj) = &less_fact.right else {
580 return Ok(None);
581 };
582
583 let zero = &less_fact.left;
584 let line_file = &less_fact.line_file;
585
586 let strict_then_weak = |this: &mut Self| -> Result<Option<StmtResult>, RuntimeError> {
587 let left_result =
588 this.verify_zero_order_on_sub_expr(zero, add_obj.left.as_ref(), false, line_file)?;
589 if !left_result.is_true() {
590 return Ok(None);
591 }
592 let right_result =
593 this.verify_zero_order_on_sub_expr(zero, add_obj.right.as_ref(), true, line_file)?;
594 if !right_result.is_true() {
595 return Ok(None);
596 }
597 Ok(Some(StmtResult::FactualStmtSuccess(
598 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
599 atomic_fact.clone().into(),
600 "0 < a + b from (0 < a and 0 <= b)".to_string(),
601 vec![left_result, right_result],
602 ),
603 )))
604 };
605 let weak_then_strict = |this: &mut Self| -> Result<Option<StmtResult>, RuntimeError> {
606 let left_result =
607 this.verify_zero_order_on_sub_expr(zero, add_obj.left.as_ref(), true, line_file)?;
608 if !left_result.is_true() {
609 return Ok(None);
610 }
611 let right_result =
612 this.verify_zero_order_on_sub_expr(zero, add_obj.right.as_ref(), false, line_file)?;
613 if !right_result.is_true() {
614 return Ok(None);
615 }
616 Ok(Some(StmtResult::FactualStmtSuccess(
617 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
618 atomic_fact.clone().into(),
619 "0 < a + b from (0 <= a and 0 < b)".to_string(),
620 vec![left_result, right_result],
621 ),
622 )))
623 };
624
625 if let Some(success) = strict_then_weak(self)? {
626 return Ok(Some(success));
627 }
628 weak_then_strict(self)
629 }
630
631 fn verify_zero_le_even_integer_pow_builtin_rule(
632 &mut self,
633 atomic_fact: &AtomicFact,
634 ) -> Result<Option<StmtResult>, RuntimeError> {
635 let Some(normalized_fact) = normalize_positive_order_atomic_fact(atomic_fact) else {
636 return Ok(None);
637 };
638 let AtomicFact::LessEqualFact(less_equal_fact) = normalized_fact else {
639 return Ok(None);
640 };
641 if less_equal_fact.left.to_string() != "0" {
642 return Ok(None);
643 }
644 let right = &less_equal_fact.right;
645 let is_equal_factors_mul = match right {
646 Obj::Mul(mul_obj) => mul_obj.left.to_string() == mul_obj.right.to_string(),
647 _ => false,
648 };
649 let is_even_pow = match right {
650 Obj::Pow(pow_obj) => match pow_obj.exponent.as_ref() {
651 Obj::Number(n) => normalized_decimal_string_is_even_integer(&n.normalized_value),
652 _ => false,
653 },
654 _ => false,
655 };
656 if !is_equal_factors_mul && !is_even_pow {
657 return Ok(None);
658 }
659 let msg = if is_equal_factors_mul {
660 "0 <= a * a from even integer exponent (here 2) (forall a R)".to_string()
661 } else {
662 "0 <= a^n for even integer n (forall a R)".to_string()
663 };
664 Ok(Some(StmtResult::FactualStmtSuccess(
665 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
666 atomic_fact.clone().into(),
667 msg,
668 Vec::new(),
669 ),
670 )))
671 }
672
673 fn verify_zero_lt_even_integer_pow_from_base_nonzero_builtin_rule(
674 &mut self,
675 atomic_fact: &AtomicFact,
676 ) -> Result<Option<StmtResult>, RuntimeError> {
677 let Some(normalized_fact) = normalize_positive_order_atomic_fact(atomic_fact) else {
678 return Ok(None);
679 };
680 let AtomicFact::LessFact(less_fact) = normalized_fact else {
681 return Ok(None);
682 };
683 if less_fact.left.to_string() != "0" {
684 return Ok(None);
685 }
686 let Obj::Pow(pow_obj) = &less_fact.right else {
687 return Ok(None);
688 };
689 let Obj::Number(exp_num) = pow_obj.exponent.as_ref() else {
690 return Ok(None);
691 };
692 if !normalized_decimal_string_is_even_integer(&exp_num.normalized_value) {
693 return Ok(None);
694 }
695
696 let line_file = less_fact.line_file.clone();
697 let base = pow_obj.base.as_ref().clone();
698 let zero_obj: Obj = Number::new("0".to_string()).into();
699 let base_neq_zero: AtomicFact = NotEqualFact::new(base, zero_obj, line_file.clone()).into();
700
701 let mut neq_result =
702 self.verify_non_equational_atomic_fact_with_known_atomic_facts(&base_neq_zero)?;
703 if !neq_result.is_true() {
704 neq_result = self.verify_non_equational_atomic_fact(
705 &base_neq_zero,
706 &VerifyState::new(0, false),
707 true,
708 )?;
709 }
710 if !neq_result.is_true() {
711 return Ok(None);
712 }
713
714 Ok(Some(StmtResult::FactualStmtSuccess(
715 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
716 atomic_fact.clone().into(),
717 "0 < a^n for even integer n from a != 0".to_string(),
718 vec![neq_result],
719 ),
720 )))
721 }
722
723 fn verify_zero_lt_pow_from_positive_base_real_exp_builtin_rule(
725 &mut self,
726 atomic_fact: &AtomicFact,
727 ) -> Result<Option<StmtResult>, RuntimeError> {
728 let Some(normalized_fact) = normalize_positive_order_atomic_fact(atomic_fact) else {
729 return Ok(None);
730 };
731 let AtomicFact::LessFact(less_fact) = normalized_fact else {
732 return Ok(None);
733 };
734 if less_fact.left.to_string() != "0" {
735 return Ok(None);
736 }
737 let Obj::Pow(pow_obj) = &less_fact.right else {
738 return Ok(None);
739 };
740 let zero = &less_fact.left;
741 let line_file = &less_fact.line_file;
742 let base = pow_obj.base.as_ref();
743 let base_result = self.verify_zero_order_on_sub_expr(zero, base, false, line_file)?;
744 if !base_result.is_true() {
745 return Ok(None);
746 }
747 let in_r: AtomicFact = InFact::new(
748 (*pow_obj.exponent).clone(),
749 StandardSet::R.into(),
750 line_file.clone(),
751 )
752 .into();
753 let mut in_r_result =
754 self.verify_non_equational_atomic_fact_with_known_atomic_facts(&in_r)?;
755 if !in_r_result.is_true() {
756 in_r_result = self.verify_non_equational_atomic_fact(
757 &in_r,
758 &VerifyState::new(0, false),
759 true,
760 )?;
761 }
762 if !in_r_result.is_true() {
763 return Ok(None);
764 }
765 Ok(Some(StmtResult::FactualStmtSuccess(
766 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
767 atomic_fact.clone().into(),
768 "0 < a^b from 0 < a and b in R".to_string(),
769 vec![base_result, in_r_result],
770 ),
771 )))
772 }
773
774 fn verify_zero_le_pow_integer_exponent_from_nonneg_base_builtin_rule(
775 &mut self,
776 atomic_fact: &AtomicFact,
777 ) -> Result<Option<StmtResult>, RuntimeError> {
778 let Some(normalized_fact) = normalize_positive_order_atomic_fact(atomic_fact) else {
779 return Ok(None);
780 };
781 let AtomicFact::LessEqualFact(less_equal_fact) = normalized_fact else {
782 return Ok(None);
783 };
784 if less_equal_fact.left.to_string() != "0" {
785 return Ok(None);
786 }
787 let Obj::Pow(pow_obj) = &less_equal_fact.right else {
788 return Ok(None);
789 };
790 let Obj::Number(exp_num) = pow_obj.exponent.as_ref() else {
791 return Ok(None);
792 };
793 if !normalized_decimal_string_is_integer(&exp_num.normalized_value) {
794 return Ok(None);
795 }
796
797 let zero = &less_equal_fact.left;
798 let line_file = &less_equal_fact.line_file;
799 let base = pow_obj.base.as_ref();
800
801 let exponent_vs_zero = compare_normalized_number_str_to_zero(&exp_num.normalized_value);
802 let base_result = match exponent_vs_zero {
803 NumberCompareResult::Less => {
804 self.verify_zero_order_on_sub_expr(zero, base, false, line_file)?
805 }
806 NumberCompareResult::Equal | NumberCompareResult::Greater => {
807 self.verify_zero_order_on_sub_expr(zero, base, true, line_file)?
808 }
809 };
810 if !base_result.is_true() {
811 return Ok(None);
812 }
813
814 let msg = match exponent_vs_zero {
815 NumberCompareResult::Less => "0 <= a^n from 0 < a and integer n < 0".to_string(),
816 _ => "0 <= a^n from 0 <= a and integer n".to_string(),
817 };
818
819 Ok(Some(StmtResult::FactualStmtSuccess(
820 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
821 atomic_fact.clone().into(),
822 msg,
823 vec![base_result],
824 ),
825 )))
826 }
827
828 fn verify_zero_le_mul_from_known_atomic_facts_builtin_rule(
829 &mut self,
830 atomic_fact: &AtomicFact,
831 ) -> Result<Option<StmtResult>, RuntimeError> {
832 let Some(normalized_fact) = normalize_positive_order_atomic_fact(atomic_fact) else {
833 return Ok(None);
834 };
835 let AtomicFact::LessEqualFact(less_equal_fact) = normalized_fact else {
836 return Ok(None);
837 };
838 if less_equal_fact.left.to_string() != "0" {
839 return Ok(None);
840 }
841 let Obj::Mul(mul_obj) = &less_equal_fact.right else {
842 return Ok(None);
843 };
844
845 let zero = &less_equal_fact.left;
846 let line_file = &less_equal_fact.line_file;
847 let left_verify_result =
848 self.verify_zero_order_on_sub_expr(zero, mul_obj.left.as_ref(), true, line_file)?;
849 if !left_verify_result.is_true() {
850 return Ok(None);
851 }
852 let right_verify_result =
853 self.verify_zero_order_on_sub_expr(zero, mul_obj.right.as_ref(), true, line_file)?;
854 if !right_verify_result.is_true() {
855 return Ok(None);
856 }
857
858 Ok(Some(StmtResult::FactualStmtSuccess(
859 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
860 atomic_fact.clone().into(),
861 "0 <= a * b from 0 <= a and 0 <= b".to_string(),
862 vec![left_verify_result, right_verify_result],
863 ),
864 )))
865 }
866
867 fn verify_zero_lt_mul_from_known_atomic_facts_builtin_rule(
868 &mut self,
869 atomic_fact: &AtomicFact,
870 ) -> Result<Option<StmtResult>, RuntimeError> {
871 let Some(normalized_fact) = normalize_positive_order_atomic_fact(atomic_fact) else {
872 return Ok(None);
873 };
874 let AtomicFact::LessFact(less_fact) = normalized_fact else {
875 return Ok(None);
876 };
877 if less_fact.left.to_string() != "0" {
878 return Ok(None);
879 }
880 let Obj::Mul(mul_obj) = &less_fact.right else {
881 return Ok(None);
882 };
883
884 let zero = &less_fact.left;
885 let line_file = &less_fact.line_file;
886 let left_verify_result =
887 self.verify_zero_order_on_sub_expr(zero, mul_obj.left.as_ref(), false, line_file)?;
888 if !left_verify_result.is_true() {
889 return Ok(None);
890 }
891 let right_verify_result =
892 self.verify_zero_order_on_sub_expr(zero, mul_obj.right.as_ref(), false, line_file)?;
893 if !right_verify_result.is_true() {
894 return Ok(None);
895 }
896
897 Ok(Some(StmtResult::FactualStmtSuccess(
898 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
899 atomic_fact.clone().into(),
900 "0 < a * b from 0 < a and 0 < b".to_string(),
901 vec![left_verify_result, right_verify_result],
902 ),
903 )))
904 }
905
906 fn verify_zero_le_div_from_known_atomic_facts_builtin_rule(
907 &mut self,
908 atomic_fact: &AtomicFact,
909 ) -> Result<Option<StmtResult>, RuntimeError> {
910 let Some(normalized_fact) = normalize_positive_order_atomic_fact(atomic_fact) else {
911 return Ok(None);
912 };
913 let AtomicFact::LessEqualFact(less_equal_fact) = normalized_fact else {
914 return Ok(None);
915 };
916 if less_equal_fact.left.to_string() != "0" {
917 return Ok(None);
918 }
919 let Obj::Div(div_obj) = &less_equal_fact.right else {
920 return Ok(None);
921 };
922
923 let zero = &less_equal_fact.left;
924 let line_file = &less_equal_fact.line_file;
925 let numer_result =
926 self.verify_zero_order_on_sub_expr(zero, div_obj.left.as_ref(), true, line_file)?;
927 if !numer_result.is_true() {
928 return Ok(None);
929 }
930 let denom_result =
931 self.verify_zero_order_on_sub_expr(zero, div_obj.right.as_ref(), false, line_file)?;
932 if !denom_result.is_true() {
933 return Ok(None);
934 }
935
936 Ok(Some(StmtResult::FactualStmtSuccess(
937 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
938 atomic_fact.clone().into(),
939 "0 <= a / b from 0 <= a and 0 < b".to_string(),
940 vec![numer_result, denom_result],
941 ),
942 )))
943 }
944
945 fn verify_zero_lt_div_from_known_atomic_facts_builtin_rule(
946 &mut self,
947 atomic_fact: &AtomicFact,
948 ) -> Result<Option<StmtResult>, RuntimeError> {
949 let Some(normalized_fact) = normalize_positive_order_atomic_fact(atomic_fact) else {
950 return Ok(None);
951 };
952 let AtomicFact::LessFact(less_fact) = normalized_fact else {
953 return Ok(None);
954 };
955 if less_fact.left.to_string() != "0" {
956 return Ok(None);
957 }
958 let Obj::Div(div_obj) = &less_fact.right else {
959 return Ok(None);
960 };
961
962 let zero = &less_fact.left;
963 let line_file = &less_fact.line_file;
964 let numer_result =
965 self.verify_zero_order_on_sub_expr(zero, div_obj.left.as_ref(), false, line_file)?;
966 if !numer_result.is_true() {
967 return Ok(None);
968 }
969 let denom_result =
970 self.verify_zero_order_on_sub_expr(zero, div_obj.right.as_ref(), false, line_file)?;
971 if !denom_result.is_true() {
972 return Ok(None);
973 }
974
975 Ok(Some(StmtResult::FactualStmtSuccess(
976 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
977 atomic_fact.clone().into(),
978 "0 < a / b from 0 < a and 0 < b".to_string(),
979 vec![numer_result, denom_result],
980 ),
981 )))
982 }
983
984 pub fn verify_number_comparison_builtin_rule(&self, atomic_fact: &AtomicFact) -> Option<bool> {
985 let normalized = normalize_positive_order_atomic_fact(atomic_fact)?;
986 match normalized {
987 AtomicFact::LessFact(less_fact) => {
988 if let Some(calculated_number_string_pair) =
989 self.calculate_obj_pair_to_number_strings(&less_fact.left, &less_fact.right)
990 {
991 return Some(matches!(
992 compare_number_strings(
993 &calculated_number_string_pair.0,
994 &calculated_number_string_pair.1
995 ),
996 NumberCompareResult::Less
997 ));
998 }
999 self.try_verify_numeric_order_via_div_elimination(
1000 &less_fact.left,
1001 &less_fact.right,
1002 false,
1003 )
1004 }
1005 AtomicFact::LessEqualFact(less_equal_fact) => {
1006 if let Some(calculated_number_string_pair) = self
1007 .calculate_obj_pair_to_number_strings(
1008 &less_equal_fact.left,
1009 &less_equal_fact.right,
1010 )
1011 {
1012 let compare_result = compare_number_strings(
1013 &calculated_number_string_pair.0,
1014 &calculated_number_string_pair.1,
1015 );
1016 return Some(matches!(
1017 compare_result,
1018 NumberCompareResult::Less | NumberCompareResult::Equal
1019 ));
1020 }
1021 self.try_verify_numeric_order_via_div_elimination(
1022 &less_equal_fact.left,
1023 &less_equal_fact.right,
1024 true,
1025 )
1026 }
1027 _ => None,
1028 }
1029 }
1030
1031 fn calculate_obj_pair_to_number_strings(
1032 &self,
1033 left_obj: &Obj,
1034 right_obj: &Obj,
1035 ) -> Option<(String, String)> {
1036 let left_number = self.resolve_obj_to_number_resolved(left_obj)?;
1037 let right_number = self.resolve_obj_to_number_resolved(right_obj)?;
1038 Some((left_number.normalized_value, right_number.normalized_value))
1039 }
1040}