1use crate::profile::{Profile, UserConstant};
7use crate::symbol::{NumType, Symbol};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
11pub enum Preset {
12 AnalyticNT,
14 Elliptic,
16 Combinatorics,
18 Physics,
20 NumberTheory,
22 Calculus,
24}
25
26impl Preset {
27 pub fn name(&self) -> &'static str {
29 match self {
30 Preset::AnalyticNT => "analytic-nt",
31 Preset::Elliptic => "elliptic",
32 Preset::Combinatorics => "combinatorics",
33 Preset::Physics => "physics",
34 Preset::NumberTheory => "number-theory",
35 Preset::Calculus => "calculus",
36 }
37 }
38
39 pub fn description(&self) -> &'static str {
41 match self {
42 Preset::AnalyticNT => "Analytic number theory: ζ values, Γ, log, π powers",
43 Preset::Elliptic => "Elliptic/modular: K(k), E(k), Γ(1/4), q-series constants",
44 Preset::Combinatorics => "Combinatorics: Catalan, Apéry, polylog patterns",
45 Preset::Physics => "Physics: π, log, γ, ζ, Clausen-type constants",
46 Preset::NumberTheory => "Number theory: rational/algebraic focus",
47 Preset::Calculus => "Calculus: standard functions, no exotic constants",
48 }
49 }
50
51 #[allow(clippy::should_implement_trait)]
53 pub fn from_str(s: &str) -> Option<Self> {
54 match s.to_lowercase().as_str() {
55 "analytic-nt" | "ant" | "analytic" => Some(Preset::AnalyticNT),
56 "elliptic" | "modular" => Some(Preset::Elliptic),
57 "combinatorics" | "combo" | "catalan" => Some(Preset::Combinatorics),
58 "physics" | "phys" => Some(Preset::Physics),
59 "number-theory" | "nt" | "algebraic" => Some(Preset::NumberTheory),
60 "calculus" | "calc" => Some(Preset::Calculus),
61 _ => None,
62 }
63 }
64
65 #[allow(clippy::wrong_self_convention)]
67 pub fn to_profile(&self) -> Profile {
68 let mut profile = Profile::new();
69
70 match self {
71 Preset::AnalyticNT => {
72 profile.symbol_weights = vec![
78 (Symbol::Pi, 6), (Symbol::Ln, 6), (Symbol::Exp, 6), (Symbol::SinPi, 12), (Symbol::CosPi, 12), (Symbol::TanPi, 14), (Symbol::LambertW, 16), ]
86 .into_iter()
87 .collect();
88
89 profile.constants.push(UserConstant {
91 weight: 10,
92 name: "z2".to_string(),
93 description: "ζ(2) = π²/6 (Basel problem)".to_string(),
94 value: std::f64::consts::PI * std::f64::consts::PI / 6.0,
95 num_type: NumType::Transcendental,
96 });
97
98 profile.symbol_weights.insert(Symbol::Apery, 8);
101
102 profile.symbol_weights.insert(Symbol::Gamma, 8);
104 }
105
106 Preset::Elliptic => {
107 profile.symbol_weights = vec![
112 (Symbol::Sqrt, 4), (Symbol::Square, 4), (Symbol::Ln, 6), (Symbol::Pi, 6), (Symbol::SinPi, 16), (Symbol::CosPi, 16), (Symbol::TanPi, 18), ]
120 .into_iter()
121 .collect();
122
123 profile.constants.push(UserConstant {
125 weight: 12,
126 name: "g14".to_string(),
127 description: "Γ(1/4) - elliptic integral constant".to_string(),
128 value: 3.625609908221908,
129 num_type: NumType::Transcendental,
130 });
131
132 profile.constants.push(UserConstant {
134 weight: 14,
135 name: "K1".to_string(),
136 description: "K(1/√2) - complete elliptic integral".to_string(),
137 value: 1.854074677301372,
138 num_type: NumType::Transcendental,
139 });
140
141 profile.symbol_weights.insert(Symbol::Phi, 8);
143 }
144
145 Preset::Combinatorics => {
146 profile.symbol_weights = vec![
150 (Symbol::One, 2), (Symbol::Two, 2), (Symbol::Three, 2), (Symbol::Four, 3), (Symbol::Five, 3), (Symbol::Pi, 12), (Symbol::E, 12), (Symbol::Ln, 14), (Symbol::Exp, 14), ]
160 .into_iter()
161 .collect();
162
163 profile.symbol_weights.insert(Symbol::Catalan, 8);
165
166 profile.symbol_weights.insert(Symbol::Apery, 8);
168 }
169
170 Preset::Physics => {
171 profile.symbol_weights = vec![
175 (Symbol::Pi, 5), (Symbol::E, 6), (Symbol::Ln, 6), (Symbol::Exp, 6), (Symbol::Sqrt, 5), (Symbol::SinPi, 10), (Symbol::CosPi, 10), ]
183 .into_iter()
184 .collect();
185
186 profile.symbol_weights.insert(Symbol::Gamma, 8);
188
189 }
192
193 Preset::NumberTheory => {
194 profile.symbol_weights = vec![
198 (Symbol::One, 2),
199 (Symbol::Two, 2),
200 (Symbol::Three, 2),
201 (Symbol::Four, 3),
202 (Symbol::Five, 3),
203 (Symbol::Six, 3),
204 (Symbol::Seven, 3),
205 (Symbol::Eight, 4),
206 (Symbol::Nine, 4),
207 (Symbol::Sqrt, 6), (Symbol::Square, 6), (Symbol::Pi, 16), (Symbol::E, 16), (Symbol::Ln, 18), (Symbol::Exp, 18), ]
214 .into_iter()
215 .collect();
216
217 profile.symbol_weights.insert(Symbol::Phi, 8);
219
220 profile.symbol_weights.insert(Symbol::Plastic, 10);
222 }
223
224 Preset::Calculus => {
225 profile.symbol_weights = vec![
229 (Symbol::Pi, 7),
230 (Symbol::E, 7),
231 (Symbol::Ln, 7),
232 (Symbol::Exp, 7),
233 (Symbol::Sqrt, 5),
234 (Symbol::Square, 5),
235 (Symbol::SinPi, 8),
236 (Symbol::CosPi, 8),
237 (Symbol::TanPi, 9),
238 ]
239 .into_iter()
240 .collect();
241 }
242 }
243
244 profile
245 }
246
247 pub fn all() -> &'static [Preset] {
249 &[
250 Preset::AnalyticNT,
251 Preset::Elliptic,
252 Preset::Combinatorics,
253 Preset::Physics,
254 Preset::NumberTheory,
255 Preset::Calculus,
256 ]
257 }
258}
259
260#[allow(clippy::print_literal)]
262pub fn print_presets() {
263 println!("Available domain presets:");
264 println!();
265 println!(" {:<15} {}", "PRESET", "DESCRIPTION");
266 println!(" {}", "-".repeat(70));
267 for preset in Preset::all() {
268 println!(" {:<15} {}", preset.name(), preset.description());
269 }
270 println!();
271 println!("Usage: ries-rs --preset <name> <target>");
272 println!("Example: ries-rs --preset physics 6.67430e-11");
273}
274
275#[cfg(test)]
276mod tests {
277 use super::*;
278
279 #[test]
280 fn test_preset_parse() {
281 assert_eq!(Preset::from_str("analytic-nt"), Some(Preset::AnalyticNT));
282 assert_eq!(Preset::from_str("ANT"), Some(Preset::AnalyticNT));
283 assert_eq!(Preset::from_str("physics"), Some(Preset::Physics));
284 assert_eq!(Preset::from_str("PHYS"), Some(Preset::Physics));
285 assert_eq!(Preset::from_str("invalid"), None);
286 }
287
288 #[test]
289 fn test_preset_profile_non_empty() {
290 for preset in Preset::all() {
291 let profile = preset.to_profile();
292 assert!(
294 !profile.symbol_weights.is_empty() || !profile.constants.is_empty(),
295 "Preset {:?} should have some configuration",
296 preset
297 );
298 }
299 }
300
301 #[test]
302 fn test_analytic_nt_has_gamma() {
303 let profile = Preset::AnalyticNT.to_profile();
304 assert!(profile.symbol_weights.contains_key(&Symbol::Gamma));
305 }
306
307 #[test]
308 fn test_number_theory_avoids_transcendentals() {
309 let profile = Preset::NumberTheory.to_profile();
310 assert!(profile.symbol_weights.get(&Symbol::Pi) > Some(&10));
312 assert!(profile.symbol_weights.get(&Symbol::E) > Some(&10));
313 }
314}