1use std::collections::HashMap;
14use std::sync::{Arc, RwLock};
15
16use crate::error::{CoreError, CoreResult, ErrorContext, ErrorLocation};
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
20pub enum Precision {
21 Half,
23 Single,
25 #[default]
27 Double,
28 Extended,
30 Auto,
32}
33
34impl Precision {
35 #[must_use]
37 pub const fn bits(&self) -> usize {
38 match self {
39 Precision::Half => 16,
40 Precision::Single => 32,
41 Precision::Double => 64,
42 Precision::Extended => 80,
43 Precision::Auto => 64, }
45 }
46
47 #[must_use]
49 pub fn epsilon(&self) -> f64 {
50 match self {
51 Precision::Half => 9.77e-4, Precision::Single => 1.19e-7, Precision::Double => 2.22e-16, Precision::Extended => 1.08e-19, Precision::Auto => f64::EPSILON,
56 }
57 }
58}
59
60#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, PartialOrd, Ord)]
62pub enum LogLevel {
63 Off,
65 Error,
67 Warn,
69 #[default]
71 Info,
72 Debug,
74 Trace,
76}
77
78impl LogLevel {
79 #[must_use]
81 pub const fn should_log(&self, level: LogLevel) -> bool {
82 (*self as u8) >= (level as u8)
83 }
84}
85
86#[derive(Debug, Clone, PartialEq)]
88pub struct PrecisionConfig {
89 pub default_precision: Precision,
91 pub epsilon: f64,
93 pub strict_mode: bool,
95 pub allow_precision_loss: bool,
97 pub max_relative_error: f64,
99}
100
101impl Default for PrecisionConfig {
102 fn default() -> Self {
103 Self {
104 default_precision: Precision::Double,
105 epsilon: 1e-10,
106 strict_mode: false,
107 allow_precision_loss: true,
108 max_relative_error: 1e-6,
109 }
110 }
111}
112
113#[derive(Debug, Clone, PartialEq)]
115pub struct ParallelConfig {
116 pub enabled: bool,
118 pub num_threads: Option<usize>,
120 pub min_chunk_size: usize,
122 pub max_tasks: usize,
124 pub work_stealing: bool,
126 pub thread_priority: u8,
128}
129
130impl Default for ParallelConfig {
131 fn default() -> Self {
132 Self {
133 enabled: true,
134 num_threads: None,
135 min_chunk_size: 1024,
136 max_tasks: 256,
137 work_stealing: true,
138 thread_priority: 50,
139 }
140 }
141}
142
143impl ParallelConfig {
144 #[must_use]
146 pub fn effective_threads(&self) -> usize {
147 self.num_threads.unwrap_or_else(|| {
148 std::thread::available_parallelism()
149 .map(|n| n.get())
150 .unwrap_or(4)
151 })
152 }
153}
154
155#[derive(Debug, Clone, PartialEq)]
157pub struct MemoryConfig {
158 pub max_memory: Option<usize>,
160 pub enable_pooling: bool,
162 pub pool_block_size: usize,
164 pub enable_mmap: bool,
166 pub mmap_threshold: usize,
168 pub track_usage: bool,
170 pub cache_size: usize,
172}
173
174impl Default for MemoryConfig {
175 fn default() -> Self {
176 Self {
177 max_memory: None,
178 enable_pooling: true,
179 pool_block_size: 4096,
180 enable_mmap: true,
181 mmap_threshold: 64 * 1024 * 1024, track_usage: false,
183 cache_size: 256 * 1024 * 1024, }
185 }
186}
187
188#[derive(Debug, Clone, PartialEq)]
190pub struct NumericConfig {
191 pub max_iterations: usize,
193 pub tolerance: f64,
195 pub check_overflow: bool,
197 pub check_special_values: bool,
199 pub random_seed: Option<u64>,
201 pub deterministic: bool,
203}
204
205impl Default for NumericConfig {
206 fn default() -> Self {
207 Self {
208 max_iterations: 1000,
209 tolerance: 1e-8,
210 check_overflow: true,
211 check_special_values: true,
212 random_seed: None,
213 deterministic: false,
214 }
215 }
216}
217
218#[derive(Debug, Clone, PartialEq)]
220pub struct DiagnosticsConfig {
221 pub log_level: LogLevel,
223 pub enable_profiling: bool,
225 pub enable_tracing: bool,
227 pub collect_metrics: bool,
229 pub debug_assertions: bool,
231 pub output_format: DiagnosticsFormat,
233}
234
235impl Default for DiagnosticsConfig {
236 fn default() -> Self {
237 Self {
238 log_level: LogLevel::Info,
239 enable_profiling: false,
240 enable_tracing: false,
241 collect_metrics: false,
242 debug_assertions: cfg!(debug_assertions),
243 output_format: DiagnosticsFormat::Text,
244 }
245 }
246}
247
248#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
250pub enum DiagnosticsFormat {
251 #[default]
253 Text,
254 Json,
256 Csv,
258 Binary,
260}
261
262#[derive(Debug, Clone, Default)]
264pub struct ModuleConfig {
265 pub name: String,
267 pub settings: HashMap<String, ConfigValue>,
269 pub enabled: bool,
271}
272
273impl ModuleConfig {
274 #[must_use]
276 pub fn new(name: impl Into<String>) -> Self {
277 Self {
278 name: name.into(),
279 settings: HashMap::new(),
280 enabled: true,
281 }
282 }
283
284 pub fn set(&mut self, key: impl Into<String>, value: impl Into<ConfigValue>) -> &mut Self {
286 self.settings.insert(key.into(), value.into());
287 self
288 }
289
290 #[must_use]
292 pub fn get(&self, key: &str) -> Option<&ConfigValue> {
293 self.settings.get(key)
294 }
295
296 pub fn get_typed<T: TryFrom<ConfigValue, Error = CoreError>>(
298 &self,
299 key: &str,
300 ) -> CoreResult<T> {
301 match self.settings.get(key) {
302 Some(value) => T::try_from(value.clone()),
303 None => Err(CoreError::ConfigError(
304 ErrorContext::new(format!(
305 "Key '{key}' not found in module '{name}'",
306 name = self.name
307 ))
308 .with_location(ErrorLocation::new(file!(), line!())),
309 )),
310 }
311 }
312
313 pub fn enable(&mut self) -> &mut Self {
315 self.enabled = true;
316 self
317 }
318
319 pub fn disable(&mut self) -> &mut Self {
321 self.enabled = false;
322 self
323 }
324}
325
326#[derive(Debug, Clone, PartialEq)]
328pub enum ConfigValue {
329 Bool(bool),
331 Int(i64),
333 UInt(u64),
335 Float(f64),
337 String(String),
339 List(Vec<ConfigValue>),
341 Map(HashMap<String, ConfigValue>),
343}
344
345impl From<bool> for ConfigValue {
346 fn from(v: bool) -> Self {
347 ConfigValue::Bool(v)
348 }
349}
350
351impl From<i64> for ConfigValue {
352 fn from(v: i64) -> Self {
353 ConfigValue::Int(v)
354 }
355}
356
357impl From<i32> for ConfigValue {
358 fn from(v: i32) -> Self {
359 ConfigValue::Int(v as i64)
360 }
361}
362
363impl From<u64> for ConfigValue {
364 fn from(v: u64) -> Self {
365 ConfigValue::UInt(v)
366 }
367}
368
369impl From<usize> for ConfigValue {
370 fn from(v: usize) -> Self {
371 ConfigValue::UInt(v as u64)
372 }
373}
374
375impl From<f64> for ConfigValue {
376 fn from(v: f64) -> Self {
377 ConfigValue::Float(v)
378 }
379}
380
381impl From<f32> for ConfigValue {
382 fn from(v: f32) -> Self {
383 ConfigValue::Float(v as f64)
384 }
385}
386
387impl From<String> for ConfigValue {
388 fn from(v: String) -> Self {
389 ConfigValue::String(v)
390 }
391}
392
393impl From<&str> for ConfigValue {
394 fn from(v: &str) -> Self {
395 ConfigValue::String(v.to_string())
396 }
397}
398
399impl TryFrom<ConfigValue> for bool {
400 type Error = CoreError;
401
402 fn try_from(value: ConfigValue) -> Result<Self, Self::Error> {
403 match value {
404 ConfigValue::Bool(v) => Ok(v),
405 _ => Err(CoreError::ConfigError(
406 ErrorContext::new(format!("Expected bool, got {:?}", value))
407 .with_location(ErrorLocation::new(file!(), line!())),
408 )),
409 }
410 }
411}
412
413impl TryFrom<ConfigValue> for i64 {
414 type Error = CoreError;
415
416 fn try_from(value: ConfigValue) -> Result<Self, Self::Error> {
417 match value {
418 ConfigValue::Int(v) => Ok(v),
419 ConfigValue::UInt(v) if v <= i64::MAX as u64 => Ok(v as i64),
420 _ => Err(CoreError::ConfigError(
421 ErrorContext::new(format!("Expected int, got {:?}", value))
422 .with_location(ErrorLocation::new(file!(), line!())),
423 )),
424 }
425 }
426}
427
428impl TryFrom<ConfigValue> for u64 {
429 type Error = CoreError;
430
431 fn try_from(value: ConfigValue) -> Result<Self, Self::Error> {
432 match value {
433 ConfigValue::UInt(v) => Ok(v),
434 ConfigValue::Int(v) if v >= 0 => Ok(v as u64),
435 _ => Err(CoreError::ConfigError(
436 ErrorContext::new(format!("Expected uint, got {:?}", value))
437 .with_location(ErrorLocation::new(file!(), line!())),
438 )),
439 }
440 }
441}
442
443impl TryFrom<ConfigValue> for f64 {
444 type Error = CoreError;
445
446 fn try_from(value: ConfigValue) -> Result<Self, Self::Error> {
447 match value {
448 ConfigValue::Float(v) => Ok(v),
449 ConfigValue::Int(v) => Ok(v as f64),
450 ConfigValue::UInt(v) => Ok(v as f64),
451 _ => Err(CoreError::ConfigError(
452 ErrorContext::new(format!("Expected float, got {:?}", value))
453 .with_location(ErrorLocation::new(file!(), line!())),
454 )),
455 }
456 }
457}
458
459impl TryFrom<ConfigValue> for String {
460 type Error = CoreError;
461
462 fn try_from(value: ConfigValue) -> Result<Self, Self::Error> {
463 match value {
464 ConfigValue::String(v) => Ok(v),
465 _ => Err(CoreError::ConfigError(
466 ErrorContext::new(format!("Expected string, got {:?}", value))
467 .with_location(ErrorLocation::new(file!(), line!())),
468 )),
469 }
470 }
471}
472
473#[derive(Debug, Clone)]
475pub struct EcosystemConfig {
476 pub precision: PrecisionConfig,
478 pub parallel: ParallelConfig,
480 pub memory: MemoryConfig,
482 pub numeric: NumericConfig,
484 pub diagnostics: DiagnosticsConfig,
486 pub modules: HashMap<String, ModuleConfig>,
488 pub custom: HashMap<String, ConfigValue>,
490 pub version: u32,
492}
493
494impl Default for EcosystemConfig {
495 fn default() -> Self {
496 Self {
497 precision: PrecisionConfig::default(),
498 parallel: ParallelConfig::default(),
499 memory: MemoryConfig::default(),
500 numeric: NumericConfig::default(),
501 diagnostics: DiagnosticsConfig::default(),
502 modules: HashMap::new(),
503 custom: HashMap::new(),
504 version: 1,
505 }
506 }
507}
508
509impl EcosystemConfig {
510 #[must_use]
512 pub fn new() -> Self {
513 Self::default()
514 }
515
516 #[must_use]
518 pub fn builder() -> EcosystemConfigBuilder {
519 EcosystemConfigBuilder::new()
520 }
521
522 #[must_use]
524 pub fn module(&self, name: &str) -> Option<&ModuleConfig> {
525 self.modules.get(name)
526 }
527
528 pub fn module_mut(&mut self, name: &str) -> Option<&mut ModuleConfig> {
530 self.modules.get_mut(name)
531 }
532
533 pub fn register_module(&mut self, config: ModuleConfig) -> &mut Self {
535 self.modules.insert(config.name.clone(), config);
536 self
537 }
538
539 pub fn set_custom(
541 &mut self,
542 key: impl Into<String>,
543 value: impl Into<ConfigValue>,
544 ) -> &mut Self {
545 self.custom.insert(key.into(), value.into());
546 self
547 }
548
549 #[must_use]
551 pub fn get_custom(&self, key: &str) -> Option<&ConfigValue> {
552 self.custom.get(key)
553 }
554
555 pub fn validate(&self) -> CoreResult<()> {
557 if self.precision.epsilon <= 0.0 {
559 return Err(CoreError::ConfigError(
560 ErrorContext::new("Epsilon must be positive")
561 .with_location(ErrorLocation::new(file!(), line!())),
562 ));
563 }
564
565 if self.precision.max_relative_error <= 0.0 {
566 return Err(CoreError::ConfigError(
567 ErrorContext::new("Max relative error must be positive")
568 .with_location(ErrorLocation::new(file!(), line!())),
569 ));
570 }
571
572 if self.parallel.min_chunk_size == 0 {
574 return Err(CoreError::ConfigError(
575 ErrorContext::new("Minimum chunk size must be positive")
576 .with_location(ErrorLocation::new(file!(), line!())),
577 ));
578 }
579
580 if self.numeric.max_iterations == 0 {
582 return Err(CoreError::ConfigError(
583 ErrorContext::new("Maximum iterations must be positive")
584 .with_location(ErrorLocation::new(file!(), line!())),
585 ));
586 }
587
588 if self.numeric.tolerance <= 0.0 {
589 return Err(CoreError::ConfigError(
590 ErrorContext::new("Tolerance must be positive")
591 .with_location(ErrorLocation::new(file!(), line!())),
592 ));
593 }
594
595 Ok(())
596 }
597
598 pub fn merge(&mut self, other: &EcosystemConfig) {
600 for (name, config) in &other.modules {
602 if let Some(existing) = self.modules.get_mut(name) {
603 existing.settings.extend(config.settings.clone());
604 existing.enabled = config.enabled;
605 } else {
606 self.modules.insert(name.clone(), config.clone());
607 }
608 }
609
610 self.custom.extend(other.custom.clone());
612 }
613}
614
615#[derive(Debug, Default)]
617pub struct EcosystemConfigBuilder {
618 config: EcosystemConfig,
619}
620
621impl EcosystemConfigBuilder {
622 #[must_use]
624 pub fn new() -> Self {
625 Self::default()
626 }
627
628 #[must_use]
630 pub fn with_precision(mut self, precision: Precision) -> Self {
631 self.config.precision.default_precision = precision;
632 self.config.precision.epsilon = precision.epsilon();
633 self
634 }
635
636 #[must_use]
638 pub fn with_epsilon(mut self, epsilon: f64) -> Self {
639 self.config.precision.epsilon = epsilon;
640 self
641 }
642
643 #[must_use]
645 pub fn with_parallel(mut self, enabled: bool) -> Self {
646 self.config.parallel.enabled = enabled;
647 self
648 }
649
650 #[must_use]
652 pub fn with_num_threads(mut self, threads: usize) -> Self {
653 self.config.parallel.num_threads = Some(threads);
654 self
655 }
656
657 #[must_use]
659 pub fn with_max_memory(mut self, bytes: usize) -> Self {
660 self.config.memory.max_memory = Some(bytes);
661 self
662 }
663
664 #[must_use]
666 pub fn with_memory_pooling(mut self, enabled: bool) -> Self {
667 self.config.memory.enable_pooling = enabled;
668 self
669 }
670
671 #[must_use]
673 pub fn with_max_iterations(mut self, max: usize) -> Self {
674 self.config.numeric.max_iterations = max;
675 self
676 }
677
678 #[must_use]
680 pub fn with_tolerance(mut self, tolerance: f64) -> Self {
681 self.config.numeric.tolerance = tolerance;
682 self
683 }
684
685 #[must_use]
687 pub fn with_random_seed(mut self, seed: u64) -> Self {
688 self.config.numeric.random_seed = Some(seed);
689 self.config.numeric.deterministic = true;
690 self
691 }
692
693 #[must_use]
695 pub fn with_deterministic(mut self, deterministic: bool) -> Self {
696 self.config.numeric.deterministic = deterministic;
697 self
698 }
699
700 #[must_use]
702 pub fn with_log_level(mut self, level: LogLevel) -> Self {
703 self.config.diagnostics.log_level = level;
704 self
705 }
706
707 #[must_use]
709 pub fn with_profiling(mut self, enabled: bool) -> Self {
710 self.config.diagnostics.enable_profiling = enabled;
711 self
712 }
713
714 #[must_use]
716 pub fn with_tracing(mut self, enabled: bool) -> Self {
717 self.config.diagnostics.enable_tracing = enabled;
718 self
719 }
720
721 #[must_use]
723 pub fn with_module(mut self, config: ModuleConfig) -> Self {
724 self.config.modules.insert(config.name.clone(), config);
725 self
726 }
727
728 #[must_use]
730 pub fn with_custom(mut self, key: impl Into<String>, value: impl Into<ConfigValue>) -> Self {
731 self.config.custom.insert(key.into(), value.into());
732 self
733 }
734
735 pub fn build(self) -> CoreResult<EcosystemConfig> {
741 self.config.validate()?;
742 Ok(self.config)
743 }
744
745 #[must_use]
747 pub fn build_unchecked(self) -> EcosystemConfig {
748 self.config
749 }
750}
751
752static GLOBAL_ECOSYSTEM_CONFIG: std::sync::LazyLock<RwLock<Arc<EcosystemConfig>>> =
754 std::sync::LazyLock::new(|| RwLock::new(Arc::new(EcosystemConfig::default())));
755
756#[must_use]
758pub fn global_config() -> Arc<EcosystemConfig> {
759 GLOBAL_ECOSYSTEM_CONFIG
760 .read()
761 .map(|guard| Arc::clone(&*guard))
762 .unwrap_or_else(|_| Arc::new(EcosystemConfig::default()))
763}
764
765pub fn set_global_config(config: EcosystemConfig) -> CoreResult<()> {
767 config.validate()?;
768 let mut guard = GLOBAL_ECOSYSTEM_CONFIG.write().map_err(|e| {
769 CoreError::ConfigError(
770 ErrorContext::new(format!("Failed to acquire write lock: {e}"))
771 .with_location(ErrorLocation::new(file!(), line!())),
772 )
773 })?;
774 *guard = Arc::new(config);
775 Ok(())
776}
777
778pub fn update_global_config<F>(f: F) -> CoreResult<()>
780where
781 F: FnOnce(&mut EcosystemConfig),
782{
783 let mut guard = GLOBAL_ECOSYSTEM_CONFIG.write().map_err(|e| {
784 CoreError::ConfigError(
785 ErrorContext::new(format!("Failed to acquire write lock: {e}"))
786 .with_location(ErrorLocation::new(file!(), line!())),
787 )
788 })?;
789
790 let mut config = (**guard).clone();
791 f(&mut config);
792 config.validate()?;
793 *guard = Arc::new(config);
794 Ok(())
795}
796
797#[cfg(test)]
798mod tests {
799 use super::*;
800
801 #[test]
802 fn test_precision_levels() {
803 assert_eq!(Precision::Half.bits(), 16);
804 assert_eq!(Precision::Single.bits(), 32);
805 assert_eq!(Precision::Double.bits(), 64);
806 assert_eq!(Precision::Extended.bits(), 80);
807 }
808
809 #[test]
810 fn test_log_level_ordering() {
811 assert!(LogLevel::Error < LogLevel::Warn);
812 assert!(LogLevel::Warn < LogLevel::Info);
813 assert!(LogLevel::Info < LogLevel::Debug);
814 assert!(LogLevel::Debug < LogLevel::Trace);
815 }
816
817 #[test]
818 fn test_config_builder() {
819 let config = EcosystemConfig::builder()
820 .with_precision(Precision::Single)
821 .with_parallel(true)
822 .with_num_threads(4)
823 .with_max_iterations(500)
824 .with_tolerance(1e-6)
825 .build()
826 .expect("Config should be valid");
827
828 assert_eq!(config.precision.default_precision, Precision::Single);
829 assert!(config.parallel.enabled);
830 assert_eq!(config.parallel.num_threads, Some(4));
831 assert_eq!(config.numeric.max_iterations, 500);
832 assert_eq!(config.numeric.tolerance, 1e-6);
833 }
834
835 #[test]
836 fn test_module_config() {
837 let mut module = ModuleConfig::new("test_module");
838 module
839 .set("key1", true)
840 .set("key2", 42i64)
841 .set("key3", "value");
842
843 assert!(module.get_typed::<bool>("key1").expect("Should work"));
844 assert_eq!(module.get_typed::<i64>("key2").expect("Should work"), 42);
845 assert_eq!(
846 module.get_typed::<String>("key3").expect("Should work"),
847 "value"
848 );
849 }
850
851 #[test]
852 fn test_config_validation() {
853 let config = EcosystemConfig::builder()
854 .with_epsilon(-1.0)
855 .build_unchecked();
856
857 assert!(config.validate().is_err());
858 }
859
860 #[test]
861 fn test_config_value_conversions() {
862 let bool_val = ConfigValue::from(true);
863 assert!(bool::try_from(bool_val).expect("Should work"));
864
865 let int_val = ConfigValue::from(42i64);
866 assert_eq!(i64::try_from(int_val).expect("Should work"), 42);
867
868 let float_val = ConfigValue::from(3.5f64);
869 let f = f64::try_from(float_val).expect("Should work");
870 assert!((f - 3.5).abs() < 1e-10);
871 }
872
873 #[test]
874 fn test_parallel_config() {
875 let config = ParallelConfig {
876 num_threads: Some(8),
877 ..Default::default()
878 };
879 assert_eq!(config.effective_threads(), 8);
880
881 let auto_config = ParallelConfig::default();
882 assert!(auto_config.effective_threads() >= 1);
883 }
884
885 #[test]
886 fn test_ecosystem_config_merge() {
887 let mut config1 = EcosystemConfig::new();
888 config1.set_custom("key1", "value1");
889 config1.register_module(ModuleConfig::new("module1"));
890
891 let mut config2 = EcosystemConfig::new();
892 config2.set_custom("key2", "value2");
893 config2.register_module(ModuleConfig::new("module2"));
894
895 config1.merge(&config2);
896
897 assert!(config1.get_custom("key1").is_some());
898 assert!(config1.get_custom("key2").is_some());
899 assert!(config1.module("module1").is_some());
900 assert!(config1.module("module2").is_some());
901 }
902}