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