1use crate::{Arity, Eval, Factory, NodeValue, TreeNode, ops::Param};
2use std::{
3 fmt::{Debug, Display},
4 hash::Hash,
5};
6
7pub enum Op<T> {
21 Fn(&'static str, Arity, fn(&[T]) -> T),
28 Var(&'static str, usize, Option<usize>),
35 Const(&'static str, T),
41 Value(&'static str, Arity, Param<T>, fn(&[T], &T) -> T),
52 Pair(&'static str, Arity, Param<(T, T)>, fn(&[T], &(T, T)) -> T),
63}
64
65impl<T> Op<T> {
66 pub fn name(&self) -> &str {
67 match self {
68 Op::Fn(name, _, _) => name,
69 Op::Var(name, _, _) => name,
70 Op::Const(name, _) => name,
71 Op::Value(name, _, _, _) => name,
72 Op::Pair(name, _, _, _) => name,
73 }
74 }
75
76 pub fn arity(&self) -> Arity {
77 match self {
78 Op::Fn(_, arity, _) => *arity,
79 Op::Var(_, _, _) => Arity::Zero,
80 Op::Const(_, _) => Arity::Zero,
81 Op::Value(_, arity, _, _) => *arity,
82 Op::Pair(_, arity, _, _) => *arity,
83 }
84 }
85
86 pub fn is_fn(&self) -> bool {
87 matches!(self, Op::Fn(_, _, _))
88 }
89
90 pub fn is_var(&self) -> bool {
91 matches!(self, Op::Var(_, _, _))
92 }
93
94 pub fn is_const(&self) -> bool {
95 matches!(self, Op::Const(_, _))
96 }
97
98 pub fn is_value(&self) -> bool {
99 matches!(self, Op::Value(_, _, _, _))
100 }
101}
102
103impl<T> Eval<[T], T> for Op<T>
104where
105 T: Clone,
106{
107 fn eval(&self, inputs: &[T]) -> T {
108 match self {
109 Op::Fn(_, _, op) => op(inputs),
110 Op::Var(_, index, _) => inputs[*index].clone(),
111 Op::Const(_, value) => value.clone(),
112 Op::Value(_, _, value, operation) => operation(inputs, value.data()),
113 Op::Pair(_, _, value, operation) => operation(inputs, value.data()),
114 }
115 }
116}
117
118impl<T> Factory<(), Op<T>> for Op<T>
119where
120 T: Clone,
121{
122 fn new_instance(&self, _: ()) -> Op<T> {
123 match self {
124 Op::Fn(name, arity, op) => Op::Fn(name, *arity, *op),
125 Op::Var(name, index, domain) => Op::Var(name, *index, *domain),
126 Op::Const(name, value) => Op::Const(name, value.clone()),
127 Op::Value(name, arity, value, operation) => {
128 Op::Value(name, *arity, value.new_instance(()), *operation)
129 }
130 Op::Pair(name, arity, value, operation) => {
131 Op::Pair(name, *arity, value.new_instance(()), *operation)
132 }
133 }
134 }
135}
136
137impl<T> Clone for Op<T>
138where
139 T: Clone,
140{
141 fn clone(&self) -> Self {
142 match self {
143 Op::Fn(name, arity, op) => Op::Fn(name, *arity, *op),
144 Op::Var(name, index, domain) => Op::Var(name, *index, *domain),
145 Op::Const(name, value) => Op::Const(name, value.clone()),
146 Op::Value(name, arity, value, operation) => {
147 Op::Value(name, *arity, value.clone(), *operation)
148 }
149 Op::Pair(name, arity, value, operation) => {
150 Op::Pair(name, *arity, value.clone(), *operation)
151 }
152 }
153 }
154}
155
156impl<T> PartialEq for Op<T>
157where
158 T: PartialEq,
159{
160 fn eq(&self, other: &Self) -> bool {
161 self.name() == other.name()
162 && self.arity() == other.arity()
163 && match (self, other) {
164 (Op::Fn(_, _, _), Op::Fn(_, _, _)) => true,
165 (Op::Var(_, idx_a, card_a), Op::Var(_, idx_b, card_b)) => {
166 idx_a == idx_b && card_a == card_b
167 }
168 (Op::Const(_, val_a), Op::Const(_, val_b)) => val_a == val_b,
169 (Op::Value(_, _, val_a, _), Op::Value(_, _, val_b, _)) => val_a == val_b,
170 (Op::Pair(_, _, val_a, _), Op::Pair(_, _, val_b, _)) => val_a == val_b,
171 _ => false,
172 }
173 }
174}
175
176impl Hash for Op<f32> {
177 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
178 self.name().hash(state);
179 self.arity().hash(state);
180 match self {
181 Op::Fn(_, _, op) => {
182 let op_ptr = *op as usize;
183 op_ptr.hash(state);
184 }
185 Op::Var(_, index, domain) => {
186 index.hash(state);
187 domain.hash(state);
188 }
189 Op::Const(_, value) => {
190 value.to_bits().hash(state);
191 }
192 Op::Value(_, _, value, operation) => {
193 (*value).data().to_bits().hash(state);
194 let op_ptr = *operation as usize;
195 op_ptr.hash(state);
196 }
197 Op::Pair(_, _, value, operation) => {
198 let data = (*value).data();
199 let b_one = data.0.to_bits();
200 let b_two = data.1.to_bits();
201 b_one.hash(state);
202 b_two.hash(state);
203 let op_ptr = *operation as usize;
204 op_ptr.hash(state);
205 }
206 }
207 }
208}
209
210impl<T> Display for Op<T> {
211 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
212 write!(f, "{}", self.name())
213 }
214}
215
216impl<T> Default for Op<T>
217where
218 T: Default,
219{
220 fn default() -> Self {
221 Op::Fn("default", Arity::Zero, |_: &[T]| T::default())
222 }
223}
224
225impl<T> Debug for Op<T>
226where
227 T: Debug,
228{
229 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
230 match self {
231 Op::Fn(name, _, _) => write!(f, "Fn: {}", name),
232 Op::Var(name, index, card) => match card {
233 Some(k) => write!(f, "Var: {}({},{})", name, index, k),
234 None => write!(f, "Var: {}({})", name, index),
235 },
236 Op::Const(name, value) => match f.precision() {
237 Some(p) => write!(f, "Con: {}({:.*?})", name, p, value),
238 None => write!(f, "Con: {}({:?})", name, value),
239 },
240 Op::Value(name, _, value, _) => match f.precision() {
241 Some(p) => write!(f, "Val: {}({:.*?})", name, p, value),
242 None => write!(f, "Val: {}({:?})", name, value),
243 },
244 Op::Pair(name, _, value, _) => match f.precision() {
245 Some(p) => write!(f, "Pair: {}{:.*?}", name, p, value),
246 None => write!(f, "Pair: {}{:?}", name, value),
247 },
248 }
249 }
250}
251
252impl<T: Clone> From<Op<T>> for NodeValue<Op<T>> {
253 fn from(value: Op<T>) -> Self {
254 let arity = value.arity();
255 NodeValue::Bounded(value, arity)
256 }
257}
258
259impl<T> From<Op<T>> for TreeNode<Op<T>> {
260 fn from(value: Op<T>) -> Self {
261 let arity = value.arity();
262 TreeNode::with_arity(value, arity)
263 }
264}
265
266impl<T> From<Op<T>> for Vec<TreeNode<Op<T>>> {
267 fn from(value: Op<T>) -> Self {
268 vec![TreeNode::from(value)]
269 }
270}
271
272#[cfg(test)]
273mod test {
274 use super::*;
275
276 #[test]
277 fn test_ops() {
278 let op = Op::add();
279 assert_eq!(op.name(), "add");
280 assert_eq!(op.arity(), Arity::Exact(2));
281 assert_eq!(op.eval(&[1_f32, 2_f32]), 3_f32);
282 assert_eq!(op.new_instance(()), op);
283 }
284
285 #[test]
286 fn test_op_clone() {
287 let op = Op::add();
288 let op2 = op.clone();
289
290 let result = op.eval(&[1_f32, 2_f32]);
291 let result2 = op2.eval(&[1_f32, 2_f32]);
292
293 assert_eq!(op, op2);
294 assert_eq!(result, result2);
295 }
296}