synthphonia/expr/ops/
base.rs

1use std::cmp::min;
2
3use derive_more::DebugCustom;
4use crate::{forward::enumeration::Enumerator3, galloc::{AllocForStr, AllocForExactSizeIter}, impl_op3, new_op2, new_op3, parser::config::Config, value::Value};
5use itertools::izip;
6
7use super::Op3;
8
9crate::impl_basic!(Eq,"=");
10impl crate::forward::enumeration::Enumerator2 for Eq{}
11
12impl crate::expr::ops::Op2 for Eq {
13    fn cost(&self) -> usize {
14        self.0
15    }
16    fn try_eval(&self, a1: Value, a2: Value) -> (bool, Value) {
17        match (a1, a2) {
18            (Value::Int(s1), Value::Int(s2)) => (
19                true,
20                Value::Bool(
21                    itertools::izip!(s1.iter(), s2.iter())
22                        .map(|(s1, s2)| s1 == s2)
23                        .galloc_scollect(),
24                ),
25            ),
26            (Value::Str(s1), Value::Str(s2)) => (
27                true,
28                Value::Bool(
29                    itertools::izip!(s1.iter(), s2.iter())
30                        .map(|(s1, s2)| s1 == s2)
31                        .galloc_scollect(),
32                ),
33            ),
34            (Value::BitVector(_, s1), Value::BitVector(_, s2)) => (
35                true,
36                Value::Bool(
37                    itertools::izip!(s1.iter(), s2.iter())
38                        .map(|(s1, s2)| s1 == s2)
39                        .galloc_scollect(),
40                ),
41            ),
42            _ => (false, Value::Null),
43        }
44    }
45}
46
47#[derive(Debug,Clone,Copy,PartialEq,Eq,Hash)]
48/// A structured data representation used to denote a conditional expression with two components. 
49/// 
50/// The first component is a `usize`, which can be used to reference an index or identifier within a specific context. 
51/// The second component is a `bool`, which typically serves as a condition to determine the control flow within an algorithm. 
52/// This structure could be utilized in scenarios where conditional logic plays a role in decision-making processes, enabling elegant integration with indexed data or scenarios.
53/// 
54pub struct Ite(pub usize, pub bool);
55
56impl Ite {
57    /// Creates an instance of `Ite` from a provided `Config` object. 
58    /// 
59    /// It initializes the `Ite` with a `usize` value extracted from the configuration under the key `"cost"`, defaulting to `1` if not present, and a `bool` value under the key `"enum"`, defaulting to `false`. 
60    /// This method allows the flexible creation of `Ite` structures based on configuration settings, facilitating custom behavior in synthesis tasks where conditional logic representations are used.
61    /// 
62    pub fn from_config(config: &Config) -> Self {
63        Self(config.get_usize("cost").unwrap_or(1), config.get_bool("enum").unwrap_or(false))
64    }
65    /// The method returns the static string "ite", which signifies the name of the operation represented by the struct. 
66    /// This simple function provides a way to retrieve the identifier for instances of the struct, likely correlating with its function or behavior within the string synthesis framework.
67    pub fn name() ->  &'static str {
68        "ite"
69    }
70}
71impl std::fmt::Display for Ite {
72    /// Formats the `Ite` type for display. 
73    /// 
74    /// The method implements custom formatting logic by invoking the `name` method of the `Ite` type and formatting its result. 
75    /// This enables `Ite` instances to be displayed as strings according to their name representation, integrating seamlessly with Rust's formatting mechanisms.
76    /// 
77    fn fmt(&self,f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { Self::name().fmt(f) }
78}
79impl Default for Ite {
80    /// Provides a method that constructs a default instance. 
81    /// 
82    /// This method utilizes another function, `from_config`, by supplying it with a default configuration, retrieved from Rust's standard `Default` trait. 
83    /// This default instance serves as a baseline configuration, which can then be adjusted as necessary for specific string synthesis tasks. 
84    /// The implementation underlines its utility by abstracting away the initial setup process, promoting a more streamlined use of the type within the module.
85    /// 
86    fn default() -> Self { Self::from_config(&Default::default()) }
87}
88impl Enumerator3 for Ite {
89    /// Enumerates possible expressions using a three-way conditional operator based on available execution data and constraints. 
90    /// 
91    /// This method first checks a flag and ensures there is sufficient size in the `Executor` before proceeding. 
92    /// It iterates over all combinations of expressions and values from specified non-terminal indices, `nt`, ensuring that their combined size is within allowable limits determined by a threshold based on the current execution size minus the expression's cost. 
93    /// For each valid combination, it constructs a ternary expression using the provided `Op3Enum` instance as the operator and evaluates it. 
94    /// When evaluation succeeds, the expression and its resultant value are processed further via the `Executor`. 
95    /// The method returns a `Result<(), ()>` to indicate completion or failure without a specific error.
96    /// 
97    fn enumerate(&self, this: &'static super::Op3Enum, exec: &'static crate::forward::executor::Executor, nt: [usize; 3]) -> Result<(), ()> {
98        if !self.1 { return Ok(())}
99        if exec.size() < self.cost() { return Ok(()); }
100        let total = exec.size() - self.cost();
101        for (i, (e1, v1)) in exec.data[nt[0]].size.get_all_under(total) {
102            for (j, (e2, v2)) in exec.data[nt[1]].size.get_all_under(total - i) {
103                for (_, (e3, v3)) in exec.data[nt[2]].size.get_all_under(total - i - j) {
104                    let expr = super::Expr::Op3(this, e1, e2, e3);
105                    if let (true, value) = self.try_eval(*v1, *v2, *v3) {
106                        exec.enum_expr(expr, value)?;
107                    }
108                }
109            } 
110        }
111        Ok(())
112    }
113}
114
115impl_op3!(Ite, "ite",
116    (Bool, Int, Int) -> Int { |(s1, s2, s3)| {
117        if *s1 {*s2} else {*s3}
118    }},
119    (Bool, Str, Str) -> Str { |(s1, s2, s3)| {
120        if *s1 {*s2} else {*s3}
121    }},
122    (Bool, Bool, Bool) -> Bool { |(s1, s2, s3)| {
123        if *s1 {*s2} else {*s3}
124    }},
125    (Bool, Float, Float) -> Float { |(s1, s2, s3)| {
126        if *s1 {*s2} else {*s3}
127    }}
128);