rust_tensors/
address_bound.rsuse crate::addressable::Addressable;
#[derive(Clone, PartialEq, Debug)]
pub struct AddressBound<A: Addressable> {
    pub smallest_possible_position: A,
    pub largest_possible_position: A,
}
impl<A: Addressable> AddressBound<A> {
    pub fn contains_address(&self, address: &A) -> bool {
        for d in 0..A::get_dimension_count() {
            if address.get_item_at_dimension_index(d)
                < self
                    .smallest_possible_position
                    .get_item_at_dimension_index(d)
                || address.get_item_at_dimension_index(d)
                    > self
                        .largest_possible_position
                        .get_item_at_dimension_index(d)
            {
                return false;
            }
        }
        true
    }
    pub fn iter(&self) -> AddressIterator<A> {
        AddressIterator {
            bounds: AddressBound::new(
                self.smallest_possible_position,
                self.largest_possible_position,
            ),
            abacus: Vec::new(),
        }
    }
    pub fn new(smallest_possible_position: A, largest_possible_position: A) -> AddressBound<A> {
        AddressBound {
            smallest_possible_position,
            largest_possible_position,
        }
    }
    pub fn index_address(&self, address: &A) -> Option<usize> {
        if !self.contains_address(&address) {
            return None;
        }
        let mut out: usize = 0;
        for d in (0..A::get_dimension_count()).rev() {
            out *= (self
                .largest_possible_position
                .get_item_at_dimension_index(d)
                - self
                    .smallest_possible_position
                    .get_item_at_dimension_index(d)
                + 1) as usize;
            out += (address.get_item_at_dimension_index(d)
                - self
                    .smallest_possible_position
                    .get_item_at_dimension_index(d)) as usize;
        }
        Some(out)
    }
    pub fn get_address_from_index(&self, index: usize) -> Result<A, &str> {
        let mut index = index;
        let mut values: Vec<i64> = Vec::new();
        for d in 0..A::get_dimension_count() {
            let min_value = *(self
                .smallest_possible_position
                .get_item_at_dimension_index(d));
            let max_value = *(self
                .largest_possible_position
                .get_item_at_dimension_index(d));
            let breadth = (max_value - min_value + 1) as usize;
            let value = (index % breadth) as i64 + min_value;
            values.push(value);
            index /= breadth;
        }
        if index != 0 {
            return Err("Index is too large.");
        }
        Ok(A::new_from_value_vec(values))
    }
}
pub struct AddressIterator<A: Addressable> {
    bounds: AddressBound<A>,
    abacus: Vec<i64>,
}
impl<A: Addressable> Iterator for AddressIterator<A> {
    type Item = A;
    fn next(&mut self) -> Option<Self::Item> {
        if self.abacus.len() == 0 {
            let dims = A::get_dimension_count();
            self.abacus = vec![0; dims as usize];
            for i in 0..dims {
                self.abacus[i as usize] = *self
                    .bounds
                    .smallest_possible_position
                    .get_item_at_dimension_index(i);
            }
            return Some(A::new_from_value_vec(self.abacus.clone()));
        }
        for dimension in 0..A::get_dimension_count() {
            if self.abacus[dimension as usize]
                >= *self
                    .bounds
                    .largest_possible_position
                    .get_item_at_dimension_index(dimension)
            {
                if dimension == A::get_dimension_count() - 1 {
                    return None;
                }
                self.abacus[dimension as usize] = *self
                    .bounds
                    .smallest_possible_position
                    .get_item_at_dimension_index(dimension);
                continue;
            } else {
                self.abacus[dimension as usize] += 1;
            }
            break;
        }
        Some(A::new_from_value_vec(self.abacus.clone()))
    }
}
#[cfg(test)]
mod tests {
    use super::*;
    use crate::matrix_address::MatrixAddress;
    use proptest::proptest;
    #[test]
    fn iteration_visual_test() {
        let bounds = AddressBound {
            smallest_possible_position: MatrixAddress { x: 50, y: 50 },
            largest_possible_position: MatrixAddress { x: 69, y: 100 },
        };
        assert!(bounds.iter().is_sorted());
    }
    proptest! {
        #[test]
        fn indexing_test(x1 in 0i64..1000, y1 in 0i64..1000, x2 in 0i64..1000, y2 in 0i64..1000) {
            if x2 < x1 || y2 < y1 {
                return Ok(());
            }
            let bounds = AddressBound {
                smallest_possible_position: MatrixAddress { x: x1, y: y1 },
                largest_possible_position: MatrixAddress { x: x2, y: y2 },
            };
            bounds.iter()
            .enumerate()
            .for_each(|(index, address)| {
                assert_eq!(bounds.index_address(&address).unwrap(), ((address.y - y1) * (x2-x1 + 1) + (address.x - x1)) as usize);
                assert_eq!(bounds.get_address_from_index(index).expect("Index out of bounds"), address);
            });
        }
        #[test]
        fn address_iteration_test(x1 in 0i64..1000, y1 in 0i64..1000, x2 in 0i64..1000, y2 in 0i64..1000) {
            if x2 < x1 || y2 < y1 {
                return Ok(());
            }
            let bounds = AddressBound {
                smallest_possible_position: MatrixAddress { x: x1, y: y1 },
                largest_possible_position: MatrixAddress { x: x2, y: y2 },
            };
            bounds.iter().for_each(|address| assert!(bounds.contains_address(&address)));
            assert_eq!((x2 - x1 + 1) * (y2 - y1 + 1), bounds.iter().collect::<Vec<_>>().len().try_into().unwrap())
        }
        #[test]
        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) {
           if x2 < x1 || y2 < y1 {
                return Ok(());
            }
            let bounds = AddressBound {
                smallest_possible_position: MatrixAddress { x: x1, y: y1 },
                largest_possible_position: MatrixAddress { x: x2, y: y2 },
            };
            assert_eq!(bounds.contains_address(&MatrixAddress{x: x3,y: y3}), x3 >= x1 && x3 <= x2 && y3 >= y1 && y3 <= y2);
        }
    }
}