ebi_arithmetic/matrix/
fraction_matrix.rs

1//======================== set type alias based on compile flags ========================//
2/// The fraction matrix is a matrix of fractions.
3/// It applies two strategies to potentially save time for exact arithmetic matrices:
4/// - it postpones reduction of fractions to the moment of export, or a user-chosen moment.
5/// - it attempts to store values in primitives rather than BigUints at each reduction.
6#[cfg(any(
7    all(
8        not(feature = "exactarithmetic"),
9        not(feature = "approximatearithmetic")
10    ),
11    all(feature = "exactarithmetic", feature = "approximatearithmetic")
12))]
13pub type FractionMatrix = super::fraction_matrix_enum::FractionMatrixEnum;
14
15#[cfg(all(not(feature = "exactarithmetic"), feature = "approximatearithmetic"))]
16pub type FractionMatrix = super::fraction_matrix_f64::FractionMatrixF64;
17
18#[cfg(all(feature = "exactarithmetic", not(feature = "approximatearithmetic")))]
19pub type FractionMatrix = super::fraction_matrix_exact::FractionMatrixExact;
20
21//======================== common code ========================//
22
23#[macro_export]
24macro_rules! push_columns {
25    ($zero:expr, $number_of_columns_to_add:expr, $values:expr, $number_of_rows:expr, $number_of_columns:expr) => {
26        for row in (0..$number_of_rows).rev() {
27            $values.splice(
28                row * $number_of_columns + $number_of_columns
29                    ..row * $number_of_columns + $number_of_columns,
30                vec![$zero; $number_of_columns_to_add],
31            );
32        }
33    };
34}
35
36#[macro_export]
37macro_rules! pop_front_columns {
38    ($number_of_columns_to_remove:expr, $values:expr, $number_of_rows:expr, $number_of_columns:expr) => {
39        for row in (0..$number_of_rows).rev() {
40            $values.drain(
41                row * $number_of_columns..row * $number_of_columns + $number_of_columns_to_remove,
42            );
43        }
44    };
45}
46
47//======================== tests ========================//
48#[cfg(test)]
49mod tests {
50
51    use crate::{
52        Inversion,
53        Zero,
54        ebi_matrix::EbiMatrix,
55        f, f0,
56        fraction::fraction::Fraction,
57        matrix::fraction_matrix::FractionMatrix,
58    };
59
60    #[test]
61    fn fraction_matrix() {
62        let m: FractionMatrix = vec![vec![f!(1, 4), f!(2, 5), f!(8, 3)]].try_into().unwrap();
63        assert_eq!(m, m);
64    }
65
66    #[test]
67    fn fraction_matrix_empty() {
68        let m = vec![vec![]];
69        let m: FractionMatrix = m.try_into().unwrap();
70        assert_eq!(m.number_of_rows(), 1);
71        assert_eq!(m.number_of_columns(), 0);
72    }
73
74    #[test]
75    fn fraction_matrix_get() {
76        let m: FractionMatrix = vec![vec![f!(1, 4), f!(2, 5), f!(8, 3)]].try_into().unwrap();
77
78        assert!(m.get(10, 10).is_none());
79
80        assert_eq!(m.get(0, 0).unwrap(), f!(1, 4));
81    }
82
83    #[test]
84    #[should_panic]
85    fn fraction_matrix_incomplete() {
86        let _: FractionMatrix = vec![vec![f!(1, 4), f!(2, 5)], vec![f!(8, 3)]]
87            .try_into()
88            .unwrap();
89    }
90
91    #[test]
92    fn fraction_matrix_pop_front() {
93        let mut m1: FractionMatrix = vec![vec![f!(1, 4), f!(2, 5), f!(8, 3)]].try_into().unwrap();
94
95        m1.pop_front_columns(1);
96
97        let m3: FractionMatrix = vec![vec![f!(2, 5), f!(8, 3)]].try_into().unwrap();
98
99        // println!("{:?}", m1);
100        // println!("{:?}", m3);
101
102        assert_eq!(m1, m3);
103    }
104
105    #[test]
106    fn fraction_matrix_push_columns() {
107        let mut m1: FractionMatrix = vec![vec![f!(1, 4), f!(2, 5), f!(8, 3)]].try_into().unwrap();
108
109        m1.push_columns(1);
110
111        let m3: FractionMatrix = vec![vec![f!(1, 4), f!(2, 5), f!(8, 3), f0!()]]
112            .try_into()
113            .unwrap();
114
115        // println!("{:?}", m1);
116        // println!("{:?}", m3);
117
118        assert_eq!(m1, m3);
119    }
120
121    #[test]
122    fn fraction_matrix_inverse() {
123        let mut m1: FractionMatrix = vec![
124            vec![1.into(), 0.into(), 0.into(), 0.into()],
125            vec![0.into(), 1.into(), 0.into(), Fraction::from((-3, 5))],
126            vec![0.into(), Fraction::from((-3, 4)), 1.into(), 0.into()],
127            vec![0.into(), 0.into(), 0.into(), 1.into()],
128        ]
129        .try_into()
130        .unwrap();
131
132        let mut m2: FractionMatrix = vec![
133            vec![1.into(), 0.into(), 0.into(), 0.into()],
134            vec![0.into(), 1.into(), 0.into(), Fraction::from((3, 5))],
135            vec![
136                0.into(),
137                Fraction::from((3, 4)),
138                1.into(),
139                Fraction::from((9, 20)),
140            ],
141            vec![0.into(), 0.into(), 0.into(), 1.into()],
142        ]
143        .try_into()
144        .unwrap();
145
146        m1 = m1.invert().unwrap();
147
148        println!("{}", m1);
149        println!("{}", m2);
150
151        assert!(m1.eq(&mut m2));
152    }
153
154    #[test]
155    fn display_empty() {
156        let m = FractionMatrix::new(0, 0);
157        let _ = format!("{}", m);
158
159        let m = FractionMatrix::new(1, 0);
160        let _ = format!("{}", m);
161
162        let m = FractionMatrix::new(0, 1);
163        let _ = format!("{}", m);
164    }
165
166    #[test]
167    fn to_vec_empty() {
168        let m = FractionMatrix::new(0, 0);
169        let u: Vec<Vec<Fraction>> = vec![];
170        assert_eq!(m.to_vec(), u);
171
172        let m = FractionMatrix::new(1, 0);
173        let u: Vec<Vec<Fraction>> = vec![vec![]];
174        assert_eq!(m.to_vec(), u);
175
176        let m = FractionMatrix::new(0, 1);
177        let u: Vec<Vec<Fraction>> = vec![];
178        assert_eq!(m.to_vec(), u);
179    }
180}