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 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 verify_zero_le_abs_builtin_rule(
271 &mut self,
272 atomic_fact: &AtomicFact,
273 ) -> Result<Option<StmtResult>, RuntimeError> {
274 let Some(norm) = normalize_positive_order_atomic_fact(atomic_fact) else {
275 return Ok(None);
276 };
277 let AtomicFact::LessEqualFact(f) = &norm else {
278 return Ok(None);
279 };
280 if f.left.to_string() != "0" {
281 return Ok(None);
282 }
283 if !matches!(&f.right, Obj::Abs(_)) {
284 return Ok(None);
285 }
286 Ok(Some(StmtResult::FactualStmtSuccess(
287 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
288 atomic_fact.clone().into(),
289 "0 <= abs(x) for x in R".to_string(),
290 Vec::new(),
291 ),
292 )))
293 }
294
295 fn try_verify_order_opposite_sign_mul_minus_one(
298 &mut self,
299 atomic_fact: &AtomicFact,
300 verify_state: &VerifyState,
301 ) -> Result<Option<StmtResult>, RuntimeError> {
302 let z: Obj = Number::new("0".to_string()).into();
303 let success = |msg: &'static str| {
304 Ok(Some(StmtResult::FactualStmtSuccess(
305 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
306 atomic_fact.clone().into(),
307 msg.to_string(),
308 Vec::new(),
309 ),
310 )))
311 };
312 match atomic_fact {
313 AtomicFact::GreaterEqualFact(f) if self.obj_is_resolved_zero(&f.right) => {
314 if let Some(x) = self.peel_mul_by_literal_neg_one(&f.left) {
315 let le: AtomicFact =
316 LessEqualFact::new(x.clone(), z.clone(), f.line_file.clone()).into();
317 if self
318 .verify_non_equational_known_then_builtin_rules_only(&le, verify_state)?
319 .is_true()
320 {
321 return success("order: (-1)*x >= 0 from x <= 0");
322 }
323 let lt: AtomicFact = LessFact::new(x, z.clone(), f.line_file.clone()).into();
324 if self
325 .verify_non_equational_known_then_builtin_rules_only(<, verify_state)?
326 .is_true()
327 {
328 return success("order: (-1)*x >= 0 from x < 0");
329 }
330 }
331 Ok(None)
332 }
333 AtomicFact::GreaterFact(f) if self.obj_is_resolved_zero(&f.right) => {
334 if let Some(x) = self.peel_mul_by_literal_neg_one(&f.left) {
335 let lt: AtomicFact = LessFact::new(x, z.clone(), f.line_file.clone()).into();
336 if self
337 .verify_non_equational_known_then_builtin_rules_only(<, verify_state)?
338 .is_true()
339 {
340 return success("order: (-1)*x > 0 from x < 0");
341 }
342 }
343 Ok(None)
344 }
345 AtomicFact::LessEqualFact(f) if self.obj_is_resolved_zero(&f.right) => {
346 if let Some(x) = self.peel_mul_by_literal_neg_one(&f.left) {
347 let ge: AtomicFact =
348 GreaterEqualFact::new(x.clone(), z.clone(), f.line_file.clone()).into();
349 if self
350 .verify_non_equational_known_then_builtin_rules_only(&ge, verify_state)?
351 .is_true()
352 {
353 return success("order: (-1)*x <= 0 from x >= 0");
354 }
355 let gt: AtomicFact = GreaterFact::new(x, z.clone(), f.line_file.clone()).into();
356 if self
357 .verify_non_equational_known_then_builtin_rules_only(>, verify_state)?
358 .is_true()
359 {
360 return success("order: (-1)*x <= 0 from x > 0");
361 }
362 }
363 Ok(None)
364 }
365 AtomicFact::LessFact(f) if self.obj_is_resolved_zero(&f.right) => {
366 if let Some(x) = self.peel_mul_by_literal_neg_one(&f.left) {
367 let gt: AtomicFact = GreaterFact::new(x, z.clone(), f.line_file.clone()).into();
368 if self
369 .verify_non_equational_known_then_builtin_rules_only(>, verify_state)?
370 .is_true()
371 {
372 return success("order: (-1)*x < 0 from x > 0");
373 }
374 }
375 Ok(None)
376 }
377 AtomicFact::LessEqualFact(f) if self.obj_is_resolved_zero(&f.left) => {
378 if let Some(x) = self.peel_mul_by_literal_neg_one(&f.right) {
379 let le: AtomicFact =
380 LessEqualFact::new(x.clone(), z.clone(), f.line_file.clone()).into();
381 if self
382 .verify_non_equational_known_then_builtin_rules_only(&le, verify_state)?
383 .is_true()
384 {
385 return success("order: 0 <= (-1)*x from x <= 0");
386 }
387 let lt: AtomicFact = LessFact::new(x, z.clone(), f.line_file.clone()).into();
388 if self
389 .verify_non_equational_known_then_builtin_rules_only(<, verify_state)?
390 .is_true()
391 {
392 return success("order: 0 <= (-1)*x from x < 0");
393 }
394 }
395 Ok(None)
396 }
397 AtomicFact::LessFact(f) if self.obj_is_resolved_zero(&f.left) => {
398 if let Some(x) = self.peel_mul_by_literal_neg_one(&f.right) {
399 let lt: AtomicFact = LessFact::new(x, z.clone(), f.line_file.clone()).into();
400 if self
401 .verify_non_equational_known_then_builtin_rules_only(<, verify_state)?
402 .is_true()
403 {
404 return success("order: 0 < (-1)*x from x < 0");
405 }
406 }
407 Ok(None)
408 }
409 AtomicFact::GreaterEqualFact(f) if self.obj_is_resolved_zero(&f.left) => {
410 if let Some(x) = self.peel_mul_by_literal_neg_one(&f.right) {
411 let ge: AtomicFact =
412 GreaterEqualFact::new(x.clone(), z.clone(), f.line_file.clone()).into();
413 if self
414 .verify_non_equational_known_then_builtin_rules_only(&ge, verify_state)?
415 .is_true()
416 {
417 return success("order: 0 >= (-1)*x from x >= 0");
418 }
419 let gt: AtomicFact = GreaterFact::new(x, z.clone(), f.line_file.clone()).into();
420 if self
421 .verify_non_equational_known_then_builtin_rules_only(>, verify_state)?
422 .is_true()
423 {
424 return success("order: 0 >= (-1)*x from x > 0");
425 }
426 }
427 Ok(None)
428 }
429 AtomicFact::GreaterFact(f) if self.obj_is_resolved_zero(&f.left) => {
430 if let Some(x) = self.peel_mul_by_literal_neg_one(&f.right) {
431 let gt: AtomicFact = GreaterFact::new(x, z.clone(), f.line_file.clone()).into();
432 if self
433 .verify_non_equational_known_then_builtin_rules_only(>, verify_state)?
434 .is_true()
435 {
436 return success("order: 0 > (-1)*x from x > 0");
437 }
438 }
439 Ok(None)
440 }
441 _ => Ok(None),
442 }
443 }
444
445 pub fn verify_order_atomic_fact_numeric_builtin_only(
460 &mut self,
461 atomic_fact: &AtomicFact,
462 ) -> Result<StmtResult, RuntimeError> {
463 let vs = VerifyState::new(0, true);
464 if let Some(result) =
465 self.try_verify_order_nonnegative_from_membership_in_n(atomic_fact, &vs)?
466 {
467 return Ok(result);
468 }
469 if let Some(result) = self.try_verify_order_opposite_sign_mul_minus_one(atomic_fact, &vs)? {
470 return Ok(result);
471 }
472 if let Some(result) = self.try_verify_order_two_sums_same_index_and_bounds(atomic_fact)? {
473 return Ok(result);
474 }
475 if let Some(result) = self.try_verify_zero_order_sum_by_pointwise_summand_order(atomic_fact)? {
476 return Ok(result);
477 }
478 if let Some(result) = self.verify_order_from_known_negated_complement(atomic_fact)? {
479 return Ok(result);
480 }
481 if let Some(result) = self.verify_negated_order_from_known_equivalent_order(atomic_fact)? {
482 return Ok(result);
483 }
484 if let Some(result) = self.verify_order_algebra_structural_builtin_rule(atomic_fact)? {
485 return Ok(result);
486 }
487 if let Some(result) = self.verify_zero_le_abs_builtin_rule(atomic_fact)? {
488 return Ok(result);
489 }
490 if let Some(result) =
491 self.verify_zero_order_on_sub_from_two_sided_order_builtin_rule(atomic_fact)?
492 {
493 return Ok(result);
494 }
495 if let Some(result) =
496 self.verify_zero_le_add_from_known_atomic_facts_builtin_rule(atomic_fact)?
497 {
498 return Ok(result);
499 }
500 if let Some(result) =
501 self.verify_zero_lt_add_from_known_atomic_facts_builtin_rule(atomic_fact)?
502 {
503 return Ok(result);
504 }
505 if let Some(result) = self.verify_zero_le_even_integer_pow_builtin_rule(atomic_fact)? {
506 return Ok(result);
507 }
508 if let Some(result) =
509 self.verify_zero_lt_even_integer_pow_from_base_nonzero_builtin_rule(atomic_fact)?
510 {
511 return Ok(result);
512 }
513 if let Some(result) =
514 self.verify_zero_lt_pow_from_positive_base_real_exp_builtin_rule(atomic_fact)?
515 {
516 return Ok(result);
517 }
518 if let Some(result) =
519 self.verify_zero_le_pow_from_positive_base_real_exp_builtin_rule(atomic_fact)?
520 {
521 return Ok(result);
522 }
523 if let Some(result) =
524 self.verify_zero_le_pow_integer_exponent_from_nonneg_base_builtin_rule(atomic_fact)?
525 {
526 return Ok(result);
527 }
528 if let Some(result) =
529 self.verify_zero_le_mul_from_known_atomic_facts_builtin_rule(atomic_fact)?
530 {
531 return Ok(result);
532 }
533 if let Some(result) =
534 self.verify_zero_lt_mul_from_known_atomic_facts_builtin_rule(atomic_fact)?
535 {
536 return Ok(result);
537 }
538 if let Some(result) =
539 self.verify_zero_le_div_from_known_atomic_facts_builtin_rule(atomic_fact)?
540 {
541 return Ok(result);
542 }
543 if let Some(result) =
544 self.verify_zero_lt_div_from_known_atomic_facts_builtin_rule(atomic_fact)?
545 {
546 return Ok(result);
547 }
548
549 if let AtomicFact::LessEqualFact(less_equal_fact) = atomic_fact {
550 if less_equal_fact.left.to_string() == less_equal_fact.right.to_string() {
551 return Ok(StmtResult::FactualStmtSuccess(
552 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
553 less_equal_fact.clone().into(),
554 "less_equal_fact_equal".to_string(),
555 Vec::new(),
556 ),
557 ));
558 }
559 let strict_fact: Fact = LessFact::new(
560 less_equal_fact.left.clone(),
561 less_equal_fact.right.clone(),
562 less_equal_fact.line_file.clone(),
563 )
564 .into();
565 let strict_key = strict_fact.to_string();
566 let (cache_ok, cache_line_file) = self.cache_known_facts_contains(&strict_key);
567 if cache_ok {
568 return Ok(StmtResult::FactualStmtSuccess(
569 FactualStmtSuccess::new_with_verified_by_known_fact_source_recording_facts(
570 less_equal_fact.clone().into(),
571 strict_key,
572 Some(strict_fact),
573 Some(cache_line_file),
574 Vec::new(),
575 ),
576 ));
577 }
578 }
579 if let AtomicFact::GreaterEqualFact(greater_equal_fact) = atomic_fact {
580 if greater_equal_fact.left.to_string() == greater_equal_fact.right.to_string() {
581 return Ok(StmtResult::FactualStmtSuccess(
582 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
583 greater_equal_fact.clone().into(),
584 "greater_equal_fact_equal".to_string(),
585 Vec::new(),
586 ),
587 ));
588 }
589 }
590 if let Some(true) = self.verify_number_comparison_builtin_rule(atomic_fact) {
591 Ok(StmtResult::FactualStmtSuccess(
592 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
593 atomic_fact.clone().into(),
594 "number comparison".to_string(),
595 Vec::new(),
596 ),
597 ))
598 } else {
599 Ok(StmtResult::StmtUnknown(StmtUnknown::new()))
600 }
601 }
602
603 fn verify_order_from_known_negated_complement(
605 &mut self,
606 atomic_fact: &AtomicFact,
607 ) -> Result<Option<StmtResult>, RuntimeError> {
608 let negated: Option<AtomicFact> = match atomic_fact {
609 AtomicFact::GreaterFact(f) => {
610 Some(NotLessEqualFact::new(f.left.clone(), f.right.clone(), f.line_file.clone()).into())
611 }
612 AtomicFact::LessFact(f) => Some(
613 NotGreaterEqualFact::new(f.left.clone(), f.right.clone(), f.line_file.clone()).into(),
614 ),
615 AtomicFact::GreaterEqualFact(f) => {
616 Some(NotLessFact::new(f.left.clone(), f.right.clone(), f.line_file.clone()).into())
617 }
618 AtomicFact::LessEqualFact(f) => {
619 Some(NotGreaterFact::new(f.left.clone(), f.right.clone(), f.line_file.clone()).into())
620 }
621 _ => None,
622 };
623 let Some(neg) = negated else {
624 return Ok(None);
625 };
626 let sub = self.verify_non_equational_atomic_fact_with_known_atomic_facts(&neg)?;
627 if sub.is_true() {
628 return Ok(Some(
629 FactualStmtSuccess::new_with_verified_by_builtin_rules(
630 atomic_fact.clone().into(),
631 InferResult::new(),
632 "order_from_known_negated_complement".to_string(),
633 vec![sub],
634 )
635 .into(),
636 ));
637 }
638 Ok(None)
639 }
640
641 fn verify_negated_order_from_known_equivalent_order(
643 &mut self,
644 atomic_fact: &AtomicFact,
645 ) -> Result<Option<StmtResult>, RuntimeError> {
646 let candidates: Vec<AtomicFact> = match atomic_fact {
647 AtomicFact::NotLessFact(f) => {
648 let lf = f.line_file.clone();
649 vec![
650 LessEqualFact::new(f.right.clone(), f.left.clone(), lf.clone()).into(),
651 GreaterEqualFact::new(f.left.clone(), f.right.clone(), lf).into(),
652 ]
653 }
654 AtomicFact::NotGreaterFact(f) => {
655 let lf = f.line_file.clone();
656 vec![
657 LessEqualFact::new(f.left.clone(), f.right.clone(), lf.clone()).into(),
658 GreaterEqualFact::new(f.right.clone(), f.left.clone(), lf).into(),
659 ]
660 }
661 AtomicFact::NotLessEqualFact(f) => {
662 let lf = f.line_file.clone();
663 vec![
664 LessFact::new(f.right.clone(), f.left.clone(), lf.clone()).into(),
665 GreaterFact::new(f.left.clone(), f.right.clone(), lf).into(),
666 ]
667 }
668 AtomicFact::NotGreaterEqualFact(f) => {
669 let lf = f.line_file.clone();
670 vec![
671 LessFact::new(f.left.clone(), f.right.clone(), lf.clone()).into(),
672 GreaterFact::new(f.right.clone(), f.left.clone(), lf).into(),
673 ]
674 }
675 _ => return Ok(None),
676 };
677 for candidate in candidates {
678 let sub = self.verify_non_equational_atomic_fact_with_known_atomic_facts(&candidate)?;
679 if sub.is_true() {
680 return Ok(Some(
681 FactualStmtSuccess::new_with_verified_by_builtin_rules(
682 atomic_fact.clone().into(),
683 InferResult::new(),
684 "negated_order_from_known_equivalent_order".to_string(),
685 vec![sub],
686 )
687 .into(),
688 ));
689 }
690 }
691 Ok(None)
692 }
693
694 fn verify_zero_order_on_sub_from_two_sided_order_builtin_rule(
696 &mut self,
697 atomic_fact: &AtomicFact,
698 ) -> Result<Option<StmtResult>, RuntimeError> {
699 let Some(norm) = normalize_positive_order_atomic_fact(atomic_fact) else {
700 return Ok(None);
701 };
702 match &norm {
703 AtomicFact::LessEqualFact(f) if f.left.to_string() == "0" => {
704 let Obj::Sub(sub) = &f.right else {
705 return Ok(None);
706 };
707 let derived: AtomicFact = LessEqualFact::new(
708 sub.right.as_ref().clone(),
709 sub.left.as_ref().clone(),
710 f.line_file.clone(),
711 )
712 .into();
713 let mut result =
714 self.verify_non_equational_atomic_fact_with_known_atomic_facts(&derived)?;
715 if !result.is_true() {
716 result = self.verify_order_atomic_fact_numeric_builtin_only(&derived)?;
717 }
718 if result.is_true() {
719 Ok(Some(StmtResult::FactualStmtSuccess(
720 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
721 atomic_fact.clone().into(),
722 "0 <= u - v from v <= u".to_string(),
723 vec![result],
724 ),
725 )))
726 } else {
727 Ok(None)
728 }
729 }
730 AtomicFact::LessFact(f) if f.left.to_string() == "0" => {
731 let Obj::Sub(sub) = &f.right else {
732 return Ok(None);
733 };
734 let derived: AtomicFact = LessFact::new(
735 sub.right.as_ref().clone(),
736 sub.left.as_ref().clone(),
737 f.line_file.clone(),
738 )
739 .into();
740 let mut result =
741 self.verify_non_equational_atomic_fact_with_known_atomic_facts(&derived)?;
742 if !result.is_true() {
743 result = self.verify_order_atomic_fact_numeric_builtin_only(&derived)?;
744 }
745 if result.is_true() {
746 Ok(Some(StmtResult::FactualStmtSuccess(
747 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
748 atomic_fact.clone().into(),
749 "0 < u - v from v < u".to_string(),
750 vec![result],
751 ),
752 )))
753 } else {
754 Ok(None)
755 }
756 }
757 _ => Ok(None),
758 }
759 }
760
761 fn verify_zero_le_add_from_known_atomic_facts_builtin_rule(
762 &mut self,
763 atomic_fact: &AtomicFact,
764 ) -> Result<Option<StmtResult>, RuntimeError> {
765 let Some(normalized_fact) = normalize_positive_order_atomic_fact(atomic_fact) else {
766 return Ok(None);
767 };
768 let AtomicFact::LessEqualFact(less_equal_fact) = normalized_fact else {
769 return Ok(None);
770 };
771 if less_equal_fact.left.to_string() != "0" {
772 return Ok(None);
773 }
774 let Obj::Add(add_obj) = &less_equal_fact.right else {
775 return Ok(None);
776 };
777
778 let zero = &less_equal_fact.left;
779 let line_file = &less_equal_fact.line_file;
780 let left_verify_result =
781 self.verify_zero_order_on_sub_expr(zero, add_obj.left.as_ref(), true, line_file)?;
782 if !left_verify_result.is_true() {
783 return Ok(None);
784 }
785 let right_verify_result =
786 self.verify_zero_order_on_sub_expr(zero, add_obj.right.as_ref(), true, line_file)?;
787 if !right_verify_result.is_true() {
788 return Ok(None);
789 }
790
791 Ok(Some(StmtResult::FactualStmtSuccess(
792 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
793 atomic_fact.clone().into(),
794 "0 <= a + b from known atomic facts 0 <= a and 0 <= b".to_string(),
795 vec![left_verify_result, right_verify_result],
796 ),
797 )))
798 }
799
800 fn verify_zero_lt_add_from_known_atomic_facts_builtin_rule(
801 &mut self,
802 atomic_fact: &AtomicFact,
803 ) -> Result<Option<StmtResult>, RuntimeError> {
804 let Some(normalized_fact) = normalize_positive_order_atomic_fact(atomic_fact) else {
805 return Ok(None);
806 };
807 let AtomicFact::LessFact(less_fact) = normalized_fact else {
808 return Ok(None);
809 };
810 if less_fact.left.to_string() != "0" {
811 return Ok(None);
812 }
813 let Obj::Add(add_obj) = &less_fact.right else {
814 return Ok(None);
815 };
816
817 let zero = &less_fact.left;
818 let line_file = &less_fact.line_file;
819
820 let strict_then_weak = |this: &mut Self| -> Result<Option<StmtResult>, RuntimeError> {
821 let left_result =
822 this.verify_zero_order_on_sub_expr(zero, add_obj.left.as_ref(), false, line_file)?;
823 if !left_result.is_true() {
824 return Ok(None);
825 }
826 let right_result =
827 this.verify_zero_order_on_sub_expr(zero, add_obj.right.as_ref(), true, line_file)?;
828 if !right_result.is_true() {
829 return Ok(None);
830 }
831 Ok(Some(StmtResult::FactualStmtSuccess(
832 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
833 atomic_fact.clone().into(),
834 "0 < a + b from (0 < a and 0 <= b)".to_string(),
835 vec![left_result, right_result],
836 ),
837 )))
838 };
839 let weak_then_strict = |this: &mut Self| -> Result<Option<StmtResult>, RuntimeError> {
840 let left_result =
841 this.verify_zero_order_on_sub_expr(zero, add_obj.left.as_ref(), true, line_file)?;
842 if !left_result.is_true() {
843 return Ok(None);
844 }
845 let right_result =
846 this.verify_zero_order_on_sub_expr(zero, add_obj.right.as_ref(), false, line_file)?;
847 if !right_result.is_true() {
848 return Ok(None);
849 }
850 Ok(Some(StmtResult::FactualStmtSuccess(
851 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
852 atomic_fact.clone().into(),
853 "0 < a + b from (0 <= a and 0 < b)".to_string(),
854 vec![left_result, right_result],
855 ),
856 )))
857 };
858
859 if let Some(success) = strict_then_weak(self)? {
860 return Ok(Some(success));
861 }
862 weak_then_strict(self)
863 }
864
865 fn verify_zero_le_even_integer_pow_builtin_rule(
866 &mut self,
867 atomic_fact: &AtomicFact,
868 ) -> Result<Option<StmtResult>, RuntimeError> {
869 let Some(normalized_fact) = normalize_positive_order_atomic_fact(atomic_fact) else {
870 return Ok(None);
871 };
872 let AtomicFact::LessEqualFact(less_equal_fact) = normalized_fact else {
873 return Ok(None);
874 };
875 if less_equal_fact.left.to_string() != "0" {
876 return Ok(None);
877 }
878 let right = &less_equal_fact.right;
879 let is_equal_factors_mul = match right {
880 Obj::Mul(mul_obj) => mul_obj.left.to_string() == mul_obj.right.to_string(),
881 _ => false,
882 };
883 let is_even_pow = match right {
884 Obj::Pow(pow_obj) => match pow_obj.exponent.as_ref() {
885 Obj::Number(n) => normalized_decimal_string_is_even_integer(&n.normalized_value),
886 _ => false,
887 },
888 _ => false,
889 };
890 if !is_equal_factors_mul && !is_even_pow {
891 return Ok(None);
892 }
893 let msg = if is_equal_factors_mul {
894 "0 <= a * a from even integer exponent (here 2) (forall a R)".to_string()
895 } else {
896 "0 <= a^n for even integer n (forall a R)".to_string()
897 };
898 Ok(Some(StmtResult::FactualStmtSuccess(
899 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
900 atomic_fact.clone().into(),
901 msg,
902 Vec::new(),
903 ),
904 )))
905 }
906
907 fn verify_zero_lt_even_integer_pow_from_base_nonzero_builtin_rule(
908 &mut self,
909 atomic_fact: &AtomicFact,
910 ) -> Result<Option<StmtResult>, RuntimeError> {
911 let Some(normalized_fact) = normalize_positive_order_atomic_fact(atomic_fact) else {
912 return Ok(None);
913 };
914 let AtomicFact::LessFact(less_fact) = normalized_fact else {
915 return Ok(None);
916 };
917 if less_fact.left.to_string() != "0" {
918 return Ok(None);
919 }
920 let Obj::Pow(pow_obj) = &less_fact.right else {
921 return Ok(None);
922 };
923 let Obj::Number(exp_num) = pow_obj.exponent.as_ref() else {
924 return Ok(None);
925 };
926 if !normalized_decimal_string_is_even_integer(&exp_num.normalized_value) {
927 return Ok(None);
928 }
929
930 let line_file = less_fact.line_file.clone();
931 let base = pow_obj.base.as_ref().clone();
932 let zero_obj: Obj = Number::new("0".to_string()).into();
933 let base_neq_zero: AtomicFact = NotEqualFact::new(base, zero_obj, line_file.clone()).into();
934
935 let neq_result = self.verify_non_equational_known_then_builtin_rules_only(
936 &base_neq_zero,
937 &VerifyState::new(0, true),
938 )?;
939 if !neq_result.is_true() {
940 return Ok(None);
941 }
942
943 Ok(Some(StmtResult::FactualStmtSuccess(
944 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
945 atomic_fact.clone().into(),
946 "0 < a^n for even integer n from a != 0".to_string(),
947 vec![neq_result],
948 ),
949 )))
950 }
951
952 fn verify_zero_lt_pow_from_positive_base_real_exp_builtin_rule(
954 &mut self,
955 atomic_fact: &AtomicFact,
956 ) -> Result<Option<StmtResult>, RuntimeError> {
957 let Some(normalized_fact) = normalize_positive_order_atomic_fact(atomic_fact) else {
958 return Ok(None);
959 };
960 let AtomicFact::LessFact(less_fact) = normalized_fact else {
961 return Ok(None);
962 };
963 if less_fact.left.to_string() != "0" {
964 return Ok(None);
965 }
966 let Obj::Pow(pow_obj) = &less_fact.right else {
967 return Ok(None);
968 };
969 let zero = &less_fact.left;
970 let line_file = &less_fact.line_file;
971 let base = pow_obj.base.as_ref();
972 let base_result = self.verify_zero_order_on_sub_expr(zero, base, false, line_file)?;
973 if !base_result.is_true() {
974 return Ok(None);
975 }
976 let in_r: AtomicFact = InFact::new(
977 (*pow_obj.exponent).clone(),
978 StandardSet::R.into(),
979 line_file.clone(),
980 )
981 .into();
982 let in_r_result = self.verify_non_equational_known_then_builtin_rules_only(
983 &in_r,
984 &VerifyState::new(0, true),
985 )?;
986 if !in_r_result.is_true() {
987 return Ok(None);
988 }
989 Ok(Some(StmtResult::FactualStmtSuccess(
990 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
991 atomic_fact.clone().into(),
992 "0 < a^b from 0 < a and b in R".to_string(),
993 vec![base_result, in_r_result],
994 ),
995 )))
996 }
997
998 fn verify_zero_le_pow_from_positive_base_real_exp_builtin_rule(
1001 &mut self,
1002 atomic_fact: &AtomicFact,
1003 ) -> Result<Option<StmtResult>, RuntimeError> {
1004 let Some(normalized_fact) = normalize_positive_order_atomic_fact(atomic_fact) else {
1005 return Ok(None);
1006 };
1007 let AtomicFact::LessEqualFact(less_equal_fact) = normalized_fact else {
1008 return Ok(None);
1009 };
1010 if less_equal_fact.left.to_string() != "0" {
1011 return Ok(None);
1012 }
1013 let Obj::Pow(pow_obj) = &less_equal_fact.right else {
1014 return Ok(None);
1015 };
1016 let zero = &less_equal_fact.left;
1017 let line_file = &less_equal_fact.line_file;
1018 let base = pow_obj.base.as_ref();
1019 let base_result = self.verify_zero_order_on_sub_expr(zero, base, false, line_file)?;
1020 if !base_result.is_true() {
1021 return Ok(None);
1022 }
1023 let in_r: AtomicFact = InFact::new(
1024 (*pow_obj.exponent).clone(),
1025 StandardSet::R.into(),
1026 line_file.clone(),
1027 )
1028 .into();
1029 let in_r_result = self.verify_non_equational_known_then_builtin_rules_only(
1030 &in_r,
1031 &VerifyState::new(0, true),
1032 )?;
1033 if !in_r_result.is_true() {
1034 return Ok(None);
1035 }
1036 Ok(Some(StmtResult::FactualStmtSuccess(
1037 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
1038 atomic_fact.clone().into(),
1039 "0 <= a^b from 0 < a and b in R".to_string(),
1040 vec![base_result, in_r_result],
1041 ),
1042 )))
1043 }
1044
1045 fn verify_zero_le_pow_integer_exponent_from_nonneg_base_builtin_rule(
1046 &mut self,
1047 atomic_fact: &AtomicFact,
1048 ) -> Result<Option<StmtResult>, RuntimeError> {
1049 let Some(normalized_fact) = normalize_positive_order_atomic_fact(atomic_fact) else {
1050 return Ok(None);
1051 };
1052 let AtomicFact::LessEqualFact(less_equal_fact) = normalized_fact else {
1053 return Ok(None);
1054 };
1055 if less_equal_fact.left.to_string() != "0" {
1056 return Ok(None);
1057 }
1058 let Obj::Pow(pow_obj) = &less_equal_fact.right else {
1059 return Ok(None);
1060 };
1061 let Obj::Number(exp_num) = pow_obj.exponent.as_ref() else {
1062 return Ok(None);
1063 };
1064 if !normalized_decimal_string_is_integer(&exp_num.normalized_value) {
1065 return Ok(None);
1066 }
1067
1068 let zero = &less_equal_fact.left;
1069 let line_file = &less_equal_fact.line_file;
1070 let base = pow_obj.base.as_ref();
1071
1072 let exponent_vs_zero = compare_normalized_number_str_to_zero(&exp_num.normalized_value);
1073 let base_result = match exponent_vs_zero {
1074 NumberCompareResult::Less => {
1075 self.verify_zero_order_on_sub_expr(zero, base, false, line_file)?
1076 }
1077 NumberCompareResult::Equal | NumberCompareResult::Greater => {
1078 self.verify_zero_order_on_sub_expr(zero, base, true, line_file)?
1079 }
1080 };
1081 if !base_result.is_true() {
1082 return Ok(None);
1083 }
1084
1085 let msg = match exponent_vs_zero {
1086 NumberCompareResult::Less => "0 <= a^n from 0 < a and integer n < 0".to_string(),
1087 _ => "0 <= a^n from 0 <= a and integer n".to_string(),
1088 };
1089
1090 Ok(Some(StmtResult::FactualStmtSuccess(
1091 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
1092 atomic_fact.clone().into(),
1093 msg,
1094 vec![base_result],
1095 ),
1096 )))
1097 }
1098
1099 fn verify_zero_le_mul_from_known_atomic_facts_builtin_rule(
1100 &mut self,
1101 atomic_fact: &AtomicFact,
1102 ) -> Result<Option<StmtResult>, RuntimeError> {
1103 let Some(normalized_fact) = normalize_positive_order_atomic_fact(atomic_fact) else {
1104 return Ok(None);
1105 };
1106 let AtomicFact::LessEqualFact(less_equal_fact) = normalized_fact else {
1107 return Ok(None);
1108 };
1109 if less_equal_fact.left.to_string() != "0" {
1110 return Ok(None);
1111 }
1112 let Obj::Mul(mul_obj) = &less_equal_fact.right else {
1113 return Ok(None);
1114 };
1115
1116 let zero = &less_equal_fact.left;
1117 let line_file = &less_equal_fact.line_file;
1118 let left_verify_result =
1119 self.verify_zero_order_on_sub_expr(zero, mul_obj.left.as_ref(), true, line_file)?;
1120 if !left_verify_result.is_true() {
1121 return Ok(None);
1122 }
1123 let right_verify_result =
1124 self.verify_zero_order_on_sub_expr(zero, mul_obj.right.as_ref(), true, line_file)?;
1125 if !right_verify_result.is_true() {
1126 return Ok(None);
1127 }
1128
1129 Ok(Some(StmtResult::FactualStmtSuccess(
1130 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
1131 atomic_fact.clone().into(),
1132 "0 <= a * b from 0 <= a and 0 <= b".to_string(),
1133 vec![left_verify_result, right_verify_result],
1134 ),
1135 )))
1136 }
1137
1138 fn verify_zero_lt_mul_from_known_atomic_facts_builtin_rule(
1139 &mut self,
1140 atomic_fact: &AtomicFact,
1141 ) -> Result<Option<StmtResult>, RuntimeError> {
1142 let Some(normalized_fact) = normalize_positive_order_atomic_fact(atomic_fact) else {
1143 return Ok(None);
1144 };
1145 let AtomicFact::LessFact(less_fact) = normalized_fact else {
1146 return Ok(None);
1147 };
1148 if less_fact.left.to_string() != "0" {
1149 return Ok(None);
1150 }
1151 let Obj::Mul(mul_obj) = &less_fact.right else {
1152 return Ok(None);
1153 };
1154
1155 let zero = &less_fact.left;
1156 let line_file = &less_fact.line_file;
1157 let left_verify_result =
1158 self.verify_zero_order_on_sub_expr(zero, mul_obj.left.as_ref(), false, line_file)?;
1159 if !left_verify_result.is_true() {
1160 return Ok(None);
1161 }
1162 let right_verify_result =
1163 self.verify_zero_order_on_sub_expr(zero, mul_obj.right.as_ref(), false, line_file)?;
1164 if !right_verify_result.is_true() {
1165 return Ok(None);
1166 }
1167
1168 Ok(Some(StmtResult::FactualStmtSuccess(
1169 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
1170 atomic_fact.clone().into(),
1171 "0 < a * b from 0 < a and 0 < b".to_string(),
1172 vec![left_verify_result, right_verify_result],
1173 ),
1174 )))
1175 }
1176
1177 fn verify_zero_le_div_from_known_atomic_facts_builtin_rule(
1178 &mut self,
1179 atomic_fact: &AtomicFact,
1180 ) -> Result<Option<StmtResult>, RuntimeError> {
1181 let Some(normalized_fact) = normalize_positive_order_atomic_fact(atomic_fact) else {
1182 return Ok(None);
1183 };
1184 let AtomicFact::LessEqualFact(less_equal_fact) = normalized_fact else {
1185 return Ok(None);
1186 };
1187 if less_equal_fact.left.to_string() != "0" {
1188 return Ok(None);
1189 }
1190 let Obj::Div(div_obj) = &less_equal_fact.right else {
1191 return Ok(None);
1192 };
1193
1194 let zero = &less_equal_fact.left;
1195 let line_file = &less_equal_fact.line_file;
1196 let numer_result =
1197 self.verify_zero_order_on_sub_expr(zero, div_obj.left.as_ref(), true, line_file)?;
1198 if !numer_result.is_true() {
1199 return Ok(None);
1200 }
1201 let denom_result =
1202 self.verify_zero_order_on_sub_expr(zero, div_obj.right.as_ref(), false, line_file)?;
1203 if !denom_result.is_true() {
1204 return Ok(None);
1205 }
1206
1207 Ok(Some(StmtResult::FactualStmtSuccess(
1208 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
1209 atomic_fact.clone().into(),
1210 "0 <= a / b from 0 <= a and 0 < b".to_string(),
1211 vec![numer_result, denom_result],
1212 ),
1213 )))
1214 }
1215
1216 fn verify_zero_lt_div_from_known_atomic_facts_builtin_rule(
1217 &mut self,
1218 atomic_fact: &AtomicFact,
1219 ) -> Result<Option<StmtResult>, RuntimeError> {
1220 let Some(normalized_fact) = normalize_positive_order_atomic_fact(atomic_fact) else {
1221 return Ok(None);
1222 };
1223 let AtomicFact::LessFact(less_fact) = normalized_fact else {
1224 return Ok(None);
1225 };
1226 if less_fact.left.to_string() != "0" {
1227 return Ok(None);
1228 }
1229 let Obj::Div(div_obj) = &less_fact.right else {
1230 return Ok(None);
1231 };
1232
1233 let zero = &less_fact.left;
1234 let line_file = &less_fact.line_file;
1235 let numer_result =
1236 self.verify_zero_order_on_sub_expr(zero, div_obj.left.as_ref(), false, line_file)?;
1237 if !numer_result.is_true() {
1238 return Ok(None);
1239 }
1240 let denom_result =
1241 self.verify_zero_order_on_sub_expr(zero, div_obj.right.as_ref(), false, line_file)?;
1242 if !denom_result.is_true() {
1243 return Ok(None);
1244 }
1245
1246 Ok(Some(StmtResult::FactualStmtSuccess(
1247 FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
1248 atomic_fact.clone().into(),
1249 "0 < a / b from 0 < a and 0 < b".to_string(),
1250 vec![numer_result, denom_result],
1251 ),
1252 )))
1253 }
1254
1255 pub fn verify_number_comparison_builtin_rule(&self, atomic_fact: &AtomicFact) -> Option<bool> {
1256 let normalized = normalize_positive_order_atomic_fact(atomic_fact)?;
1257 match normalized {
1258 AtomicFact::LessFact(less_fact) => {
1259 if let Some(calculated_number_string_pair) =
1260 self.calculate_obj_pair_to_number_strings(&less_fact.left, &less_fact.right)
1261 {
1262 return Some(matches!(
1263 compare_number_strings(
1264 &calculated_number_string_pair.0,
1265 &calculated_number_string_pair.1
1266 ),
1267 NumberCompareResult::Less
1268 ));
1269 }
1270 self.try_verify_numeric_order_via_div_elimination(
1271 &less_fact.left,
1272 &less_fact.right,
1273 false,
1274 )
1275 }
1276 AtomicFact::LessEqualFact(less_equal_fact) => {
1277 if let Some(calculated_number_string_pair) = self
1278 .calculate_obj_pair_to_number_strings(
1279 &less_equal_fact.left,
1280 &less_equal_fact.right,
1281 )
1282 {
1283 let compare_result = compare_number_strings(
1284 &calculated_number_string_pair.0,
1285 &calculated_number_string_pair.1,
1286 );
1287 return Some(matches!(
1288 compare_result,
1289 NumberCompareResult::Less | NumberCompareResult::Equal
1290 ));
1291 }
1292 self.try_verify_numeric_order_via_div_elimination(
1293 &less_equal_fact.left,
1294 &less_equal_fact.right,
1295 true,
1296 )
1297 }
1298 _ => None,
1299 }
1300 }
1301
1302 fn calculate_obj_pair_to_number_strings(
1303 &self,
1304 left_obj: &Obj,
1305 right_obj: &Obj,
1306 ) -> Option<(String, String)> {
1307 let left_number = self.resolve_obj_to_number_resolved(left_obj)?;
1308 let right_number = self.resolve_obj_to_number_resolved(right_obj)?;
1309 Some((left_number.normalized_value, right_number.normalized_value))
1310 }
1311}