1use crate::prelude::*;
2use crate::verify::verify_equality_by_builtin_rules::objs_equal_by_display_string;
3use std::result::Result;
4
5fn order_split_or_is_exhaustive_pair(a: &AtomicFact, b: &AtomicFact) -> bool {
8 use AtomicFact::*;
9 match (a, b) {
10 (GreaterFact(g), LessEqualFact(le)) => {
11 objs_equal_by_display_string(&g.left, &le.left)
12 && objs_equal_by_display_string(&g.right, &le.right)
13 }
14 (LessFact(l), GreaterEqualFact(ge)) => {
15 objs_equal_by_display_string(&l.left, &ge.left)
16 && objs_equal_by_display_string(&l.right, &ge.right)
17 }
18 (LessEqualFact(le), GreaterFact(g)) => {
19 objs_equal_by_display_string(&le.left, &g.left)
20 && objs_equal_by_display_string(&le.right, &g.right)
21 }
22 (GreaterEqualFact(ge), LessFact(l)) => {
23 objs_equal_by_display_string(&ge.left, &l.left)
24 && objs_equal_by_display_string(&ge.right, &l.right)
25 }
26 _ => false,
27 }
28}
29
30fn obj_is_literal_neg_one_for_abs_or_builtin(obj: &Obj) -> bool {
31 match obj {
32 Obj::Number(n) => n.normalized_value == "-1",
33 _ => false,
34 }
35}
36
37fn obj_is_negation_of_for_abs_or_builtin(obj: &Obj, expected_arg: &Obj) -> bool {
38 match obj {
39 Obj::Mul(m) => {
40 (obj_is_literal_neg_one_for_abs_or_builtin(m.left.as_ref())
41 && objs_equal_by_display_string(m.right.as_ref(), expected_arg))
42 || (obj_is_literal_neg_one_for_abs_or_builtin(m.right.as_ref())
43 && objs_equal_by_display_string(m.left.as_ref(), expected_arg))
44 }
45 _ => false,
46 }
47}
48
49fn abs_sign_split_or_is_exhaustive_pair(a: &AtomicFact, b: &AtomicFact) -> bool {
50 let (AtomicFact::EqualFact(first), AtomicFact::EqualFact(second)) = (a, b) else {
51 return false;
52 };
53 let (arg, first_other) = match (&first.left, &first.right) {
54 (Obj::Abs(abs), other) => (abs.arg.as_ref(), other),
55 (other, Obj::Abs(abs)) => (abs.arg.as_ref(), other),
56 _ => return false,
57 };
58 if !objs_equal_by_display_string(arg, first_other) {
59 return false;
60 }
61 let (second_arg, second_other) = match (&second.left, &second.right) {
62 (Obj::Abs(abs), other) => (abs.arg.as_ref(), other),
63 (other, Obj::Abs(abs)) => (abs.arg.as_ref(), other),
64 _ => return false,
65 };
66 objs_equal_by_display_string(arg, second_arg)
67 && obj_is_negation_of_for_abs_or_builtin(second_other, arg)
68}
69
70fn positive_integer_number_to_usize_for_mod_or_builtin(number: &Number) -> Option<usize> {
71 let value = normalized_nonnegative_integer_number_to_usize_for_mod_or_builtin(number)?;
72 if value == 0 {
73 return None;
74 }
75 Some(value)
76}
77
78fn nonnegative_integer_number_to_usize_for_mod_or_builtin(number: &Number) -> Option<usize> {
79 normalized_nonnegative_integer_number_to_usize_for_mod_or_builtin(number)
80}
81
82fn normalized_nonnegative_integer_number_to_usize_for_mod_or_builtin(
83 number: &Number,
84) -> Option<usize> {
85 let value = number.normalized_value.trim();
86 if value.starts_with('-') {
87 return None;
88 }
89 let unsigned = value.trim_start_matches('+');
90 let integer_part = match unsigned.find('.') {
91 Some(index) => {
92 let fractional_part = &unsigned[index + 1..];
93 if !fractional_part.chars().all(|c| c == '0') {
94 return None;
95 }
96 &unsigned[..index]
97 }
98 None => unsigned,
99 };
100 integer_part.parse::<usize>().ok()
101}
102
103fn mod_obj_and_residue_from_atomic_equal_for_mod_or_builtin(
104 atomic_fact: &AtomicFact,
105) -> Option<(&Obj, &Number, &Number)> {
106 let AtomicFact::EqualFact(eq) = atomic_fact else {
107 return None;
108 };
109 match (&eq.left, &eq.right) {
110 (Obj::Mod(m), Obj::Number(residue)) => match m.right.as_ref() {
111 Obj::Number(modulus) => Some((m.left.as_ref(), modulus, residue)),
112 _ => None,
113 },
114 (Obj::Number(residue), Obj::Mod(m)) => match m.right.as_ref() {
115 Obj::Number(modulus) => Some((m.left.as_ref(), modulus, residue)),
116 _ => None,
117 },
118 _ => None,
119 }
120}
121
122fn mod_positive_integer_residue_or_is_exhaustive(or_fact: &OrFact) -> bool {
126 if or_fact.facts.is_empty() {
127 return false;
128 }
129
130 let first_atomic = match &or_fact.facts[0] {
131 AndChainAtomicFact::AtomicFact(atomic) => atomic,
132 _ => return false,
133 };
134 let Some((first_obj, first_modulus, first_residue)) =
135 mod_obj_and_residue_from_atomic_equal_for_mod_or_builtin(first_atomic)
136 else {
137 return false;
138 };
139 let Some(modulus_value) = positive_integer_number_to_usize_for_mod_or_builtin(first_modulus)
140 else {
141 return false;
142 };
143 if modulus_value <= 1 || modulus_value != or_fact.facts.len() {
144 return false;
145 }
146
147 let mut seen_residues = vec![false; modulus_value];
148 let Some(first_residue_value) =
149 nonnegative_integer_number_to_usize_for_mod_or_builtin(first_residue)
150 else {
151 return false;
152 };
153 if first_residue_value >= modulus_value {
154 return false;
155 }
156 seen_residues[first_residue_value] = true;
157
158 for fact in or_fact.facts.iter().skip(1) {
159 let AndChainAtomicFact::AtomicFact(atomic) = fact else {
160 return false;
161 };
162 let Some((obj, modulus, residue)) =
163 mod_obj_and_residue_from_atomic_equal_for_mod_or_builtin(atomic)
164 else {
165 return false;
166 };
167 if !objs_equal_by_display_string(obj, first_obj)
168 || !objs_equal_by_display_string(
169 &Obj::Number(modulus.clone()),
170 &Obj::Number(first_modulus.clone()),
171 )
172 {
173 return false;
174 }
175 let Some(residue_value) = nonnegative_integer_number_to_usize_for_mod_or_builtin(residue)
176 else {
177 return false;
178 };
179 if residue_value >= modulus_value || seen_residues[residue_value] {
180 return false;
181 }
182 seen_residues[residue_value] = true;
183 }
184
185 seen_residues.iter().all(|seen| *seen)
186}
187
188fn obj_is_literal_zero_for_or_builtin(obj: &Obj) -> bool {
189 match obj {
190 Obj::Number(n) => n.normalized_value == "0",
191 _ => false,
192 }
193}
194
195fn nonzero_operand_from_atomic_fact_for_square_sum_or_builtin(atomic: &AtomicFact) -> Option<Obj> {
196 let AtomicFact::NotEqualFact(not_equal) = atomic else {
197 return None;
198 };
199 if obj_is_literal_zero_for_or_builtin(¬_equal.right) {
200 return Some(not_equal.left.clone());
201 }
202 if obj_is_literal_zero_for_or_builtin(¬_equal.left) {
203 return Some(not_equal.right.clone());
204 }
205 None
206}
207
208fn square_pow_sum_not_equal_zero_fact_for_or_builtin(
209 left_base: Obj,
210 right_base: Obj,
211 line_file: LineFile,
212) -> AtomicFact {
213 let two_obj: Obj = Number::new("2".to_string()).into();
214 let zero_obj: Obj = Number::new("0".to_string()).into();
215 let left_square: Obj = Pow::new(left_base, two_obj.clone()).into();
216 let right_square: Obj = Pow::new(right_base, two_obj).into();
217 let square_sum: Obj = Add::new(left_square, right_square).into();
218 NotEqualFact::new(square_sum, zero_obj, line_file).into()
219}
220
221fn square_mul_sum_not_equal_zero_fact_for_or_builtin(
222 left_base: Obj,
223 right_base: Obj,
224 line_file: LineFile,
225) -> AtomicFact {
226 let zero_obj: Obj = Number::new("0".to_string()).into();
227 let left_square: Obj = Mul::new(left_base.clone(), left_base).into();
228 let right_square: Obj = Mul::new(right_base.clone(), right_base).into();
229 let square_sum: Obj = Add::new(left_square, right_square).into();
230 NotEqualFact::new(square_sum, zero_obj, line_file).into()
231}
232
233impl Runtime {
234 pub fn verify_or_fact(
235 &mut self,
236 or_fact: &OrFact,
237 verify_state: &VerifyState,
238 ) -> Result<StmtResult, RuntimeError> {
239 if let Some(cached_result) =
240 self.verify_fact_from_cache_using_display_string(&or_fact.clone().into())
241 {
242 return Ok(cached_result);
243 }
244
245 if !verify_state.well_defined_already_verified {
246 if let Err(e) = self.verify_or_fact_well_defined(or_fact, verify_state) {
247 return Err({
248 VerifyRuntimeError(RuntimeErrorStruct::new(
249 Some(Fact::from(or_fact.clone()).into_stmt()),
250 String::new(),
251 or_fact.line_file.clone(),
252 Some(e),
253 vec![],
254 ))
255 .into()
256 });
257 }
258 }
259
260 let verify_state_for_children = verify_state.make_state_with_req_ok_set_to_true();
261
262 if or_fact.facts.len() == 2 {
263 if let (
264 AndChainAtomicFact::AtomicFact(first_atomic),
265 AndChainAtomicFact::AtomicFact(second_atomic),
266 ) = (&or_fact.facts[0], &or_fact.facts[1])
267 {
268 if first_atomic.make_reversed().to_string() == second_atomic.to_string() {
269 return Ok(
270 (FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
271 or_fact.clone().into(),
272 "or: complementary atomic facts (make_reversed first equals second)"
273 .to_string(),
274 Vec::new(),
275 ))
276 .into(),
277 );
278 }
279 if order_split_or_is_exhaustive_pair(first_atomic, second_atomic)
280 || order_split_or_is_exhaustive_pair(second_atomic, first_atomic)
281 {
282 return Ok(
283 (FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
284 or_fact.clone().into(),
285 "or: complementary order relations (strict vs non-strict) on the same terms"
286 .to_string(),
287 Vec::new(),
288 ))
289 .into(),
290 );
291 }
292 if abs_sign_split_or_is_exhaustive_pair(first_atomic, second_atomic)
293 || abs_sign_split_or_is_exhaustive_pair(second_atomic, first_atomic)
294 {
295 return Ok(
296 (FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
297 or_fact.clone().into(),
298 "or: abs(x) is x or -x".to_string(),
299 Vec::new(),
300 ))
301 .into(),
302 );
303 }
304 }
305 }
306
307 if mod_positive_integer_residue_or_is_exhaustive(or_fact) {
308 return Ok(
309 (FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
310 or_fact.clone().into(),
311 "or: complete residue classes modulo a positive integer".to_string(),
312 Vec::new(),
313 ))
314 .into(),
315 );
316 }
317
318 if let Some(result) =
319 self.try_verify_component_nonzero_or_from_known_square_sum_not_equal_zero(or_fact)?
320 {
321 return Ok(result);
322 }
323
324 for fact in or_fact.facts.iter() {
325 let result = self.verify_and_chain_atomic_fact(fact, &verify_state_for_children)?;
326 if result.is_true() {
327 return Ok((FactualStmtSuccess::new_with_verified_by_known_fact(
328 or_fact.clone().into(),
329 VerifiedByResult::wrap_bys(vec![VerifiedBysEnum::cited_fact(
330 or_fact.clone().into(),
331 fact.clone().into(),
332 None,
333 )]),
334 Vec::new(),
335 ))
336 .into());
337 }
338 }
339
340 let result = self.verify_or_fact_with_known_or_facts(or_fact)?;
341 if result.is_true() {
342 return Ok(result);
343 }
344
345 let result = self.verify_or_fact_with_known_forall(or_fact, verify_state)?;
346 if result.is_true() {
347 return Ok(result);
348 }
349
350 Ok((StmtUnknown::new()).into())
351 }
352
353 fn try_verify_component_nonzero_or_from_known_square_sum_not_equal_zero(
354 &mut self,
355 or_fact: &OrFact,
356 ) -> Result<Option<StmtResult>, RuntimeError> {
357 if or_fact.facts.len() != 2 {
358 return Ok(None);
359 }
360 let (
361 AndChainAtomicFact::AtomicFact(first_atomic),
362 AndChainAtomicFact::AtomicFact(second_atomic),
363 ) = (&or_fact.facts[0], &or_fact.facts[1])
364 else {
365 return Ok(None);
366 };
367 let Some(first_base) =
368 nonzero_operand_from_atomic_fact_for_square_sum_or_builtin(first_atomic)
369 else {
370 return Ok(None);
371 };
372 let Some(second_base) =
373 nonzero_operand_from_atomic_fact_for_square_sum_or_builtin(second_atomic)
374 else {
375 return Ok(None);
376 };
377
378 let line_file = or_fact.line_file.clone();
379 let candidates = vec![
380 square_pow_sum_not_equal_zero_fact_for_or_builtin(
381 first_base.clone(),
382 second_base.clone(),
383 line_file.clone(),
384 ),
385 square_pow_sum_not_equal_zero_fact_for_or_builtin(
386 second_base.clone(),
387 first_base.clone(),
388 line_file.clone(),
389 ),
390 square_mul_sum_not_equal_zero_fact_for_or_builtin(
391 first_base.clone(),
392 second_base.clone(),
393 line_file.clone(),
394 ),
395 square_mul_sum_not_equal_zero_fact_for_or_builtin(second_base, first_base, line_file),
396 ];
397
398 for candidate in candidates {
399 let result =
400 self.verify_non_equational_atomic_fact_with_known_atomic_facts(&candidate)?;
401 if result.is_true() {
402 return Ok(Some(
403 FactualStmtSuccess::new_with_verified_by_builtin_rules_label_and_steps(
404 or_fact.clone().into(),
405 InferResult::new(),
406 "or: square sum nonzero implies one component nonzero".to_string(),
407 vec![result],
408 )
409 .into(),
410 ));
411 }
412 }
413 Ok(None)
414 }
415
416 pub fn verify_or_fact_with_known_or_facts(
417 &mut self,
418 or_fact: &OrFact,
419 ) -> Result<StmtResult, RuntimeError> {
420 let args_in_or_fact = or_fact.get_args_from_fact();
421 let mut all_objs_equal_to_each_arg: Vec<Vec<String>> = Vec::new();
422 for arg in args_in_or_fact.iter() {
423 let mut all_objs_equal_to_current_arg =
424 self.get_all_objs_equal_to_given(&arg.to_string());
425 if all_objs_equal_to_current_arg.is_empty() {
426 all_objs_equal_to_current_arg.push(arg.to_string());
427 }
428 all_objs_equal_to_each_arg.push(all_objs_equal_to_current_arg);
429 }
430
431 for environment in self.iter_environments_from_top() {
432 let result = Self::verify_or_fact_with_known_or_facts_with_facts_in_environment(
433 environment,
434 or_fact,
435 &all_objs_equal_to_each_arg,
436 )?;
437 if result.is_true() {
438 return Ok(result);
439 }
440 }
441
442 Ok((StmtUnknown::new()).into())
443 }
444
445 pub fn verify_or_fact_with_known_or_facts_with_facts_in_environment(
446 environment: &Environment,
447 or_fact: &OrFact,
448 all_objs_equal_to_each_arg: &Vec<Vec<String>>,
449 ) -> Result<StmtResult, RuntimeError> {
450 if let Some(known_or_facts) = environment.known_or_facts.get(&or_fact.key()) {
451 for known_or_fact in known_or_facts.iter() {
452 let result = Self::_verify_or_fact_the_same_type_and_return_matched_args(
453 known_or_fact,
454 or_fact,
455 )?;
456 let mut all_args_match = true;
457 if let Some(matched_args) = result {
458 for (index, matched_arg) in matched_args.iter().enumerate() {
459 let known_arg_string = matched_arg.0.to_string();
460
461 if known_arg_string != matched_arg.1.to_string()
462 && !all_objs_equal_to_each_arg[index].contains(&known_arg_string)
463 {
464 all_args_match = false;
465 break;
466 }
467 }
468 }
469
470 if all_args_match {
471 return Ok((FactualStmtSuccess::new_with_verified_by_known_fact(
472 or_fact.clone().into(),
473 VerifiedByResult::wrap_bys(vec![VerifiedBysEnum::cited_fact(
474 or_fact.clone().into(),
475 known_or_fact.clone().into(),
476 None,
477 )]),
478 Vec::new(),
479 ))
480 .into());
481 }
482 }
483 }
484
485 return Ok((StmtUnknown::new()).into());
486 }
487}