1use std::fmt;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
11pub enum EquationClass {
12 Conservation,
14 Queueing,
16 Statistical,
18 Inventory,
20 Optimization,
22 MachineLearning,
24}
25
26impl fmt::Display for EquationClass {
27 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28 match self {
29 Self::Conservation => write!(f, "Conservation"),
30 Self::Queueing => write!(f, "Queueing Theory"),
31 Self::Statistical => write!(f, "Statistical Mechanics"),
32 Self::Inventory => write!(f, "Inventory Management"),
33 Self::Optimization => write!(f, "Optimization"),
34 Self::MachineLearning => write!(f, "Machine Learning"),
35 }
36 }
37}
38
39#[derive(Debug, Clone)]
41pub struct EquationVariable {
42 pub symbol: String,
44 pub name: String,
46 pub units: String,
48 pub description: String,
50 pub constraints: Option<VariableConstraints>,
52}
53
54#[derive(Debug, Clone)]
56pub struct VariableConstraints {
57 pub min: Option<f64>,
59 pub max: Option<f64>,
61 pub positive: bool,
63 pub integer: bool,
65}
66
67impl EquationVariable {
68 #[must_use]
70 pub fn new(symbol: &str, name: &str, units: &str) -> Self {
71 Self {
72 symbol: symbol.to_string(),
73 name: name.to_string(),
74 units: units.to_string(),
75 description: String::new(),
76 constraints: None,
77 }
78 }
79
80 #[must_use]
82 pub fn with_description(mut self, desc: &str) -> Self {
83 self.description = desc.to_string();
84 self
85 }
86
87 #[must_use]
89 pub fn with_constraints(mut self, constraints: VariableConstraints) -> Self {
90 self.constraints = Some(constraints);
91 self
92 }
93}
94
95#[derive(Debug, Clone)]
97pub struct Citation {
98 pub authors: Vec<String>,
100 pub title: String,
102 pub venue: String,
104 pub year: u32,
106 pub doi: Option<String>,
108 pub pages: Option<String>,
110}
111
112impl Citation {
113 #[must_use]
115 pub fn new(authors: &[&str], venue: &str, year: u32) -> Self {
116 Self {
117 authors: authors.iter().map(|s| (*s).to_string()).collect(),
118 title: String::new(),
119 venue: venue.to_string(),
120 year,
121 doi: None,
122 pages: None,
123 }
124 }
125
126 #[must_use]
128 pub fn with_title(mut self, title: &str) -> Self {
129 self.title = title.to_string();
130 self
131 }
132
133 #[must_use]
135 pub fn with_doi(mut self, doi: &str) -> Self {
136 self.doi = Some(doi.to_string());
137 self
138 }
139
140 #[must_use]
142 pub fn with_pages(mut self, pages: &str) -> Self {
143 self.pages = Some(pages.to_string());
144 self
145 }
146}
147
148impl fmt::Display for Citation {
149 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
150 let authors = self.authors.join(", ");
151 write!(
152 f,
153 "{authors} ({}) \"{}\", {}",
154 self.year, self.title, self.venue
155 )?;
156 if let Some(ref pages) = self.pages {
157 write!(f, ", pp. {pages}")?;
158 }
159 if let Some(ref doi) = self.doi {
160 write!(f, ", doi:{doi}")?;
161 }
162 Ok(())
163 }
164}
165
166pub trait GoverningEquation {
171 fn latex(&self) -> &str;
173
174 fn class(&self) -> EquationClass;
176
177 fn citation(&self) -> Citation;
179
180 fn variables(&self) -> Vec<EquationVariable>;
182
183 fn description(&self) -> &str;
185
186 fn name(&self) -> &'static str;
188
189 fn validate_consistency(&self, values: &[(&str, f64)], tolerance: f64) -> Result<(), String>;
196}
197
198#[cfg(test)]
199mod tests {
200 use super::*;
201
202 #[test]
203 fn test_equation_variable_builder() {
204 let var = EquationVariable::new("λ", "arrival_rate", "items/hour")
205 .with_description("Rate at which items arrive to the system")
206 .with_constraints(VariableConstraints {
207 min: Some(0.0),
208 max: None,
209 positive: true,
210 integer: false,
211 });
212
213 assert_eq!(var.symbol, "λ");
214 assert_eq!(var.name, "arrival_rate");
215 assert_eq!(var.units, "items/hour");
216 assert!(!var.description.is_empty());
217 assert!(var.constraints.is_some());
218 }
219
220 #[test]
221 fn test_citation_builder() {
222 let cite = Citation::new(&["Little, J.D.C."], "Operations Research", 1961)
223 .with_title("A Proof for the Queuing Formula: L = λW")
224 .with_doi("10.1287/opre.9.3.383");
225
226 assert_eq!(cite.authors.len(), 1);
227 assert_eq!(cite.year, 1961);
228 assert!(cite.doi.is_some());
229 }
230
231 #[test]
232 fn test_equation_class_display() {
233 assert_eq!(EquationClass::Queueing.to_string(), "Queueing Theory");
234 assert_eq!(EquationClass::Conservation.to_string(), "Conservation");
235 }
236
237 #[test]
238 fn test_all_equation_classes_display() {
239 assert_eq!(
240 EquationClass::Statistical.to_string(),
241 "Statistical Mechanics"
242 );
243 assert_eq!(EquationClass::Inventory.to_string(), "Inventory Management");
244 assert_eq!(EquationClass::Optimization.to_string(), "Optimization");
245 assert_eq!(
246 EquationClass::MachineLearning.to_string(),
247 "Machine Learning"
248 );
249 }
250
251 #[test]
252 fn test_citation_display() {
253 let cite = Citation::new(&["Author A", "Author B"], "Test Journal", 2020)
254 .with_title("Test Title")
255 .with_pages("1-10");
256 let display = format!("{cite}");
257 assert!(display.contains("Author A"));
258 assert!(display.contains("Author B"));
259 assert!(display.contains("2020"));
260 assert!(display.contains("Test Title"));
261 assert!(display.contains("1-10"));
262 }
263
264 #[test]
265 fn test_citation_display_with_doi() {
266 let cite = Citation::new(&["Author"], "Journal", 2021)
267 .with_title("Title")
268 .with_doi("10.1234/test");
269 let display = format!("{cite}");
270 assert!(display.contains("doi:10.1234/test"));
271 }
272
273 #[test]
274 fn test_citation_with_pages() {
275 let cite = Citation::new(&["Test"], "Venue", 2022).with_pages("100-200");
276 assert!(cite.pages.is_some());
277 assert_eq!(cite.pages.as_deref(), Some("100-200"));
278 }
279
280 #[test]
281 fn test_variable_constraints() {
282 let constraints = VariableConstraints {
283 min: Some(-5.0),
284 max: Some(5.0),
285 positive: false,
286 integer: true,
287 };
288 assert_eq!(constraints.min, Some(-5.0));
289 assert_eq!(constraints.max, Some(5.0));
290 assert!(!constraints.positive);
291 assert!(constraints.integer);
292 }
293
294 #[test]
295 fn test_equation_variable_without_constraints() {
296 let var = EquationVariable::new("x", "variable", "units");
297 assert!(var.constraints.is_none());
298 assert!(var.description.is_empty());
299 }
300}