bicmath_statistics/
lib.rs1pub mod bayesian;
5pub mod causal;
6pub mod common;
7pub mod continuous_distributions;
8pub mod descriptive;
9pub mod discrete_distributions;
10pub mod distributions;
11pub mod inference;
12pub mod mathfn;
13pub mod regression;
14pub mod sequential;
15pub mod stratified;
16pub mod testing;
17
18use std::sync::Arc;
19
20use bicmath_core::contract::{Function, Module, ModuleDescriptor};
21use bicmath_core::number::NumericMode;
22
23pub fn module() -> Module {
25 let mut functions: Vec<Arc<dyn Function>> = Vec::new();
26 functions.extend(descriptive::functions());
27 functions.extend(distributions::functions());
28 functions.extend(discrete_distributions::functions());
29 functions.extend(continuous_distributions::functions());
30 functions.extend(inference::functions());
31 functions.extend(regression::functions());
32 functions.extend(testing::functions());
33 functions.extend(stratified::functions());
34 functions.extend(sequential::functions());
35 functions.extend(bayesian::functions());
36 functions.extend(causal::functions());
37 let descriptor = ModuleDescriptor::new(
38 "statistics",
39 "Statistics",
40 "1.0.0",
41 "Descriptive statistics, probability distributions, inference, and stratified \
42 experiment analysis.",
43 )
44 .with_capabilities(vec![
45 "exact_descriptive_statistics",
46 "probability_distributions",
47 "confidence_intervals",
48 "sample_size_planning",
49 "regression_models",
50 "hypothesis_testing",
51 "equivalence_testing",
52 "stratified_experiments",
53 "sequential_inference",
54 "confidence_sequences",
55 "bayesian_conjugate_updates",
56 "causal_estimators",
57 ])
58 .with_dependencies(vec!["core"])
59 .with_modes(vec![
60 NumericMode::Exact,
61 NumericMode::Auto,
62 NumericMode::Scientific,
63 ])
64 .with_source("crates/bicmath-statistics");
65 Module::new(descriptor, functions)
66}
67
68#[cfg(test)]
69mod tests {
70 use super::*;
71 use bicmath_core::context::ExecContext;
72 use bicmath_core::contract::{Args, ExampleExpectation, Function};
73 use std::collections::{BTreeMap, BTreeSet};
74
75 fn context_for(function: &dyn Function) -> ExecContext {
76 if function.descriptor().modes.contains(&NumericMode::Exact) {
77 ExecContext::exact()
79 } else {
80 ExecContext::scientific()
81 }
82 }
83
84 #[test]
85 fn descriptor_contract_is_complete() {
86 let module = module();
87 assert_eq!(module.descriptor.id, "statistics");
88 assert_eq!(module.descriptor.version, "1.0.0");
89 assert!(
90 module.functions.len() >= 38,
91 "expected all statistics functions"
92 );
93 let mut ids = BTreeSet::new();
94 for function in &module.functions {
95 let descriptor = function.descriptor();
96 assert!(
97 descriptor.id.starts_with("statistics."),
98 "bad id {}",
99 descriptor.id
100 );
101 assert_eq!(descriptor.module, "statistics");
102 assert_eq!(descriptor.version, "1.0.0");
103 assert!(
104 descriptor
105 .method_ref
106 .starts_with("docs/methods/statistics.md#"),
107 "bad method_ref {}",
108 descriptor.method_ref
109 );
110 assert!(
111 !descriptor.examples.is_empty(),
112 "function {} has no executable example",
113 descriptor.id
114 );
115 assert!(ids.insert(descriptor.id.clone()), "duplicate id");
116 }
117 }
118
119 #[test]
120 fn every_example_executes_and_matches() {
121 for function in module().functions {
122 let ctx = context_for(function.as_ref());
123 for example in &function.descriptor().examples {
124 let mut values = BTreeMap::new();
125 for (name, raw) in &example.arguments {
126 let parameter = function
127 .descriptor()
128 .parameter(name)
129 .unwrap_or_else(|| panic!("example parameter {name} is not declared"));
130 parameter
131 .schema
132 .validate(raw, name, &ctx.limits)
133 .unwrap_or_else(|error| {
134 panic!(
135 "example {:?} of {} has an invalid {name}: {error}",
136 example.title,
137 function.descriptor().id
138 )
139 });
140 values.insert(name.clone(), raw.clone());
141 }
142 let result = function.invoke(&Args::new(values), &ctx);
143 match &example.expected {
144 Some(ExampleExpectation::Value(expected)) => {
145 let outcome = result.unwrap_or_else(|error| {
146 panic!(
147 "example {:?} of {} failed: {error}",
148 example.title,
149 function.descriptor().id
150 )
151 });
152 assert_eq!(
153 &outcome.value,
154 expected,
155 "example {:?} of {} produced an unexpected value",
156 example.title,
157 function.descriptor().id
158 );
159 }
160 Some(ExampleExpectation::Error(code)) => {
161 let error = match result {
162 Err(error) => error,
163 Ok(outcome) => panic!(
164 "example {:?} of {} expected {code:?}, produced {:?}",
165 example.title,
166 function.descriptor().id,
167 outcome.value
168 ),
169 };
170 assert_eq!(
171 error.code,
172 *code,
173 "example {:?} of {} produced the wrong error: {}",
174 example.title,
175 function.descriptor().id,
176 error.message
177 );
178 }
179 Some(ExampleExpectation::Contains(text)) => {
180 let outcome = result.unwrap_or_else(|error| {
181 panic!(
182 "example {:?} of {} failed: {error}",
183 example.title,
184 function.descriptor().id
185 )
186 });
187 assert!(
188 format!("{:?}", outcome.value).contains(text),
189 "example {:?} of {} does not contain {text:?}",
190 example.title,
191 function.descriptor().id
192 );
193 }
194 None => {
195 let _ = result;
197 }
198 }
199 }
200 }
201 }
202}