Skip to main content

litex/verify/verify_builtin_rules/
not_equal_builtin.rs

1use crate::prelude::*;
2use crate::verify::verify_number_in_standard_set::is_integer_after_simplification;
3
4impl Runtime {
5    fn try_parse_number_literal_obj_string_for_not_equal_builtin_rule(
6        &self,
7        obj_string: &str,
8    ) -> Option<Number> {
9        let trimmed = obj_string.trim();
10        if trimmed.is_empty() {
11            return None;
12        }
13        let parsed = Number::new(trimmed.to_string());
14        if parsed.to_string() == trimmed {
15            return Some(parsed);
16        }
17        None
18    }
19
20    fn resolve_obj_to_number_for_not_equal_builtin_rule(&self, obj: &Obj) -> Option<Number> {
21        if let Some(number) = self.resolve_obj_to_number_resolved(obj) {
22            return Some(number);
23        }
24        let obj_key = obj.to_string();
25        if let Some(number) = self.get_object_equal_to_normalized_decimal_number(&obj_key) {
26            return Some(number);
27        }
28        let all_equal_obj_strings = self.get_all_objs_equal_to_given(&obj_key);
29        for equal_obj_string in all_equal_obj_strings {
30            if let Some(number) =
31                self.get_object_equal_to_normalized_decimal_number(&equal_obj_string)
32            {
33                return Some(number);
34            }
35            if let Some(number) = self
36                .try_parse_number_literal_obj_string_for_not_equal_builtin_rule(&equal_obj_string)
37            {
38                return Some(number);
39            }
40        }
41        None
42    }
43
44    pub fn _verify_not_equal_fact_with_builtin_rules(
45        &mut self,
46        not_equal_fact: &NotEqualFact,
47        verify_state: &VerifyState,
48    ) -> Result<StmtResult, RuntimeError> {
49        let left_obj = &not_equal_fact.left;
50        let right_obj = &not_equal_fact.right;
51
52        match (
53            self.resolve_obj_to_number_for_not_equal_builtin_rule(left_obj),
54            self.resolve_obj_to_number_for_not_equal_builtin_rule(right_obj),
55        ) {
56            (Some(left_number), Some(right_number)) => {
57                if left_number.normalized_value != right_number.normalized_value {
58                    return Ok(
59                        (FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
60                            not_equal_fact.clone().into(),
61                            "not_equal_numeric_resolved_or_equal_class_calculation".to_string(),
62                            Vec::new(),
63                        ))
64                        .into(),
65                    );
66                }
67            }
68            _ => {}
69        }
70
71        if let (Obj::ListSet(left_ls), Obj::ListSet(right_ls)) = (left_obj, right_obj) {
72            if left_ls.list.len() != right_ls.list.len() {
73                return Ok(
74                    (FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
75                        not_equal_fact.clone().into(),
76                        "list_set_different_length".to_string(),
77                        Vec::new(),
78                    ))
79                    .into(),
80                );
81            }
82        }
83
84        if let Some(verified_result) =
85            self.try_verify_not_equal_from_known_strict_order(not_equal_fact)?
86        {
87            return Ok(verified_result);
88        }
89
90        if let Some(verified_result) =
91            self.try_verify_not_equal_zero_from_n_and_one_le(not_equal_fact, verify_state)?
92        {
93            return Ok(verified_result);
94        }
95
96        if let Some(verified_result) =
97            self.try_verify_not_equal_pow_from_base_nonzero(not_equal_fact, verify_state)?
98        {
99            return Ok(verified_result);
100        }
101
102        match self
103            .try_verify_not_equal_fact_when_zero_and_binary_arithmetic_reduces_by_operand_facts(
104                not_equal_fact,
105                verify_state,
106            )? {
107            Some(verified_result) => return Ok(verified_result),
108            None => {}
109        }
110
111        Ok((StmtUnknown::new()).into())
112    }
113
114    // x < y or x > y (including y < x / y > x spellings) in known facts implies x != y.
115    fn try_verify_not_equal_from_known_strict_order(
116        &mut self,
117        not_equal_fact: &NotEqualFact,
118    ) -> Result<Option<StmtResult>, RuntimeError> {
119        let line_file = not_equal_fact.line_file.clone();
120        let x = not_equal_fact.left.clone();
121        let y = not_equal_fact.right.clone();
122        let candidates: [AtomicFact; 4] = [
123            LessFact::new(x.clone(), y.clone(), line_file.clone()).into(),
124            GreaterFact::new(x.clone(), y.clone(), line_file.clone()).into(),
125            LessFact::new(y.clone(), x.clone(), line_file.clone()).into(),
126            GreaterFact::new(y.clone(), x.clone(), line_file.clone()).into(),
127        ];
128        for order_atomic in candidates {
129            let sub =
130                self.verify_non_equational_atomic_fact_with_known_atomic_facts(&order_atomic)?;
131            if sub.is_true() {
132                return Ok(Some(
133                    FactualStmtSuccess::new_with_verified_by_builtin_rules_label_and_steps(
134                        not_equal_fact.clone().into(),
135                        InferResult::new(),
136                        "not_equal_from_known_strict_order".to_string(),
137                        vec![sub],
138                    )
139                    .into(),
140                ));
141            }
142        }
143        Ok(None)
144    }
145
146    /// `n != 0` from `n $in N` and `1 <= n` (or `n >= 1`). Example: `forall x N: 1 <= x =>: x != 0`.
147    fn try_verify_not_equal_zero_from_n_and_one_le(
148        &mut self,
149        not_equal_fact: &NotEqualFact,
150        verify_state: &VerifyState,
151    ) -> Result<Option<StmtResult>, RuntimeError> {
152        let line_file = not_equal_fact.line_file.clone();
153        let one_obj: Obj = Number::new("1".to_string()).into();
154        let x = match (&not_equal_fact.left, &not_equal_fact.right) {
155            (l, r) if self.obj_represents_zero_for_not_equal_builtin_rules(r) => l.clone(),
156            (l, r) if self.obj_represents_zero_for_not_equal_builtin_rules(l) => r.clone(),
157            _ => return Ok(None),
158        };
159        let in_n: AtomicFact =
160            InFact::new(x.clone(), StandardSet::N.into(), line_file.clone()).into();
161        if !self
162            .verify_non_equational_known_then_builtin_rules_only(&in_n, verify_state)?
163            .is_true()
164        {
165            return Ok(None);
166        }
167        let ge: AtomicFact =
168            GreaterEqualFact::new(x.clone(), one_obj.clone(), line_file.clone()).into();
169        if self
170            .verify_non_equational_atomic_fact_with_known_atomic_facts(&ge)?
171            .is_true()
172        {
173            return Ok(Some(
174                FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
175                    not_equal_fact.clone().into(),
176                    "n != 0 from n $in N and 1 <= n".to_string(),
177                    Vec::new(),
178                )
179                .into(),
180            ));
181        }
182        let one_le: AtomicFact = LessEqualFact::new(one_obj, x, line_file.clone()).into();
183        if self
184            .verify_non_equational_atomic_fact_with_known_atomic_facts(&one_le)?
185            .is_true()
186        {
187            return Ok(Some(
188                FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
189                    not_equal_fact.clone().into(),
190                    "n != 0 from n $in N and 1 <= n".to_string(),
191                    Vec::new(),
192                )
193                .into(),
194            ));
195        }
196        Ok(None)
197    }
198
199    // a^n != 0 with literal integer exponent n, from a != 0 (known / full non-equational verify).
200    fn try_verify_not_equal_pow_from_base_nonzero(
201        &mut self,
202        not_equal_fact: &NotEqualFact,
203        verify_state: &VerifyState,
204    ) -> Result<Option<StmtResult>, RuntimeError> {
205        let line_file = not_equal_fact.line_file.clone();
206        let zero_obj: Obj = Number::new("0".to_string()).into();
207        let pow = match (&not_equal_fact.left, &not_equal_fact.right) {
208            (Obj::Pow(p), r) if self.obj_represents_zero_for_not_equal_builtin_rules(r) => p,
209            (l, Obj::Pow(p)) if self.obj_represents_zero_for_not_equal_builtin_rules(l) => p,
210            _ => return Ok(None),
211        };
212        let Obj::Number(exp_num) = pow.exponent.as_ref() else {
213            return Ok(None);
214        };
215        if !is_integer_after_simplification(exp_num) {
216            return Ok(None);
217        }
218
219        let base = pow.base.as_ref().clone();
220        let base_neq_zero: AtomicFact = NotEqualFact::new(base, zero_obj, line_file.clone()).into();
221        let mut result =
222            self.verify_non_equational_atomic_fact_with_known_atomic_facts(&base_neq_zero)?;
223        if !result.is_true() {
224            result = self.verify_non_equational_atomic_fact(&base_neq_zero, verify_state, true)?;
225        }
226        if result.is_true() {
227            return Ok(Some(
228                FactualStmtSuccess::new_with_verified_by_builtin_rules_label_and_steps(
229                    not_equal_fact.clone().into(),
230                    InferResult::new(),
231                    "not_equal_pow_from_base_nonzero".to_string(),
232                    vec![result],
233                )
234                .into(),
235            ));
236        }
237        Ok(None)
238    }
239
240    fn obj_represents_zero_for_not_equal_builtin_rules(self: &Self, obj: &Obj) -> bool {
241        match self.resolve_obj_to_number(obj) {
242            Some(number) => number.normalized_value == "0",
243            None => false,
244        }
245    }
246
247    fn operand_is_not_equal_to_zero_by_known_non_equational_facts(
248        &mut self,
249        operand: &Obj,
250        line_file: LineFile,
251    ) -> Result<bool, RuntimeError> {
252        let zero_obj: Obj = Number::new("0".to_string()).into();
253        let operand_not_equal_zero_fact =
254            NotEqualFact::new(operand.clone(), zero_obj, line_file).into();
255        let verify_result = self.verify_non_equational_atomic_fact_with_known_atomic_facts(
256            &operand_not_equal_zero_fact,
257        )?;
258        Ok(verify_result.is_true())
259    }
260
261    fn both_operands_nonzero_by_known_non_equational_facts(
262        &mut self,
263        left_operand: &Obj,
264        right_operand: &Obj,
265        line_file: LineFile,
266    ) -> Result<bool, RuntimeError> {
267        let left_nonzero = self.operand_is_not_equal_to_zero_by_known_non_equational_facts(
268            left_operand,
269            line_file.clone(),
270        )?;
271        if !left_nonzero {
272            return Ok(false);
273        }
274        self.operand_is_not_equal_to_zero_by_known_non_equational_facts(right_operand, line_file)
275    }
276
277    fn both_operands_strictly_positive_by_non_equational_verify(
278        &mut self,
279        left_operand: &Obj,
280        right_operand: &Obj,
281        line_file: LineFile,
282        verify_state: &VerifyState,
283    ) -> Result<bool, RuntimeError> {
284        let zero_obj: Obj = Number::new("0".to_string()).into();
285        let zero_less_than_left =
286            LessFact::new(zero_obj.clone(), left_operand.clone(), line_file.clone()).into();
287        if !self.non_equational_atomic_fact_holds_by_full_verify_pipeline(
288            &zero_less_than_left,
289            verify_state,
290        )? {
291            return Ok(false);
292        }
293        let zero_less_than_right = LessFact::new(zero_obj, right_operand.clone(), line_file).into();
294        self.non_equational_atomic_fact_holds_by_full_verify_pipeline(
295            &zero_less_than_right,
296            verify_state,
297        )
298    }
299
300    fn both_operands_strictly_negative_by_non_equational_verify(
301        &mut self,
302        left_operand: &Obj,
303        right_operand: &Obj,
304        line_file: LineFile,
305        verify_state: &VerifyState,
306    ) -> Result<bool, RuntimeError> {
307        let zero_obj: Obj = Number::new("0".to_string()).into();
308        let left_less_than_zero =
309            LessFact::new(left_operand.clone(), zero_obj.clone(), line_file.clone()).into();
310        if !self.non_equational_atomic_fact_holds_by_full_verify_pipeline(
311            &left_less_than_zero,
312            verify_state,
313        )? {
314            return Ok(false);
315        }
316        let right_less_than_zero = LessFact::new(right_operand.clone(), zero_obj, line_file).into();
317        self.non_equational_atomic_fact_holds_by_full_verify_pipeline(
318            &right_less_than_zero,
319            verify_state,
320        )
321    }
322
323    pub fn mul_product_negative_when_factors_have_strict_opposite_sign_by_non_equational_verify(
324        &mut self,
325        left_factor: &Obj,
326        right_factor: &Obj,
327        line_file: LineFile,
328        verify_state: &VerifyState,
329    ) -> Result<bool, RuntimeError> {
330        let zero_obj: Obj = Number::new("0".to_string()).into();
331        let left_less_than_zero =
332            LessFact::new(left_factor.clone(), zero_obj.clone(), line_file.clone()).into();
333        let zero_less_than_right =
334            LessFact::new(zero_obj.clone(), right_factor.clone(), line_file.clone()).into();
335        if self.non_equational_atomic_fact_holds_by_full_verify_pipeline(
336            &left_less_than_zero,
337            verify_state,
338        )? && self.non_equational_atomic_fact_holds_by_full_verify_pipeline(
339            &zero_less_than_right,
340            verify_state,
341        )? {
342            return Ok(true);
343        }
344        let zero_less_than_left =
345            LessFact::new(zero_obj.clone(), left_factor.clone(), line_file.clone()).into();
346        let right_less_than_zero = LessFact::new(right_factor.clone(), zero_obj, line_file).into();
347        Ok(
348            self.non_equational_atomic_fact_holds_by_full_verify_pipeline(
349                &zero_less_than_left,
350                verify_state,
351            )? && self.non_equational_atomic_fact_holds_by_full_verify_pipeline(
352                &right_less_than_zero,
353                verify_state,
354            )?,
355        )
356    }
357
358    fn sub_difference_nonzero_when_operands_have_strict_opposite_sign_by_non_equational_verify(
359        &mut self,
360        minuend: &Obj,
361        subtrahend: &Obj,
362        line_file: LineFile,
363        verify_state: &VerifyState,
364    ) -> Result<bool, RuntimeError> {
365        let zero_obj: Obj = Number::new("0".to_string()).into();
366        let zero_less_than_minuend =
367            LessFact::new(zero_obj.clone(), minuend.clone(), line_file.clone()).into();
368        let subtrahend_less_than_zero =
369            LessFact::new(subtrahend.clone(), zero_obj.clone(), line_file.clone()).into();
370        if self.non_equational_atomic_fact_holds_by_full_verify_pipeline(
371            &zero_less_than_minuend,
372            verify_state,
373        )? && self.non_equational_atomic_fact_holds_by_full_verify_pipeline(
374            &subtrahend_less_than_zero,
375            verify_state,
376        )? {
377            return Ok(true);
378        }
379        let minuend_less_than_zero =
380            LessFact::new(minuend.clone(), zero_obj.clone(), line_file.clone()).into();
381        let zero_less_than_subtrahend =
382            LessFact::new(zero_obj, subtrahend.clone(), line_file).into();
383        Ok(
384            self.non_equational_atomic_fact_holds_by_full_verify_pipeline(
385                &minuend_less_than_zero,
386                verify_state,
387            )? && self.non_equational_atomic_fact_holds_by_full_verify_pipeline(
388                &zero_less_than_subtrahend,
389                verify_state,
390            )?,
391        )
392    }
393
394    fn try_verify_not_equal_fact_when_zero_and_binary_arithmetic_reduces_by_operand_facts(
395        &mut self,
396        not_equal_fact: &NotEqualFact,
397        verify_state: &VerifyState,
398    ) -> Result<Option<StmtResult>, RuntimeError> {
399        let line_file = not_equal_fact.line_file.clone();
400        let expression_obj =
401            if self.obj_represents_zero_for_not_equal_builtin_rules(&not_equal_fact.right) {
402                &not_equal_fact.left
403            } else if self.obj_represents_zero_for_not_equal_builtin_rules(&not_equal_fact.left) {
404                &not_equal_fact.right
405            } else {
406                return Ok(None);
407            };
408
409        let builtin_rule_label = match expression_obj {
410            Obj::Add(add) => {
411                if self.both_operands_strictly_positive_by_non_equational_verify(
412                    &add.left,
413                    &add.right,
414                    line_file.clone(),
415                    verify_state,
416                )? {
417                    Some("add_not_equal_zero_both_operands_strictly_positive")
418                } else if self.both_operands_strictly_negative_by_non_equational_verify(
419                    &add.left,
420                    &add.right,
421                    line_file.clone(),
422                    verify_state,
423                )? {
424                    Some("add_not_equal_zero_both_operands_strictly_negative")
425                } else {
426                    None
427                }
428            }
429            Obj::Mul(mul) => {
430                if self.both_operands_nonzero_by_known_non_equational_facts(
431                    &mul.left,
432                    &mul.right,
433                    line_file.clone(),
434                )? {
435                    Some("mul_not_equal_zero_both_factors_nonzero_by_known_facts")
436                } else if self.both_operands_strictly_positive_by_non_equational_verify(
437                    &mul.left,
438                    &mul.right,
439                    line_file.clone(),
440                    verify_state,
441                )? {
442                    Some("mul_not_equal_zero_both_factors_strictly_positive")
443                } else if self.both_operands_strictly_negative_by_non_equational_verify(
444                    &mul.left,
445                    &mul.right,
446                    line_file.clone(),
447                    verify_state,
448                )? {
449                    Some("mul_not_equal_zero_both_factors_strictly_negative")
450                } else {
451                    None
452                }
453            }
454            Obj::Sub(sub) => {
455                if self.sub_difference_nonzero_when_operands_have_strict_opposite_sign_by_non_equational_verify(
456                    &sub.left,
457                    &sub.right,
458                    line_file,
459                    verify_state,
460                )? {
461                    Some("sub_not_equal_zero_operands_strict_opposite_sign")
462                } else {
463                    None
464                }
465            }
466            other => {
467                let zero_obj: Obj = Number::new("0".to_string()).into();
468                let zero_lt_a = LessFact::new(
469                    zero_obj.clone(),
470                    other.clone(),
471                    line_file.clone(),
472                ).into();
473
474                let final_round_verify_state = verify_state.make_final_round_state();
475
476                if self.non_equational_atomic_fact_holds_by_full_verify_pipeline(
477                    &zero_lt_a,
478                    &final_round_verify_state,
479                )? {
480                    Some("not_equal_zero_operand_strictly_positive")
481                } else {
482                    let a_lt_0 = LessFact::new(
483                        other.clone(),
484                        zero_obj,
485                        line_file.clone(),
486                    ).into();
487                    if self.non_equational_atomic_fact_holds_by_full_verify_pipeline(
488                        &a_lt_0,
489                        &final_round_verify_state,
490                    )? {
491                        Some("not_equal_zero_operand_strictly_negative")
492                    } else {
493                        None
494                    }
495                }
496            }
497        };
498
499        match builtin_rule_label {
500            Some(rule_label) => Ok(Some(
501                (FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
502                    not_equal_fact.clone().into(),
503                    rule_label.to_string(),
504                    Vec::new(),
505                ))
506                .into(),
507            )),
508            None => Ok(None),
509        }
510    }
511}