mathhook_core/algebra/simplification/
registry.rs

1//! Simplification Registry
2//!
3//! Global registry of simplification strategies for all functions.
4//! Provides O(1) lookup for function-specific algebraic rewrite rules.
5
6use super::elementary::{
7    AbsSimplificationStrategy, ExpSimplificationStrategy, SqrtSimplificationStrategy,
8};
9use super::logarithmic::{LogarithmSimplificationStrategy, NaturalLogSimplificationStrategy};
10use super::special::{FactorialSimplificationStrategy, GammaSimplificationStrategy};
11use super::strategy::SimplificationStrategy;
12use super::trigonometric::{
13    CosSimplificationStrategy, GenericTrigSimplificationStrategy, SinSimplificationStrategy,
14    TanSimplificationStrategy,
15};
16use crate::core::Expression;
17use once_cell::sync::Lazy;
18use std::collections::HashMap;
19
20/// Global simplification strategy registry
21///
22/// Lazy initialization ensures zero startup cost while providing
23/// universal access to simplification strategies.
24pub static SIMPLIFICATION_REGISTRY: Lazy<SimplificationRegistry> =
25    Lazy::new(SimplificationRegistry::new);
26
27/// Simplification registry for all functions
28///
29/// Stores and provides access to function-specific simplification strategies.
30/// Separate from FunctionProperties (which stores declarative mathematical data).
31pub struct SimplificationRegistry {
32    /// Function name → Simplification strategy mapping
33    strategies: HashMap<String, Box<dyn SimplificationStrategy>>,
34}
35
36impl Default for SimplificationRegistry {
37    fn default() -> Self {
38        Self::new()
39    }
40}
41
42impl SimplificationRegistry {
43    /// Create new simplification registry
44    ///
45    /// Initializes with all built-in simplification strategies.
46    pub fn new() -> Self {
47        let mut registry = Self {
48            strategies: HashMap::with_capacity(32),
49        };
50
51        registry.initialize_logarithmic_strategies();
52        registry.initialize_trigonometric_strategies();
53        registry.initialize_elementary_strategies();
54        registry.initialize_special_strategies();
55
56        registry
57    }
58
59    /// Register simplification strategy for function
60    ///
61    /// # Arguments
62    ///
63    /// * `name` - Function name (e.g., "log", "sin")
64    /// * `strategy` - Simplification strategy implementation
65    pub fn register(&mut self, name: &str, strategy: Box<dyn SimplificationStrategy>) {
66        self.strategies.insert(name.to_owned(), strategy);
67    }
68
69    /// Get simplification strategy for function
70    ///
71    /// # Arguments
72    ///
73    /// * `name` - Function name to look up
74    ///
75    /// # Returns
76    ///
77    /// Strategy if registered, None otherwise
78    #[inline(always)]
79    pub fn get_strategy(&self, name: &str) -> Option<&dyn SimplificationStrategy> {
80        self.strategies.get(name).map(|s| &**s)
81    }
82
83    /// Simplify function call using registered strategy
84    ///
85    /// # Arguments
86    ///
87    /// * `name` - Function name
88    /// * `args` - Function arguments
89    ///
90    /// # Returns
91    ///
92    /// Simplified expression (unchanged if no strategy registered)
93    ///
94    /// # Examples
95    ///
96    /// ```
97    /// use mathhook_core::algebra::simplification::registry::SIMPLIFICATION_REGISTRY;
98    /// use mathhook_core::expr;
99    ///
100    /// let result = SIMPLIFICATION_REGISTRY.simplify_function("log", &[expr!(1)]);
101    /// assert_eq!(result, expr!(0));  // log(1) = 0
102    /// ```
103    pub fn simplify_function(&self, name: &str, args: &[Expression]) -> Expression {
104        if let Some(strategy) = self.get_strategy(name) {
105            if strategy.applies_to(args) {
106                strategy.simplify(args)
107            } else {
108                Expression::function(name, args.to_vec())
109            }
110        } else {
111            Expression::function(name, args.to_vec())
112        }
113    }
114
115    /// Check if function has simplification strategy registered
116    #[inline(always)]
117    pub fn has_strategy(&self, name: &str) -> bool {
118        self.strategies.contains_key(name)
119    }
120
121    /// Get count of registered strategies (for debugging)
122    pub fn strategy_count(&self) -> usize {
123        self.strategies.len()
124    }
125
126    /// List all registered function names (for debugging)
127    pub fn list_functions(&self) -> Vec<String> {
128        self.strategies.keys().cloned().collect()
129    }
130
131    fn initialize_logarithmic_strategies(&mut self) {
132        self.register("log", Box::new(LogarithmSimplificationStrategy));
133        self.register("ln", Box::new(NaturalLogSimplificationStrategy));
134    }
135
136    fn initialize_trigonometric_strategies(&mut self) {
137        self.register("sin", Box::new(SinSimplificationStrategy));
138        self.register("cos", Box::new(CosSimplificationStrategy));
139        self.register("tan", Box::new(TanSimplificationStrategy));
140
141        self.register(
142            "csc",
143            Box::new(GenericTrigSimplificationStrategy::new("csc")),
144        );
145        self.register(
146            "sec",
147            Box::new(GenericTrigSimplificationStrategy::new("sec")),
148        );
149        self.register(
150            "cot",
151            Box::new(GenericTrigSimplificationStrategy::new("cot")),
152        );
153
154        self.register(
155            "asin",
156            Box::new(GenericTrigSimplificationStrategy::new("asin")),
157        );
158        self.register(
159            "acos",
160            Box::new(GenericTrigSimplificationStrategy::new("acos")),
161        );
162        self.register(
163            "atan",
164            Box::new(GenericTrigSimplificationStrategy::new("atan")),
165        );
166
167        self.register(
168            "sinh",
169            Box::new(GenericTrigSimplificationStrategy::new("sinh")),
170        );
171        self.register(
172            "cosh",
173            Box::new(GenericTrigSimplificationStrategy::new("cosh")),
174        );
175        self.register(
176            "tanh",
177            Box::new(GenericTrigSimplificationStrategy::new("tanh")),
178        );
179    }
180
181    fn initialize_elementary_strategies(&mut self) {
182        self.register("sqrt", Box::new(SqrtSimplificationStrategy));
183        self.register("abs", Box::new(AbsSimplificationStrategy));
184        self.register("exp", Box::new(ExpSimplificationStrategy));
185    }
186
187    fn initialize_special_strategies(&mut self) {
188        self.register("gamma", Box::new(GammaSimplificationStrategy));
189        self.register("factorial", Box::new(FactorialSimplificationStrategy));
190    }
191}
192
193/// Get global simplification registry
194///
195/// Provides universal access to simplification strategies throughout MathHook
196#[inline(always)]
197pub fn get_simplification_registry() -> &'static SimplificationRegistry {
198    &SIMPLIFICATION_REGISTRY
199}
200
201#[cfg(test)]
202mod tests {
203    use super::*;
204
205    #[test]
206    fn test_registry_initialization() {
207        let registry = SimplificationRegistry::new();
208
209        // Registry should initialize successfully with all strategies
210        assert_eq!(registry.strategy_count(), 19);
211
212        // Should have expected capacity
213        assert!(registry.strategies.capacity() >= 32);
214    }
215
216    #[test]
217    fn test_registry_has_strategies() {
218        let registry = SimplificationRegistry::new();
219
220        assert!(registry.has_strategy("log"));
221        assert!(registry.has_strategy("ln"));
222        assert!(registry.has_strategy("sin"));
223        assert!(registry.has_strategy("cos"));
224        assert!(registry.has_strategy("tan"));
225        assert!(registry.has_strategy("sqrt"));
226        assert!(registry.has_strategy("abs"));
227        assert!(registry.has_strategy("exp"));
228        assert!(registry.has_strategy("gamma"));
229        assert!(registry.has_strategy("factorial"));
230    }
231
232    #[test]
233    fn test_global_registry_access() {
234        let _registry = get_simplification_registry();
235
236        let result = SIMPLIFICATION_REGISTRY.simplify_function("unknown", &[]);
237
238        assert!(matches!(result, Expression::Function { .. }));
239    }
240
241    #[test]
242    fn test_simplify_log_of_one() {
243        use crate::expr;
244
245        let result = SIMPLIFICATION_REGISTRY.simplify_function("log", &[expr!(1)]);
246        assert_eq!(result, expr!(0));
247    }
248
249    #[test]
250    fn test_simplify_ln_of_one() {
251        use crate::expr;
252
253        let result = SIMPLIFICATION_REGISTRY.simplify_function("ln", &[expr!(1)]);
254        assert_eq!(result, expr!(0));
255    }
256
257    #[test]
258    fn test_simplify_sin_of_zero() {
259        use crate::expr;
260
261        let result = SIMPLIFICATION_REGISTRY.simplify_function("sin", &[expr!(0)]);
262        assert_eq!(result, expr!(0));
263    }
264
265    #[test]
266    fn test_simplify_cos_of_zero() {
267        use crate::expr;
268
269        let result = SIMPLIFICATION_REGISTRY.simplify_function("cos", &[expr!(0)]);
270        assert_eq!(result, expr!(1));
271    }
272
273    #[test]
274    fn test_simplify_factorial_of_zero() {
275        use crate::expr;
276
277        let result = SIMPLIFICATION_REGISTRY.simplify_function("factorial", &[expr!(0)]);
278        assert_eq!(result, expr!(1));
279    }
280
281    #[test]
282    fn test_simplify_factorial_of_five() {
283        use crate::expr;
284
285        let result = SIMPLIFICATION_REGISTRY.simplify_function("factorial", &[expr!(5)]);
286        assert_eq!(result, expr!(120));
287    }
288}