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);