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_pow_from_base_nonzero(not_equal_fact, verify_state)?
92        {
93            return Ok(verified_result);
94        }
95
96        match self
97            .try_verify_not_equal_fact_when_zero_and_binary_arithmetic_reduces_by_operand_facts(
98                not_equal_fact,
99                verify_state,
100            )? {
101            Some(verified_result) => return Ok(verified_result),
102            None => {}
103        }
104
105        Ok((StmtUnknown::new()).into())
106    }
107
108    // x < y or x > y (including y < x / y > x spellings) in known facts implies x != y.
109    fn try_verify_not_equal_from_known_strict_order(
110        &mut self,
111        not_equal_fact: &NotEqualFact,
112    ) -> Result<Option<StmtResult>, RuntimeError> {
113        let line_file = not_equal_fact.line_file.clone();
114        let x = not_equal_fact.left.clone();
115        let y = not_equal_fact.right.clone();
116        let candidates: [AtomicFact; 4] = [
117            LessFact::new(x.clone(), y.clone(), line_file.clone()).into(),
118            GreaterFact::new(x.clone(), y.clone(), line_file.clone()).into(),
119            LessFact::new(y.clone(), x.clone(), line_file.clone()).into(),
120            GreaterFact::new(y.clone(), x.clone(), line_file.clone()).into(),
121        ];
122        for order_atomic in candidates {
123            let sub =
124                self.verify_non_equational_atomic_fact_with_known_atomic_facts(&order_atomic)?;
125            if sub.is_true() {
126                return Ok(Some(
127                    FactualStmtSuccess::new_with_verified_by_builtin_rules(
128                        not_equal_fact.clone().into(),
129                        InferResult::new(),
130                        "not_equal_from_known_strict_order".to_string(),
131                        vec![sub],
132                    )
133                    .into(),
134                ));
135            }
136        }
137        Ok(None)
138    }
139
140    // a^n != 0 with literal integer exponent n, from a != 0 (known / full non-equational verify).
141    fn try_verify_not_equal_pow_from_base_nonzero(
142        &mut self,
143        not_equal_fact: &NotEqualFact,
144        verify_state: &VerifyState,
145    ) -> Result<Option<StmtResult>, RuntimeError> {
146        let line_file = not_equal_fact.line_file.clone();
147        let zero_obj: Obj = Number::new("0".to_string()).into();
148        let pow = match (&not_equal_fact.left, &not_equal_fact.right) {
149            (Obj::Pow(p), r) if self.obj_represents_zero_for_not_equal_builtin_rules(r) => p,
150            (l, Obj::Pow(p)) if self.obj_represents_zero_for_not_equal_builtin_rules(l) => p,
151            _ => return Ok(None),
152        };
153        let Obj::Number(exp_num) = pow.exponent.as_ref() else {
154            return Ok(None);
155        };
156        if !is_integer_after_simplification(exp_num) {
157            return Ok(None);
158        }
159
160        let base = pow.base.as_ref().clone();
161        let base_neq_zero: AtomicFact = NotEqualFact::new(base, zero_obj, line_file.clone()).into();
162        let mut result =
163            self.verify_non_equational_atomic_fact_with_known_atomic_facts(&base_neq_zero)?;
164        if !result.is_true() {
165            result = self.verify_non_equational_atomic_fact(&base_neq_zero, verify_state, true)?;
166        }
167        if result.is_true() {
168            return Ok(Some(
169                FactualStmtSuccess::new_with_verified_by_builtin_rules(
170                    not_equal_fact.clone().into(),
171                    InferResult::new(),
172                    "not_equal_pow_from_base_nonzero".to_string(),
173                    vec![result],
174                )
175                .into(),
176            ));
177        }
178        Ok(None)
179    }
180
181    fn obj_represents_zero_for_not_equal_builtin_rules(self: &Self, obj: &Obj) -> bool {
182        match self.resolve_obj_to_number(obj) {
183            Some(number) => number.normalized_value == "0",
184            None => false,
185        }
186    }
187
188    fn operand_is_not_equal_to_zero_by_known_non_equational_facts(
189        &mut self,
190        operand: &Obj,
191        line_file: LineFile,
192    ) -> Result<bool, RuntimeError> {
193        let zero_obj: Obj = Number::new("0".to_string()).into();
194        let operand_not_equal_zero_fact =
195            NotEqualFact::new(operand.clone(), zero_obj, line_file).into();
196        let verify_result = self.verify_non_equational_atomic_fact_with_known_atomic_facts(
197            &operand_not_equal_zero_fact,
198        )?;
199        Ok(verify_result.is_true())
200    }
201
202    fn both_operands_nonzero_by_known_non_equational_facts(
203        &mut self,
204        left_operand: &Obj,
205        right_operand: &Obj,
206        line_file: LineFile,
207    ) -> Result<bool, RuntimeError> {
208        let left_nonzero = self.operand_is_not_equal_to_zero_by_known_non_equational_facts(
209            left_operand,
210            line_file.clone(),
211        )?;
212        if !left_nonzero {
213            return Ok(false);
214        }
215        self.operand_is_not_equal_to_zero_by_known_non_equational_facts(right_operand, line_file)
216    }
217
218    fn both_operands_strictly_positive_by_non_equational_verify(
219        &mut self,
220        left_operand: &Obj,
221        right_operand: &Obj,
222        line_file: LineFile,
223        verify_state: &VerifyState,
224    ) -> Result<bool, RuntimeError> {
225        let zero_obj: Obj = Number::new("0".to_string()).into();
226        let zero_less_than_left =
227            LessFact::new(zero_obj.clone(), left_operand.clone(), line_file.clone()).into();
228        if !self.non_equational_atomic_fact_holds_by_full_verify_pipeline(
229            &zero_less_than_left,
230            verify_state,
231        )? {
232            return Ok(false);
233        }
234        let zero_less_than_right = LessFact::new(zero_obj, right_operand.clone(), line_file).into();
235        self.non_equational_atomic_fact_holds_by_full_verify_pipeline(
236            &zero_less_than_right,
237            verify_state,
238        )
239    }
240
241    fn both_operands_strictly_negative_by_non_equational_verify(
242        &mut self,
243        left_operand: &Obj,
244        right_operand: &Obj,
245        line_file: LineFile,
246        verify_state: &VerifyState,
247    ) -> Result<bool, RuntimeError> {
248        let zero_obj: Obj = Number::new("0".to_string()).into();
249        let left_less_than_zero =
250            LessFact::new(left_operand.clone(), zero_obj.clone(), line_file.clone()).into();
251        if !self.non_equational_atomic_fact_holds_by_full_verify_pipeline(
252            &left_less_than_zero,
253            verify_state,
254        )? {
255            return Ok(false);
256        }
257        let right_less_than_zero = LessFact::new(right_operand.clone(), zero_obj, line_file).into();
258        self.non_equational_atomic_fact_holds_by_full_verify_pipeline(
259            &right_less_than_zero,
260            verify_state,
261        )
262    }
263
264    pub fn mul_product_negative_when_factors_have_strict_opposite_sign_by_non_equational_verify(
265        &mut self,
266        left_factor: &Obj,
267        right_factor: &Obj,
268        line_file: LineFile,
269        verify_state: &VerifyState,
270    ) -> Result<bool, RuntimeError> {
271        let zero_obj: Obj = Number::new("0".to_string()).into();
272        let left_less_than_zero =
273            LessFact::new(left_factor.clone(), zero_obj.clone(), line_file.clone()).into();
274        let zero_less_than_right =
275            LessFact::new(zero_obj.clone(), right_factor.clone(), line_file.clone()).into();
276        if self.non_equational_atomic_fact_holds_by_full_verify_pipeline(
277            &left_less_than_zero,
278            verify_state,
279        )? && self.non_equational_atomic_fact_holds_by_full_verify_pipeline(
280            &zero_less_than_right,
281            verify_state,
282        )? {
283            return Ok(true);
284        }
285        let zero_less_than_left =
286            LessFact::new(zero_obj.clone(), left_factor.clone(), line_file.clone()).into();
287        let right_less_than_zero = LessFact::new(right_factor.clone(), zero_obj, line_file).into();
288        Ok(
289            self.non_equational_atomic_fact_holds_by_full_verify_pipeline(
290                &zero_less_than_left,
291                verify_state,
292            )? && self.non_equational_atomic_fact_holds_by_full_verify_pipeline(
293                &right_less_than_zero,
294                verify_state,
295            )?,
296        )
297    }
298
299    fn sub_difference_nonzero_when_operands_have_strict_opposite_sign_by_non_equational_verify(
300        &mut self,
301        minuend: &Obj,
302        subtrahend: &Obj,
303        line_file: LineFile,
304        verify_state: &VerifyState,
305    ) -> Result<bool, RuntimeError> {
306        let zero_obj: Obj = Number::new("0".to_string()).into();
307        let zero_less_than_minuend =
308            LessFact::new(zero_obj.clone(), minuend.clone(), line_file.clone()).into();
309        let subtrahend_less_than_zero =
310            LessFact::new(subtrahend.clone(), zero_obj.clone(), line_file.clone()).into();
311        if self.non_equational_atomic_fact_holds_by_full_verify_pipeline(
312            &zero_less_than_minuend,
313            verify_state,
314        )? && self.non_equational_atomic_fact_holds_by_full_verify_pipeline(
315            &subtrahend_less_than_zero,
316            verify_state,
317        )? {
318            return Ok(true);
319        }
320        let minuend_less_than_zero =
321            LessFact::new(minuend.clone(), zero_obj.clone(), line_file.clone()).into();
322        let zero_less_than_subtrahend =
323            LessFact::new(zero_obj, subtrahend.clone(), line_file).into();
324        Ok(
325            self.non_equational_atomic_fact_holds_by_full_verify_pipeline(
326                &minuend_less_than_zero,
327                verify_state,
328            )? && self.non_equational_atomic_fact_holds_by_full_verify_pipeline(
329                &zero_less_than_subtrahend,
330                verify_state,
331            )?,
332        )
333    }
334
335    fn try_verify_not_equal_fact_when_zero_and_binary_arithmetic_reduces_by_operand_facts(
336        &mut self,
337        not_equal_fact: &NotEqualFact,
338        verify_state: &VerifyState,
339    ) -> Result<Option<StmtResult>, RuntimeError> {
340        let line_file = not_equal_fact.line_file.clone();
341        let expression_obj =
342            if self.obj_represents_zero_for_not_equal_builtin_rules(&not_equal_fact.right) {
343                &not_equal_fact.left
344            } else if self.obj_represents_zero_for_not_equal_builtin_rules(&not_equal_fact.left) {
345                &not_equal_fact.right
346            } else {
347                return Ok(None);
348            };
349
350        let builtin_rule_label = match expression_obj {
351            Obj::Add(add) => {
352                if self.both_operands_strictly_positive_by_non_equational_verify(
353                    &add.left,
354                    &add.right,
355                    line_file.clone(),
356                    verify_state,
357                )? {
358                    Some("add_not_equal_zero_both_operands_strictly_positive")
359                } else if self.both_operands_strictly_negative_by_non_equational_verify(
360                    &add.left,
361                    &add.right,
362                    line_file.clone(),
363                    verify_state,
364                )? {
365                    Some("add_not_equal_zero_both_operands_strictly_negative")
366                } else {
367                    None
368                }
369            }
370            Obj::Mul(mul) => {
371                if self.both_operands_nonzero_by_known_non_equational_facts(
372                    &mul.left,
373                    &mul.right,
374                    line_file.clone(),
375                )? {
376                    Some("mul_not_equal_zero_both_factors_nonzero_by_known_facts")
377                } else if self.both_operands_strictly_positive_by_non_equational_verify(
378                    &mul.left,
379                    &mul.right,
380                    line_file.clone(),
381                    verify_state,
382                )? {
383                    Some("mul_not_equal_zero_both_factors_strictly_positive")
384                } else if self.both_operands_strictly_negative_by_non_equational_verify(
385                    &mul.left,
386                    &mul.right,
387                    line_file.clone(),
388                    verify_state,
389                )? {
390                    Some("mul_not_equal_zero_both_factors_strictly_negative")
391                } else {
392                    None
393                }
394            }
395            Obj::Sub(sub) => {
396                if self.sub_difference_nonzero_when_operands_have_strict_opposite_sign_by_non_equational_verify(
397                    &sub.left,
398                    &sub.right,
399                    line_file,
400                    verify_state,
401                )? {
402                    Some("sub_not_equal_zero_operands_strict_opposite_sign")
403                } else {
404                    None
405                }
406            }
407            other => {
408                let zero_obj: Obj = Number::new("0".to_string()).into();
409                let zero_lt_a = LessFact::new(
410                    zero_obj.clone(),
411                    other.clone(),
412                    line_file.clone(),
413                ).into();
414
415                let final_round_verify_state = verify_state.make_final_round_state();
416
417                if self.non_equational_atomic_fact_holds_by_full_verify_pipeline(
418                    &zero_lt_a,
419                    &final_round_verify_state,
420                )? {
421                    Some("not_equal_zero_operand_strictly_positive")
422                } else {
423                    let a_lt_0 = LessFact::new(
424                        other.clone(),
425                        zero_obj,
426                        line_file.clone(),
427                    ).into();
428                    if self.non_equational_atomic_fact_holds_by_full_verify_pipeline(
429                        &a_lt_0,
430                        &final_round_verify_state,
431                    )? {
432                        Some("not_equal_zero_operand_strictly_negative")
433                    } else {
434                        None
435                    }
436                }
437            }
438        };
439
440        match builtin_rule_label {
441            Some(rule_label) => Ok(Some(
442                (FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
443                    not_equal_fact.clone().into(),
444                    rule_label.to_string(),
445                    Vec::new(),
446                ))
447                .into(),
448            )),
449            None => Ok(None),
450        }
451    }
452}