Skip to main content

litex/verify/
verify_or_fact.rs

1use crate::prelude::*;
2use crate::verify::verify_equality_by_builtin_rules::objs_equal_by_display_string;
3use std::result::Result;
4
5/// Two atomic facts of the form `s > t` / `s <= t` (or `<` / `>=`) with the same left and right
6/// operands; the disjunction is a trivial order split (totally ordered carriers such as `R`).
7fn 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
122// Remainder by a positive integer covers exactly one residue class.
123// If m > 1, `x % m = 0 or x % m = 1 or ... or x % m = m - 1` is exhaustive.
124// Example: `forall x Z: x % 2 = 0 or x % 2 = 1`.
125fn 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
188impl Runtime {
189    pub fn verify_or_fact(
190        &mut self,
191        or_fact: &OrFact,
192        verify_state: &VerifyState,
193    ) -> Result<StmtResult, RuntimeError> {
194        if let Some(cached_result) =
195            self.verify_fact_from_cache_using_display_string(&or_fact.clone().into())
196        {
197            return Ok(cached_result);
198        }
199
200        if !verify_state.well_defined_already_verified {
201            if let Err(e) = self.verify_or_fact_well_defined(or_fact, verify_state) {
202                return Err({
203                    VerifyRuntimeError(RuntimeErrorStruct::new(
204                        Some(Fact::from(or_fact.clone()).into_stmt()),
205                        String::new(),
206                        or_fact.line_file.clone(),
207                        Some(e),
208                        vec![],
209                    ))
210                    .into()
211                });
212            }
213        }
214
215        let verify_state_for_children = verify_state.make_state_with_req_ok_set_to_true();
216
217        if or_fact.facts.len() == 2 {
218            if let (
219                AndChainAtomicFact::AtomicFact(first_atomic),
220                AndChainAtomicFact::AtomicFact(second_atomic),
221            ) = (&or_fact.facts[0], &or_fact.facts[1])
222            {
223                if first_atomic.make_reversed().to_string() == second_atomic.to_string() {
224                    return Ok(
225                        (FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
226                            or_fact.clone().into(),
227                            "or: complementary atomic facts (make_reversed first equals second)"
228                                .to_string(),
229                            Vec::new(),
230                        ))
231                        .into(),
232                    );
233                }
234                if order_split_or_is_exhaustive_pair(first_atomic, second_atomic)
235                    || order_split_or_is_exhaustive_pair(second_atomic, first_atomic)
236                {
237                    return Ok(
238                        (FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
239                            or_fact.clone().into(),
240                            "or: complementary order relations (strict vs non-strict) on the same terms"
241                                .to_string(),
242                            Vec::new(),
243                        ))
244                        .into(),
245                    );
246                }
247                if abs_sign_split_or_is_exhaustive_pair(first_atomic, second_atomic)
248                    || abs_sign_split_or_is_exhaustive_pair(second_atomic, first_atomic)
249                {
250                    return Ok(
251                        (FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
252                            or_fact.clone().into(),
253                            "or: abs(x) is x or -x".to_string(),
254                            Vec::new(),
255                        ))
256                        .into(),
257                    );
258                }
259            }
260        }
261
262        if mod_positive_integer_residue_or_is_exhaustive(or_fact) {
263            return Ok(
264                (FactualStmtSuccess::new_with_verified_by_builtin_rules_recording_stmt(
265                    or_fact.clone().into(),
266                    "or: complete residue classes modulo a positive integer".to_string(),
267                    Vec::new(),
268                ))
269                .into(),
270            );
271        }
272
273        for fact in or_fact.facts.iter() {
274            let result = self.verify_and_chain_atomic_fact(fact, &verify_state_for_children)?;
275            if result.is_true() {
276                return Ok((FactualStmtSuccess::new_with_verified_by_known_fact(
277                    or_fact.clone().into(),
278                    VerifiedByResult::wrap_bys(vec![VerifiedByResult::Fact(
279                        fact.clone().into(),
280                        fact.to_string(),
281                    )]),
282                    Vec::new(),
283                ))
284                .into());
285            }
286        }
287
288        let result = self.verify_or_fact_with_known_or_facts(or_fact)?;
289        if result.is_true() {
290            return Ok(result);
291        }
292
293        let result = self.verify_or_fact_with_known_forall(or_fact, verify_state)?;
294        if result.is_true() {
295            return Ok(result);
296        }
297
298        Ok((StmtUnknown::new()).into())
299    }
300
301    pub fn verify_or_fact_with_known_or_facts(
302        &mut self,
303        or_fact: &OrFact,
304    ) -> Result<StmtResult, RuntimeError> {
305        let args_in_or_fact = or_fact.get_args_from_fact();
306        let mut all_objs_equal_to_each_arg: Vec<Vec<String>> = Vec::new();
307        for arg in args_in_or_fact.iter() {
308            let mut all_objs_equal_to_current_arg =
309                self.get_all_objs_equal_to_given(&arg.to_string());
310            if all_objs_equal_to_current_arg.is_empty() {
311                all_objs_equal_to_current_arg.push(arg.to_string());
312            }
313            all_objs_equal_to_each_arg.push(all_objs_equal_to_current_arg);
314        }
315
316        for environment in self.iter_environments_from_top() {
317            let result = Self::verify_or_fact_with_known_or_facts_with_facts_in_environment(
318                environment,
319                or_fact,
320                &all_objs_equal_to_each_arg,
321            )?;
322            if result.is_true() {
323                return Ok(result);
324            }
325        }
326
327        Ok((StmtUnknown::new()).into())
328    }
329
330    pub fn verify_or_fact_with_known_or_facts_with_facts_in_environment(
331        environment: &Environment,
332        or_fact: &OrFact,
333        all_objs_equal_to_each_arg: &Vec<Vec<String>>,
334    ) -> Result<StmtResult, RuntimeError> {
335        if let Some(known_or_facts) = environment.known_or_facts.get(&or_fact.key()) {
336            for known_or_fact in known_or_facts.iter() {
337                let result = Self::_verify_or_fact_the_same_type_and_return_matched_args(
338                    known_or_fact,
339                    or_fact,
340                )?;
341                let mut all_args_match = true;
342                if let Some(matched_args) = result {
343                    for (index, matched_arg) in matched_args.iter().enumerate() {
344                        let known_arg_string = matched_arg.0.to_string();
345
346                        if known_arg_string != matched_arg.1.to_string()
347                            && !all_objs_equal_to_each_arg[index].contains(&known_arg_string)
348                        {
349                            all_args_match = false;
350                            break;
351                        }
352                    }
353                }
354
355                if all_args_match {
356                    return Ok((FactualStmtSuccess::new_with_verified_by_known_fact(
357                        or_fact.clone().into(),
358                        VerifiedByResult::wrap_bys(vec![VerifiedByResult::Fact(
359                            known_or_fact.clone().into(),
360                            known_or_fact.to_string(),
361                        )]),
362                        Vec::new(),
363                    ))
364                    .into());
365                }
366            }
367        }
368
369        return Ok((StmtUnknown::new()).into());
370    }
371}