Skip to main content

litex/execute/
exec_have_fn_by_induc.rs

1use crate::prelude::*;
2use crate::stmt::definition_stmt::induc_obj_plus_offset;
3
4impl Runtime {
5    pub fn exec_have_fn_by_induc(
6        &mut self,
7        stmt: &HaveFnByInducStmt,
8    ) -> Result<StmtResult, RuntimeError> {
9        self.run_in_local_env(|rt| rt.exec_have_fn_by_induc_verify_process(stmt))?;
10
11        let infer_result = self.exec_have_fn_by_induc_store_process(stmt)?;
12
13        Ok((NonFactualStmtSuccess::new(stmt.clone().into(), infer_result, vec![])).into())
14    }
15
16    fn exec_have_fn_by_induc_verify_process(
17        &mut self,
18        stmt: &HaveFnByInducStmt,
19    ) -> Result<(), RuntimeError> {
20        for special_case_equal_to in stmt.special_cases_equal_tos.iter() {
21            self.verify_obj_well_defined_and_store_cache(
22                special_case_equal_to,
23                &VerifyState::new(0, false),
24            )?;
25        }
26
27        self.run_in_local_env(|rt| rt.exec_have_fn_by_induc_verify_last_case(stmt))?;
28
29        Ok(())
30    }
31
32    fn have_fn_by_induc_err(stmt: &HaveFnByInducStmt, cause: RuntimeError) -> RuntimeError {
33        exec_stmt_error_with_stmt_and_cause(stmt.clone().into(), cause)
34    }
35
36    // have fn by induc from 0: f(x Z: x >= 0) R: case 0: … case 1: …
37    // In the induction step, recursive calls may use any already defined value: from <= arg < x.
38    fn have_fn_by_induc_verify_last_case_register_fn(
39        &mut self,
40        stmt: &HaveFnByInducStmt,
41        param_name: &str,
42    ) -> Result<(), RuntimeError> {
43        self.store_free_param_or_identifier_name(&stmt.name, ParamObjType::Identifier)?;
44
45        let random_param = self.generate_random_unused_name();
46
47        let induc_outer_param =
48            obj_for_bound_param_in_scope(param_name.to_string(), ParamObjType::Induc);
49        let dom_facts: Vec<OrAndChainAtomicFact> = vec![
50            GreaterEqualFact::new(
51                obj_for_bound_param_in_scope(random_param.clone(), ParamObjType::FnSet),
52                stmt.induc_from.clone(),
53                stmt.line_file.clone(),
54            )
55            .into(),
56            LessFact::new(
57                obj_for_bound_param_in_scope(random_param.clone(), ParamObjType::FnSet),
58                induc_outer_param,
59                stmt.line_file.clone(),
60            )
61            .into(),
62        ];
63
64        let fn_set = self.new_fn_set(
65            vec![ParamGroupWithSet::new(
66                vec![random_param.clone()],
67                StandardSet::Z.into(),
68            )],
69            dom_facts,
70            stmt.ret_set.clone(),
71        )?;
72
73        let function_in_function_set_fact: Fact = InFact::new(
74            Identifier::new(stmt.name.clone()).into(),
75            fn_set.into(),
76            stmt.line_file.clone(),
77        )
78        .into();
79
80        self.verify_well_defined_and_store_and_infer_with_default_verify_state(
81            function_in_function_set_fact,
82        )
83        .map_err(|e| Self::have_fn_by_induc_err(stmt, e))?;
84
85        Ok(())
86    }
87
88    fn have_fn_by_induc_verify_one_equal_to_well_defined(
89        &mut self,
90        stmt: &HaveFnByInducStmt,
91        equal_to: &Obj,
92        verify_state: &VerifyState,
93    ) -> Result<(), RuntimeError> {
94        self.verify_obj_well_defined_and_store_cache(equal_to, verify_state)
95            .map_err(|e| Self::have_fn_by_induc_err(stmt, e))?;
96
97        let equal_to_in_ret_set_atomic_fact: AtomicFact = InFact::new(
98            equal_to.clone(),
99            stmt.ret_set.clone(),
100            stmt.line_file.clone(),
101        )
102        .into();
103        let verify_result = self
104            .verify_atomic_fact(&equal_to_in_ret_set_atomic_fact, verify_state)
105            .map_err(|e| Self::have_fn_by_induc_err(stmt, e))?;
106        if verify_result.is_unknown() {
107            return Err(short_exec_error(
108                stmt.clone().into(),
109                format!(
110                    "have_fn_by_induc: {} is not in return set {}",
111                    equal_to, stmt.ret_set
112                ),
113                None,
114                vec![],
115            ));
116        }
117        Ok(())
118    }
119
120    // have fn by induc from 0: f(x Z: x >= 0) R:
121    // case 0: 1
122    // case 1: 2
123    // case >= 2:
124    //      case x % 2 = 0: f(x - 1)
125    //      case x % 2 = 2: f(x - 1) + f(x - 2)
126    fn exec_have_fn_by_induc_verify_last_case(
127        &mut self,
128        stmt: &HaveFnByInducStmt,
129    ) -> Result<(), RuntimeError> {
130        let verify_state = VerifyState::new(0, false);
131        let n = stmt.special_cases_equal_tos.len();
132        let line_file = stmt.line_file.clone();
133
134        let param_name_str = stmt.param.clone();
135
136        let left_id: Obj =
137            obj_for_bound_param_in_scope(param_name_str.clone(), ParamObjType::Induc);
138
139        self.store_free_param_or_identifier_name(&param_name_str, ParamObjType::Induc)?;
140
141        self.define_parameter_by_binding_param_type(
142            &param_name_str,
143            &ParamType::Obj(StandardSet::Z.into()),
144            ParamObjType::Induc,
145        )?;
146
147        let param_larger_than_induc_plus_offset: AndChainAtomicFact = GreaterEqualFact::new(
148            left_id,
149            induc_obj_plus_offset(&stmt.induc_from, n),
150            line_file.clone(),
151        )
152        .into();
153
154        self.verify_well_defined_and_store_and_infer_with_default_verify_state(
155            param_larger_than_induc_plus_offset.into(),
156        )
157        .map_err(|e| Self::have_fn_by_induc_err(stmt, e))?;
158
159        // Induction step needs f(x-1)..f(x-n); cache alone skips fn membership, so store FnObj and in ret_set.
160        for i in 1..=n {
161            let arg: Obj = Sub::new(
162                obj_for_bound_param_in_scope(param_name_str.clone(), ParamObjType::Induc),
163                i.into(),
164            )
165            .into();
166            let fn_obj: Obj = FnObj::new(
167                FnObjHead::Identifier(Identifier::new(stmt.name.clone())),
168                vec![vec![Box::new(arg.clone())]],
169            )
170            .into();
171            self.store_well_defined_obj_cache(&fn_obj);
172            let fn_in_ret: Fact =
173                InFact::new(fn_obj, stmt.ret_set.clone(), line_file.clone()).into();
174            self.verify_well_defined_and_store_and_infer_with_default_verify_state(fn_in_ret)
175                .map_err(|e| Self::have_fn_by_induc_err(stmt, e))?;
176        }
177
178        self.have_fn_by_induc_verify_last_case_register_fn(stmt, &param_name_str)?;
179
180        match &stmt.last_case {
181            HaveFnByInducLastCase::EqualTo(eq) => {
182                self.have_fn_by_induc_verify_one_equal_to_well_defined(stmt, eq, &verify_state)?;
183            }
184            HaveFnByInducLastCase::NestedCases(last_pairs) if !last_pairs.is_empty() => {
185                let coverage_cases: Vec<AndChainAtomicFact> =
186                    last_pairs.iter().map(|c| c.case_fact.clone()).collect();
187                let coverage: Fact = OrFact::new(coverage_cases, line_file.clone()).into();
188                self.verify_fact_return_err_if_not_true(&coverage, &verify_state)
189                    .map_err(|e| {
190                        short_exec_error(
191                            stmt.clone().into(),
192                            "have_fn_by_induc: nested last cases do not cover all situations"
193                                .to_string(),
194                            Some(e),
195                            vec![],
196                        )
197                    })?;
198
199                for nested in last_pairs.iter() {
200                    self.run_in_local_env(|rt| {
201                        rt.verify_well_defined_and_store_and_infer_with_default_verify_state(
202                            nested.case_fact.clone().into(),
203                        )
204                        .map_err(|e| Self::have_fn_by_induc_err(stmt, e))?;
205                        rt.have_fn_by_induc_verify_one_equal_to_well_defined(
206                            stmt,
207                            &nested.equal_to,
208                            &verify_state,
209                        )
210                    })?;
211                }
212            }
213            HaveFnByInducLastCase::NestedCases(_) => {
214                return Err(short_exec_error(
215                    stmt.clone().into(),
216                    "have_fn_by_induc: nested last case list must not be empty".to_string(),
217                    None,
218                    vec![],
219                ));
220            }
221        }
222
223        Ok(())
224    }
225
226    // When store_*_and_infer yields an empty chain, record the stored fact for infer_facts display.
227    fn merge_store_infer_with_fallback_fact(
228        infer_result: &mut InferResult,
229        store_infer: InferResult,
230        fallback_fact: &Fact,
231    ) {
232        let empty = store_infer.is_empty();
233        infer_result.new_infer_result_inside(store_infer);
234        if empty {
235            infer_result.new_fact(fallback_fact);
236        }
237    }
238
239    fn exec_have_fn_by_induc_store_process(
240        &mut self,
241        stmt: &HaveFnByInducStmt,
242    ) -> Result<InferResult, RuntimeError> {
243        // stmt.induc_from 得是 Z
244        let in_fact: AtomicFact = InFact::new(
245            stmt.induc_from.clone(),
246            StandardSet::Z.into(),
247            stmt.line_file.clone(),
248        )
249        .into();
250        let verify_result = self
251            .verify_atomic_fact(&in_fact, &VerifyState::new(0, false))
252            .map_err(|e| Self::have_fn_by_induc_err(stmt, e))?;
253        if verify_result.is_unknown() {
254            return Err(short_exec_error(
255                stmt.clone().into(),
256                "have_fn_by_induc: induc_from is not in Z".to_string(),
257                None,
258                vec![],
259            ));
260        }
261
262        let mut infer_result = InferResult::new();
263        let fs = self.fn_set_from_fn_set_clause(&stmt.fn_user_fn_set_clause())?;
264
265        // Same order as `define_params_with_type`: declare the name before storing the typing fact,
266        // otherwise `verify_fact_well_defined` rejects `f $in ...` with "identifier not defined".
267        self.store_free_param_or_identifier_name(&stmt.name, ParamObjType::Identifier)?;
268
269        let bind_infer = self.define_parameter_by_binding_param_type(
270            &stmt.name,
271            &ParamType::Obj(fs.clone().into()),
272            ParamObjType::Identifier,
273        )?;
274        let bind_fact: Fact = InFact::new(
275            Identifier::new(stmt.name.clone()).into(),
276            fs.clone().into(),
277            stmt.line_file.clone(),
278        )
279        .into();
280        Self::merge_store_infer_with_fallback_fact(&mut infer_result, bind_infer, &bind_fact);
281
282        // Special cases: stmt.name(induc_from + i) = equal_to[i]
283        for i in 0..stmt.special_cases_equal_tos.len() {
284            let raw_arg = if i == 0 {
285                stmt.induc_from.clone()
286            } else {
287                Add::new(stmt.induc_from.clone(), i.into()).into()
288            };
289            // Fold numeric adds so we store f(1) not f(0 + 1)
290            let arg = raw_arg.replace_with_numeric_result_if_can_be_calculated().0;
291
292            let equal_to = &stmt.special_cases_equal_tos[i];
293
294            let fn_obj: Obj = FnObj::new(
295                FnObjHead::Identifier(Identifier::new(stmt.name.clone())),
296                vec![vec![Box::new(arg.clone())]],
297            )
298            .into();
299
300            let equal_fact: Fact =
301                EqualFact::new(fn_obj.clone(), equal_to.clone(), stmt.line_file.clone()).into();
302
303            let result = self
304                .verify_well_defined_and_store_and_infer_with_default_verify_state(
305                    equal_fact.clone(),
306                )
307                .map_err(|e| Self::have_fn_by_induc_err(stmt, e))?;
308
309            Self::merge_store_infer_with_fallback_fact(&mut infer_result, result, &equal_fact);
310        }
311
312        match &stmt.last_case {
313            HaveFnByInducLastCase::EqualTo(eq) => {
314                let param_name = stmt.param.clone();
315                let param_def = vec![ParamGroupWithParamType::new(
316                    vec![param_name.clone()],
317                    ParamType::Obj(StandardSet::Z.into()),
318                )];
319
320                let mut dom: Vec<Fact> = stmt.forall_fn_base_dom_exist_or_facts();
321
322                let induc_plus_n =
323                    induc_obj_plus_offset(&stmt.induc_from, stmt.special_cases_equal_tos.len())
324                        .replace_with_numeric_result_if_can_be_calculated()
325                        .0;
326                dom.push(
327                    GreaterEqualFact::new(
328                        obj_for_bound_param_in_scope(param_name.clone(), ParamObjType::Forall),
329                        induc_plus_n,
330                        stmt.line_file.clone(),
331                    )
332                    .into(),
333                );
334
335                let forall_fact_raw = ForallFact::new(
336                    ParamDefWithType::new(param_def),
337                    dom,
338                    vec![EqualFact::new(
339                        FnObj::new(
340                            FnObjHead::Identifier(Identifier::new(stmt.name.clone())),
341                            vec![vec![Box::new(obj_for_bound_param_in_scope(
342                                param_name.clone(),
343                                ParamObjType::Forall,
344                            ))]],
345                        )
346                        .into(),
347                        eq.clone(),
348                        stmt.line_file.clone(),
349                    )
350                    .into()],
351                    stmt.line_file.clone(),
352                )?;
353
354                let forall_fact = self
355                    .inst_have_fn_forall_fact_for_store(forall_fact_raw)
356                    .map_err(|e| Self::have_fn_by_induc_err(stmt, e))?;
357
358                let result = self
359                    .verify_well_defined_and_store_and_infer_with_default_verify_state(
360                        forall_fact.clone(),
361                    )
362                    .map_err(|e| Self::have_fn_by_induc_err(stmt, e))?;
363                Self::merge_store_infer_with_fallback_fact(&mut infer_result, result, &forall_fact);
364            }
365            HaveFnByInducLastCase::NestedCases(last_pairs) => {
366                for nested in last_pairs.iter() {
367                    let param_name = stmt.param.clone();
368                    let param_def = vec![ParamGroupWithParamType::new(
369                        vec![param_name.clone()],
370                        ParamType::Obj(StandardSet::Z.into()),
371                    )];
372                    let eq = nested.equal_to.clone();
373
374                    let mut dom: Vec<Fact> = stmt.forall_fn_base_dom_exist_or_facts();
375
376                    let induc_plus_n =
377                        induc_obj_plus_offset(&stmt.induc_from, stmt.special_cases_equal_tos.len())
378                            .replace_with_numeric_result_if_can_be_calculated()
379                            .0;
380                    dom.push(
381                        GreaterEqualFact::new(
382                            obj_for_bound_param_in_scope(param_name.clone(), ParamObjType::Forall),
383                            induc_plus_n,
384                            stmt.line_file.clone(),
385                        )
386                        .into(),
387                    );
388
389                    let c: AndChainAtomicFact = nested.case_fact.clone();
390                    dom.push(c.into());
391
392                    let forall_fact_raw = ForallFact::new(
393                        ParamDefWithType::new(param_def),
394                        dom,
395                        vec![EqualFact::new(
396                            FnObj::new(
397                                FnObjHead::Identifier(Identifier::new(stmt.name.clone())),
398                                vec![vec![Box::new(obj_for_bound_param_in_scope(
399                                    param_name.clone(),
400                                    ParamObjType::Forall,
401                                ))]],
402                            )
403                            .into(),
404                            eq.clone(),
405                            stmt.line_file.clone(),
406                        )
407                        .into()],
408                        stmt.line_file.clone(),
409                    )?;
410
411                    let forall_fact = self
412                        .inst_have_fn_forall_fact_for_store(forall_fact_raw)
413                        .map_err(|e| Self::have_fn_by_induc_err(stmt, e))?;
414
415                    let result = self
416                        .verify_well_defined_and_store_and_infer_with_default_verify_state(
417                            forall_fact.clone(),
418                        )
419                        .map_err(|e| Self::have_fn_by_induc_err(stmt, e))?;
420                    Self::merge_store_infer_with_fallback_fact(
421                        &mut infer_result,
422                        result,
423                        &forall_fact,
424                    );
425                }
426            }
427        }
428
429        Ok(infer_result)
430    }
431
432    pub fn exec_have_fn_by_induc_stmt(
433        &mut self,
434        stmt: &HaveFnByInducStmt,
435    ) -> Result<StmtResult, RuntimeError> {
436        self.exec_have_fn_by_induc(stmt)
437    }
438}