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