1use crate::{UtilsError, UtilsResult};
7use scirs2_core::ndarray::{Array1, Array2};
8use std::marker::PhantomData;
9
10pub struct Untrained;
14
15pub struct Trained;
17
18pub struct Validated;
20
21pub struct Unvalidated;
23
24#[derive(Debug, Clone)]
26pub struct ModelState<T, State> {
27 pub inner: T,
28 _state: PhantomData<State>,
29}
30
31impl<T> ModelState<T, Untrained> {
32 pub fn new(inner: T) -> Self {
34 Self {
35 inner,
36 _state: PhantomData,
37 }
38 }
39
40 pub fn train(self) -> ModelState<T, Trained> {
42 ModelState {
43 inner: self.inner,
44 _state: PhantomData,
45 }
46 }
47}
48
49impl<T> ModelState<T, Trained> {
50 pub fn predict<F, Input, Output>(&self, predict_fn: F, input: Input) -> Output
52 where
53 F: Fn(&T, Input) -> Output,
54 {
55 predict_fn(&self.inner, input)
56 }
57
58 pub fn reset(self) -> ModelState<T, Untrained> {
60 ModelState {
61 inner: self.inner,
62 _state: PhantomData,
63 }
64 }
65}
66
67#[derive(Debug, Clone)]
71pub struct DataState<T, State> {
72 pub data: T,
73 _state: PhantomData<State>,
74}
75
76impl<T> DataState<T, Unvalidated> {
77 pub fn new(data: T) -> Self {
79 Self {
80 data,
81 _state: PhantomData,
82 }
83 }
84
85 pub fn validate<F>(self, validator: F) -> UtilsResult<DataState<T, Validated>>
87 where
88 F: FnOnce(&T) -> UtilsResult<()>,
89 {
90 validator(&self.data)?;
91 Ok(DataState {
92 data: self.data,
93 _state: PhantomData,
94 })
95 }
96}
97
98impl<T> DataState<T, Validated> {
99 pub fn as_validated(&self) -> &T {
101 &self.data
102 }
103
104 pub fn map<U, F>(self, transform: F) -> DataState<U, Validated>
106 where
107 F: FnOnce(T) -> U,
108 {
109 DataState {
110 data: transform(self.data),
111 _state: PhantomData,
112 }
113 }
114}
115
116pub struct D1;
120pub struct D2;
121pub struct D3;
122
123#[derive(Debug, Clone)]
125pub struct TypedArray<T, D> {
126 data: T,
127 _dimension: PhantomData<D>,
128}
129
130impl<T> TypedArray<Array1<T>, D1> {
131 pub fn new_1d(array: Array1<T>) -> Self {
133 Self {
134 data: array,
135 _dimension: PhantomData,
136 }
137 }
138
139 pub fn as_array1(&self) -> &Array1<T> {
141 &self.data
142 }
143
144 pub fn to_2d(self) -> TypedArray<Array2<T>, D2>
146 where
147 T: Clone,
148 {
149 let shape = (1, self.data.len());
150 let data =
151 Array2::from_shape_vec(shape, self.data.to_vec()).expect("operation should succeed");
152 TypedArray {
153 data,
154 _dimension: PhantomData,
155 }
156 }
157}
158
159impl<T> TypedArray<Array2<T>, D2> {
160 pub fn new_2d(array: Array2<T>) -> Self {
162 Self {
163 data: array,
164 _dimension: PhantomData,
165 }
166 }
167
168 pub fn as_array2(&self) -> &Array2<T> {
170 &self.data
171 }
172
173 pub fn shape(&self) -> (usize, usize) {
175 let shape = self.data.shape();
176 (shape[0], shape[1])
177 }
178
179 pub fn flatten(self) -> TypedArray<Array1<T>, D1>
181 where
182 T: Clone,
183 {
184 let (vec, offset) = self.data.into_raw_vec_and_offset();
185 assert_eq!(offset, Some(0), "Array offset must be zero for conversion");
186 let data = Array1::from_vec(vec);
187 TypedArray {
188 data,
189 _dimension: PhantomData,
190 }
191 }
192}
193
194pub trait Unit: 'static {
198 const NAME: &'static str;
199}
200
201pub struct Meters;
202pub struct Seconds;
203pub struct Kilograms;
204pub struct Pixels;
205
206impl Unit for Meters {
207 const NAME: &'static str = "meters";
208}
209
210impl Unit for Seconds {
211 const NAME: &'static str = "seconds";
212}
213
214impl Unit for Kilograms {
215 const NAME: &'static str = "kilograms";
216}
217
218impl Unit for Pixels {
219 const NAME: &'static str = "pixels";
220}
221
222#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
224pub struct Measurement<T, U: Unit> {
225 value: T,
226 _unit: PhantomData<U>,
227}
228
229impl<T, U: Unit> Measurement<T, U> {
230 pub fn new(value: T) -> Self {
232 Self {
233 value,
234 _unit: PhantomData,
235 }
236 }
237
238 pub fn value(&self) -> &T {
240 &self.value
241 }
242
243 pub unsafe fn convert_unit<V: Unit>(self) -> Measurement<T, V> {
250 Measurement {
251 value: self.value,
252 _unit: PhantomData,
253 }
254 }
255}
256
257impl<T, U: Unit> std::ops::Add for Measurement<T, U>
258where
259 T: std::ops::Add<Output = T>,
260{
261 type Output = Self;
262
263 fn add(self, other: Self) -> Self::Output {
264 Self {
265 value: self.value + other.value,
266 _unit: PhantomData,
267 }
268 }
269}
270
271impl<T, U: Unit> std::ops::Sub for Measurement<T, U>
272where
273 T: std::ops::Sub<Output = T>,
274{
275 type Output = Self;
276
277 fn sub(self, other: Self) -> Self::Output {
278 Self {
279 value: self.value - other.value,
280 _unit: PhantomData,
281 }
282 }
283}
284
285pub trait ShapeValidation {
289 type Shape;
290 fn validate_shape(shape: Self::Shape) -> bool;
291}
292
293pub struct ExactSize<const N: usize>;
295
296impl<const N: usize> ShapeValidation for ExactSize<N> {
297 type Shape = usize;
298
299 fn validate_shape(shape: Self::Shape) -> bool {
300 shape == N
301 }
302}
303
304pub struct MinSize<const N: usize>;
306
307impl<const N: usize> ShapeValidation for MinSize<N> {
308 type Shape = usize;
309
310 fn validate_shape(shape: Self::Shape) -> bool {
311 shape >= N
312 }
313}
314
315pub struct ValidatedArray<T, V: ShapeValidation> {
317 data: Array1<T>,
318 _validator: PhantomData<V>,
319}
320
321impl<T, V: ShapeValidation<Shape = usize>> ValidatedArray<T, V> {
322 pub fn new(data: Array1<T>) -> Option<Self> {
324 if V::validate_shape(data.len()) {
325 Some(Self {
326 data,
327 _validator: PhantomData,
328 })
329 } else {
330 None
331 }
332 }
333
334 pub fn data(&self) -> &Array1<T> {
336 &self.data
337 }
338}
339
340#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
344pub struct Normalized<T>(T);
345
346impl<T> Normalized<T> {
347 pub unsafe fn new_unchecked(value: T) -> Self {
354 Self(value)
355 }
356
357 pub fn get(self) -> T {
359 self.0
360 }
361}
362
363impl Normalized<f64> {
364 pub fn new(value: f64) -> UtilsResult<Self> {
366 if (0.0..=1.0).contains(&value) {
367 Ok(Self(value))
368 } else {
369 Err(UtilsError::InvalidParameter(format!(
370 "Value {value} is not in range [0, 1]"
371 )))
372 }
373 }
374
375 pub fn clamp(value: f64) -> Self {
377 Self(value.clamp(0.0, 1.0))
378 }
379}
380
381#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
383pub struct Positive<T>(T);
384
385impl<T> Positive<T> {
386 pub fn get(self) -> T {
388 self.0
389 }
390}
391
392impl Positive<f64> {
393 pub fn new(value: f64) -> UtilsResult<Self> {
395 if value > 0.0 {
396 Ok(Self(value))
397 } else {
398 Err(UtilsError::InvalidParameter(format!(
399 "Value {value} is not positive"
400 )))
401 }
402 }
403}
404
405#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
407pub struct NonNegative<T>(T);
408
409impl<T> NonNegative<T> {
410 pub fn get(self) -> T {
412 self.0
413 }
414}
415
416impl NonNegative<f64> {
417 pub fn new(value: f64) -> UtilsResult<Self> {
419 if value >= 0.0 {
420 Ok(Self(value))
421 } else {
422 Err(UtilsError::InvalidParameter(format!(
423 "Value {value} is negative"
424 )))
425 }
426 }
427}
428
429#[macro_export]
433macro_rules! const_assert {
434 ($condition:expr) => {
435 const _: () = if !$condition {
436 panic!("Compile-time assertion failed");
437 } else {
438 ()
439 };
440 };
441}
442
443#[macro_export]
445macro_rules! assert_shape {
446 ($array:expr, $expected:expr) => {
447 if $array.shape() != $expected {
448 return Err(UtilsError::ShapeMismatch {
449 expected: $expected.to_vec(),
450 actual: $array.shape().to_vec(),
451 });
452 }
453 };
454}
455
456pub trait TypeNum {
460 const VALUE: usize;
461}
462
463pub struct Zero;
464pub struct One;
465pub struct Two;
466pub struct Three;
467
468impl TypeNum for Zero {
469 const VALUE: usize = 0;
470}
471impl TypeNum for One {
472 const VALUE: usize = 1;
473}
474impl TypeNum for Two {
475 const VALUE: usize = 2;
476}
477impl TypeNum for Three {
478 const VALUE: usize = 3;
479}
480
481pub trait Add<Rhs> {
483 type Output: TypeNum;
484}
485
486impl Add<Zero> for Zero {
487 type Output = Zero;
488}
489impl Add<One> for Zero {
490 type Output = One;
491}
492impl Add<Two> for Zero {
493 type Output = Two;
494}
495impl Add<Zero> for One {
496 type Output = One;
497}
498impl Add<One> for One {
499 type Output = Two;
500}
501impl Add<Two> for One {
502 type Output = Three;
503}
504
505pub struct MatrixMul<L: TypeNum, M: TypeNum, N: TypeNum> {
507 _phantom: PhantomData<(L, M, N)>,
508}
509
510impl<L: TypeNum, M: TypeNum, N: TypeNum> MatrixMul<L, M, N> {
511 pub fn multiply(left: &Array2<f64>, right: &Array2<f64>) -> UtilsResult<Array2<f64>> {
513 let left_shape = left.shape();
515 let right_shape = right.shape();
516
517 if left_shape[1] != right_shape[0] {
518 return Err(UtilsError::ShapeMismatch {
519 expected: vec![left_shape[0], right_shape[1]],
520 actual: vec![left_shape[0], left_shape[1], right_shape[0], right_shape[1]],
521 });
522 }
523
524 Ok(left.dot(right))
525 }
526}
527
528#[allow(non_snake_case)]
529#[cfg(test)]
530mod tests {
531 use super::*;
532
533 #[test]
534 fn test_model_state_transitions() {
535 #[derive(Debug, Clone)]
536 struct MockModel {
537 value: i32,
538 }
539
540 let model = MockModel { value: 42 };
541 let untrained = ModelState::new(model);
542
543 let trained = untrained.train();
545
546 let result = trained.predict(|model, input: i32| model.value + input, 10);
548 assert_eq!(result, 52);
549
550 let _reset = trained.reset();
552 }
553
554 #[test]
555 fn test_data_validation() {
556 let data = vec![1, 2, 3, 4, 5];
557 let unvalidated = DataState::new(data);
558
559 let validated = unvalidated
561 .validate(|data| {
562 if data.iter().all(|&x| x > 0) {
563 Ok(())
564 } else {
565 Err(UtilsError::InvalidParameter(
566 "Negative values found".to_string(),
567 ))
568 }
569 })
570 .expect("operation should succeed");
571
572 let validated_data = validated.as_validated();
574 assert_eq!(validated_data.len(), 5);
575
576 let transformed = validated.map(|data| data.len());
578 assert_eq!(*transformed.as_validated(), 5);
579 }
580
581 #[test]
582 fn test_typed_arrays() {
583 let array1d = Array1::from_vec(vec![1.0, 2.0, 3.0]);
584 let typed1d = TypedArray::new_1d(array1d);
585
586 let typed2d = typed1d.to_2d();
588 assert_eq!(typed2d.shape(), (1, 3));
589
590 let flattened = typed2d.flatten();
592 assert_eq!(flattened.as_array1().len(), 3);
593 }
594
595 #[test]
596 fn test_measurements() {
597 let distance1 = Measurement::<f64, Meters>::new(10.0);
598 let distance2 = Measurement::<f64, Meters>::new(5.0);
599
600 let total_distance = distance1 + distance2;
601 assert_eq!(*total_distance.value(), 15.0);
602
603 let _time = Measurement::<f64, Seconds>::new(2.0);
604 }
606
607 #[test]
608 fn test_normalized_values() {
609 let norm1 = Normalized::new(0.5).expect("operation should succeed");
611 assert_eq!(norm1.get(), 0.5);
612
613 assert!(Normalized::new(1.5).is_err());
615
616 let norm2 = Normalized::clamp(1.5);
618 assert_eq!(norm2.get(), 1.0);
619 }
620
621 #[test]
622 fn test_positive_values() {
623 let pos = Positive::new(5.0).expect("operation should succeed");
624 assert_eq!(pos.get(), 5.0);
625
626 assert!(Positive::new(-1.0).is_err());
627 assert!(Positive::new(0.0).is_err());
628 }
629
630 #[test]
631 fn test_validated_arrays() {
632 let data = Array1::from_vec(vec![1, 2, 3]);
633
634 let validated = ValidatedArray::<i32, ExactSize<3>>::new(data.clone());
636 assert!(validated.is_some());
637
638 let validated = ValidatedArray::<i32, ExactSize<5>>::new(data.clone());
640 assert!(validated.is_none());
641
642 let validated = ValidatedArray::<i32, MinSize<2>>::new(data);
644 assert!(validated.is_some());
645 }
646
647 #[test]
648 fn test_matrix_multiplication_validation() {
649 let left = Array2::from_shape_vec((2, 3), vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0])
650 .expect("operation should succeed");
651 let right = Array2::from_shape_vec((3, 2), vec![7.0, 8.0, 9.0, 10.0, 11.0, 12.0])
652 .expect("operation should succeed");
653
654 let result = MatrixMul::<Two, Three, Two>::multiply(&left, &right)
655 .expect("operation should succeed");
656 assert_eq!(result.shape(), &[2, 2]);
657
658 let wrong_right = Array2::from_shape_vec((2, 2), vec![1.0, 2.0, 3.0, 4.0])
660 .expect("operation should succeed");
661 assert!(MatrixMul::<Two, Three, Two>::multiply(&left, &wrong_right).is_err());
662 }
663}