rust_tensors/
address_bound.rs

1use crate::addressable::Addressable;
2
3#[derive(Clone, PartialEq, Debug)]
4pub struct AddressBound<A: Addressable> {
5    pub smallest_possible_position: A,
6    pub largest_possible_position: A,
7}
8
9impl<A: Addressable> AddressBound<A> {
10    pub fn contains_address(&self, address: &A) -> bool {
11        for d in 0..A::get_dimension_count() {
12            if address.get_item_at_dimension_index(d)
13                < self
14                    .smallest_possible_position
15                    .get_item_at_dimension_index(d)
16                || address.get_item_at_dimension_index(d)
17                    > self
18                        .largest_possible_position
19                        .get_item_at_dimension_index(d)
20            {
21                return false;
22            }
23        }
24        true
25    }
26
27    pub fn iter(&self) -> AddressIterator<A> {
28        AddressIterator {
29            bounds: AddressBound::new(
30                self.smallest_possible_position,
31                self.largest_possible_position,
32            ),
33            abacus: Vec::new(),
34        }
35    }
36
37    pub fn new(smallest_possible_position: A, largest_possible_position: A) -> AddressBound<A> {
38        AddressBound {
39            smallest_possible_position,
40            largest_possible_position,
41        }
42    }
43
44    pub fn index_address(&self, address: &A) -> Option<usize> {
45        if !self.contains_address(&address) {
46            return None;
47        }
48        let mut out: usize = 0;
49
50        for d in (0..A::get_dimension_count()).rev() {
51            out *= (self
52                .largest_possible_position
53                .get_item_at_dimension_index(d)
54                - self
55                    .smallest_possible_position
56                    .get_item_at_dimension_index(d)
57                + 1) as usize;
58            out += (address.get_item_at_dimension_index(d)
59                - self
60                    .smallest_possible_position
61                    .get_item_at_dimension_index(d)) as usize;
62        }
63
64        Some(out)
65    }
66    pub fn get_address_from_index(&self, index: usize) -> Result<A, &str> {
67        let mut index = index;
68        let mut values: Vec<i64> = Vec::new();
69        for d in 0..A::get_dimension_count() {
70            let min_value = *(self
71                .smallest_possible_position
72                .get_item_at_dimension_index(d));
73            let max_value = *(self
74                .largest_possible_position
75                .get_item_at_dimension_index(d));
76            let breadth = (max_value - min_value + 1) as usize;
77            let value = (index % breadth) as i64 + min_value;
78            values.push(value);
79            index /= breadth;
80        }
81        if index != 0 {
82            return Err("Index is too large.");
83        }
84        Ok(A::new_from_value_vec(values))
85    }
86}
87
88pub struct AddressIterator<A: Addressable> {
89    bounds: AddressBound<A>,
90    abacus: Vec<i64>,
91}
92
93impl<A: Addressable> Iterator for AddressIterator<A> {
94    type Item = A;
95
96    fn next(&mut self) -> Option<Self::Item> {
97        if self.abacus.len() == 0 {
98            let dims = A::get_dimension_count();
99            self.abacus = vec![0; dims as usize];
100            for i in 0..dims {
101                self.abacus[i as usize] = *self
102                    .bounds
103                    .smallest_possible_position
104                    .get_item_at_dimension_index(i);
105            }
106            return Some(A::new_from_value_vec(self.abacus.clone()));
107        }
108        for dimension in 0..A::get_dimension_count() {
109            if self.abacus[dimension as usize]
110                >= *self
111                    .bounds
112                    .largest_possible_position
113                    .get_item_at_dimension_index(dimension)
114            {
115                if dimension == A::get_dimension_count() - 1 {
116                    return None;
117                }
118                self.abacus[dimension as usize] = *self
119                    .bounds
120                    .smallest_possible_position
121                    .get_item_at_dimension_index(dimension);
122                continue;
123            } else {
124                self.abacus[dimension as usize] += 1;
125            }
126            break;
127        }
128        Some(A::new_from_value_vec(self.abacus.clone()))
129    }
130}
131
132#[cfg(test)]
133mod tests {
134    use super::*;
135    use crate::matrix_address::MatrixAddress;
136    use proptest::proptest;
137
138    #[test]
139    fn iteration_visual_test() {
140        let bounds = AddressBound {
141            smallest_possible_position: MatrixAddress { x: 50, y: 50 },
142            largest_possible_position: MatrixAddress { x: 69, y: 100 },
143        };
144        assert!(bounds.iter().is_sorted());
145    }
146    proptest! {
147        #[test]
148        fn indexing_test(x1 in 0i64..1000, y1 in 0i64..1000, x2 in 0i64..1000, y2 in 0i64..1000) {
149            if x2 < x1 || y2 < y1 {
150                return Ok(());
151            }
152            let bounds = AddressBound {
153                smallest_possible_position: MatrixAddress { x: x1, y: y1 },
154                largest_possible_position: MatrixAddress { x: x2, y: y2 },
155            };
156            bounds.iter()
157            .enumerate()
158            .for_each(|(index, address)| {
159                assert_eq!(bounds.index_address(&address).unwrap(), ((address.y - y1) * (x2-x1 + 1) + (address.x - x1)) as usize);
160                assert_eq!(bounds.get_address_from_index(index).expect("Index out of bounds"), address);
161            });
162        }
163        #[test]
164        fn address_iteration_test(x1 in 0i64..1000, y1 in 0i64..1000, x2 in 0i64..1000, y2 in 0i64..1000) {
165            if x2 < x1 || y2 < y1 {
166                return Ok(());
167            }
168            let bounds = AddressBound {
169                smallest_possible_position: MatrixAddress { x: x1, y: y1 },
170                largest_possible_position: MatrixAddress { x: x2, y: y2 },
171            };
172            bounds.iter().for_each(|address| assert!(bounds.contains_address(&address)));
173            assert_eq!((x2 - x1 + 1) * (y2 - y1 + 1), bounds.iter().collect::<Vec<_>>().len().try_into().unwrap())
174        }
175        #[test]
176        fn contains_test(x1 in 0i64..1000, y1 in 0i64..1000, x2 in 0i64..1000, y2 in 0i64..1000, x3 in 0i64..1000, y3 in 0i64..1000) {
177           if x2 < x1 || y2 < y1 {
178                return Ok(());
179            }
180            let bounds = AddressBound {
181                smallest_possible_position: MatrixAddress { x: x1, y: y1 },
182                largest_possible_position: MatrixAddress { x: x2, y: y2 },
183            };
184            assert_eq!(bounds.contains_address(&MatrixAddress{x: x3,y: y3}), x3 >= x1 && x3 <= x2 && y3 >= y1 && y3 <= y2);
185        }
186    }
187}