use std::collections::HashMap;
use crate::domain::EvaluationDomain;
type ExternalFn<T> = Box<dyn Fn(&[T]) -> T + Send + Sync>;
pub struct FunctionMap<T: EvaluationDomain> {
entries: Vec<(String, FunctionEntry<T>)>,
name_to_idx: HashMap<String, usize>,
aliases: HashMap<String, String>,
}
pub struct FunctionEntry<T: EvaluationDomain> {
pub arity: usize,
func: ExternalFn<T>,
}
impl<T: EvaluationDomain> FunctionMap<T> {
pub fn new() -> Self {
Self {
entries: Vec::new(),
name_to_idx: HashMap::new(),
aliases: HashMap::new(),
}
}
pub fn register(&mut self, name: &str, arity: usize, func: ExternalFn<T>) {
let idx = self.entries.len();
self.entries
.push((name.to_string(), FunctionEntry { arity, func }));
self.name_to_idx.insert(name.to_string(), idx);
}
pub fn register_alias(&mut self, alias: &str, canonical: &str) {
self.aliases
.insert(alias.to_string(), canonical.to_string());
}
pub fn resolve(&self, name: &str) -> Option<&FunctionEntry<T>> {
self.resolve_idx(name).map(|idx| &self.entries[idx].1)
}
pub fn index_of(&self, name: &str) -> Option<usize> {
self.resolve_idx(name)
}
fn resolve_idx(&self, name: &str) -> Option<usize> {
if let Some(idx) = self.name_to_idx.get(name) {
return Some(*idx);
}
let lower = name.to_lowercase();
if let Some(idx) = self.name_to_idx.get(&lower) {
return Some(*idx);
}
if let Some(canonical) = self.aliases.get(name) {
return self.name_to_idx.get(canonical.as_str()).copied();
}
if let Some(canonical) = self.aliases.get(&lower) {
return self.name_to_idx.get(canonical.as_str()).copied();
}
None
}
pub fn call_by_index(&self, idx: usize, args: &[T]) -> Option<T> {
self.entries.get(idx).map(|(_, entry)| (entry.func)(args))
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
}
impl<T: EvaluationDomain> Default for FunctionMap<T> {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn register_and_resolve() {
let mut map = FunctionMap::<f64>::new();
map.register("square", 1, Box::new(|args| args[0] * args[0]));
assert!(map.resolve("square").is_some());
assert!(map.resolve("Square").is_some());
assert!(map.resolve("unknown").is_none());
}
#[test]
fn alias_resolution() {
let mut map = FunctionMap::<f64>::new();
map.register("log", 1, Box::new(|args| args[0].ln()));
map.register_alias("ln", "log");
assert!(map.resolve("ln").is_some());
assert!(map.resolve("Ln").is_some());
}
#[test]
fn call_by_index() {
let mut map = FunctionMap::<f64>::new();
map.register("square", 1, Box::new(|args| args[0] * args[0]));
let result = map.call_by_index(0, &[3.0]).unwrap();
assert!((result - 9.0).abs() < 1e-10);
}
#[test]
fn empty_map() {
let map = FunctionMap::<f64>::new();
assert!(map.is_empty());
assert_eq!(map.len(), 0);
}
}