use std::str::FromStr;
use itertools::Itertools;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "snake_case")]
pub enum Operation {
Sqrt,
Abs,
Floor,
Ceil,
Round,
Exp,
Ln,
Log2,
Log10,
Log,
Sin,
Cos,
Tan,
Asin,
Acos,
Atan,
Atan2,
Min,
Max,
Pow,
}
impl Operation {
pub const ALL: [Self; 20] = [
Self::Sqrt,
Self::Abs,
Self::Floor,
Self::Ceil,
Self::Round,
Self::Exp,
Self::Ln,
Self::Log2,
Self::Log10,
Self::Log,
Self::Sin,
Self::Cos,
Self::Tan,
Self::Asin,
Self::Acos,
Self::Atan,
Self::Atan2,
Self::Min,
Self::Max,
Self::Pow,
];
pub fn apply(&self, args: &[f64]) -> Result<f64, String> {
match (self, args) {
(Operation::Sqrt, [x]) => Ok(x.sqrt()),
(Operation::Abs, [x]) => Ok(x.abs()),
(Operation::Floor, [x]) => Ok(x.floor()),
(Operation::Ceil, [x]) => Ok(x.ceil()),
(Operation::Round, [x]) => Ok(x.round()),
(Operation::Exp, [x]) => Ok(x.exp()),
(Operation::Ln, [x]) => Ok(x.ln()),
(Operation::Log2, [x]) => Ok(x.log2()),
(Operation::Log10, [x]) => Ok(x.log10()),
(Operation::Log, [b, x]) => Ok(x.log(*b)),
(Operation::Sin, [x]) => Ok(x.sin()),
(Operation::Cos, [x]) => Ok(x.cos()),
(Operation::Tan, [x]) => Ok(x.tan()),
(Operation::Asin, [x]) => Ok(x.asin()),
(Operation::Acos, [x]) => Ok(x.acos()),
(Operation::Atan, [x]) => Ok(x.atan()),
(Operation::Atan2, [y, x]) => Ok(y.atan2(*x)),
(Operation::Min, [a, b]) => Ok(a.min(*b)),
(Operation::Max, [a, b]) => Ok(a.max(*b)),
(Operation::Pow, [b, e]) => Ok(b.powf(*e)),
_ => Err(format!(
"wrong number of arguments for '{}' found {:?}",
self.describe(),
args
)),
}
}
pub fn describe(&self) -> String {
let op = self.to_string();
let args = match self {
Self::Log => "(b, x)",
Self::Atan2 => "(y, x)",
Self::Min | Self::Max => "(a, b)",
Self::Pow => "(b, e)",
_ => "(x)",
};
format!("{op}{args}")
}
}
impl std::fmt::Display for Operation {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = match self {
Self::Sqrt => "sqrt",
Self::Abs => "abs",
Self::Floor => "floor",
Self::Ceil => "ceil",
Self::Round => "round",
Self::Exp => "exp",
Self::Ln => "ln",
Self::Log2 => "log2",
Self::Log10 => "log10",
Self::Log => "log",
Self::Sin => "sin",
Self::Cos => "cos",
Self::Tan => "tan",
Self::Asin => "asin",
Self::Acos => "acos",
Self::Atan => "atan",
Self::Atan2 => "atan2",
Self::Min => "min",
Self::Max => "max",
Self::Pow => "pow",
};
write!(f, "{s}")
}
}
impl FromStr for Operation {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"sqrt" => Ok(Self::Sqrt),
"abs" => Ok(Self::Abs),
"floor" => Ok(Self::Floor),
"ceil" => Ok(Self::Ceil),
"round" => Ok(Self::Round),
"exp" => Ok(Self::Exp),
"ln" => Ok(Self::Ln),
"log2" => Ok(Self::Log2),
"log10" => Ok(Self::Log10),
"log" => Ok(Self::Log),
"sin" => Ok(Self::Sin),
"cos" => Ok(Self::Cos),
"tan" => Ok(Self::Tan),
"asin" => Ok(Self::Asin),
"acos" => Ok(Self::Acos),
"atan" => Ok(Self::Atan),
"atan2" => Ok(Self::Atan2),
"min" => Ok(Self::Min),
"max" => Ok(Self::Max),
"pow" => Ok(Self::Pow),
_ => {
let all_str = Self::ALL.iter().map(|s| s.to_string()).join(",");
Err(format!("unknown operation {s}; supported ops: [{all_str}]"))
}
}
}
}