1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
//! Food delivery system.

use crate::xor_distance::XorDistance;
use num_traits::{PrimInt, Unsigned};

/// Food delivery system of local food from from local farms.
///
/// # Examples
/// ```
/// extern crate xor_distance_exercise;
///
/// use xor_distance_exercise::delivery_system::FoodDeliverySystem;
///
/// let delivery_system: FoodDeliverySystem<u64> = FoodDeliverySystem::new(vec![
///     0, 1, 2, 4, 6, 8, 12, 18, 19, 20, 21, 22, 406, 407, 408, 409, 410, 444, 445,
/// ]);
///
/// let position = 200;
/// let count = 10;
///
/// // Get closest farms and reversed guess of possible customer's `position`.
/// let closest_farms = delivery_system.closest_farms(position, count);
/// let position_guess = delivery_system.reverse_closest_farms(&closest_farms).unwrap();
/// ```
pub struct FoodDeliverySystem<T: PrimInt + Unsigned> {
    xor_distance: XorDistance<T>,
}

impl<T: PrimInt + Unsigned> FoodDeliverySystem<T> {
    pub fn new(points: Vec<T>) -> Self {
        let xor_distance = XorDistance::new(points);

        Self { xor_distance }
    }

    /// Return specified count of closest farms to the provided `position`.
    ///
    /// The closest farms are ordered from the closest to the n-th closest, where `n` is the count.
    ///
    /// # Examples
    /// ```
    /// extern crate xor_distance_exercise;
    ///
    /// use xor_distance_exercise::delivery_system::FoodDeliverySystem;
    ///
    /// let delivery_system: FoodDeliverySystem<u64> = FoodDeliverySystem::new(vec![
    ///     0, 1, 2, 4, 6, 8, 12, 18, 19, 20, 21, 22, 406, 407, 408, 409, 410, 444, 445,
    /// ]);
    ///
    /// let position = 10;
    /// let count = 10;
    ///
    /// let closest_farms = delivery_system.closest_farms(position, count);
    /// ```
    pub fn closest_farms(&self, position: T, count: usize) -> Vec<T> {
        self.xor_distance.closest(position, count)
    }

    /// Return a `Some(position)` such that `self.closest(position)` equals closest_farms and return
    /// None in case such a `position` does not exists.
    ///
    /// # Examples
    /// ```
    /// extern crate xor_distance_exercise;
    ///
    /// use xor_distance_exercise::delivery_system::FoodDeliverySystem;
    ///
    /// let delivery_system: FoodDeliverySystem<u64> = FoodDeliverySystem::new(vec![
    ///     0, 1, 2, 4, 6, 8, 12, 18, 19, 20, 21, 22, 406, 407, 408, 409, 410, 444, 445,
    /// ]);
    ///
    /// let position = 200;
    /// let count = 10;
    ///
    /// // Get closest farms and reversed guess of possible customer's `position`.
    /// let closest_farms = delivery_system.closest_farms(position, count);
    /// let position_guess = delivery_system.reverse_closest_farms(&closest_farms).unwrap();
    ///
    /// // Check that both `position` and `position_guess` produce the same result.
    /// assert_eq!(closest_farms, delivery_system.closest_farms(position_guess, count));
    /// ```
    pub fn reverse_closest_farms(&self, closest_farms: &[T]) -> Option<T> {
        self.xor_distance.reverse_closest(closest_farms)
    }
}

#[cfg(test)]
mod tests {
    //! FoodDeliverySystem struct mirrors the XorDistance struct mostly and gives an opportunity to
    //! add in more food delivery system specific functionality.
    //!
    //! There are a few simple tests mirroring some XorDistance tests and additional complementary
    //! random tests.

    use super::FoodDeliverySystem;
    use rand::distributions::Standard;
    use rand::prelude::*;
    use rand::{self, Rng};

    #[test]
    fn closest_farms() {
        let delivery_system: FoodDeliverySystem<u64> = FoodDeliverySystem::new(vec![
            0, 1, 2, 4, 6, 8, 12, 18, 19, 20, 21, 22, 406, 407, 408, 409, 410, 444, 445,
        ]);

        let result = delivery_system.closest_farms(10, 10);
        let expected = vec![8, 12, 2, 0, 1, 6, 4, 18, 19, 22];

        assert_eq!(expected, result);
    }

    #[test]
    fn reverse_closest_farms() {
        let delivery_system: FoodDeliverySystem<u64> = FoodDeliverySystem::new(vec![
            0, 1, 2, 4, 6, 8, 12, 18, 19, 20, 21, 22, 406, 407, 408, 409, 410, 444, 445,
        ]);

        let position = 200;
        let count = 10;

        // Get closest farms and reversed guess of possible customer's `position`.
        let closest_farms = delivery_system.closest_farms(position, count);
        let position_guess = delivery_system
            .reverse_closest_farms(&closest_farms)
            .expect("The FoodDeliverySystem::reverse_closest_farms() should return a Some(position), but None returned instead!");

        // Check that both `position` and `position_guess` produce the same result.
        assert_eq!(
            closest_farms,
            delivery_system.closest_farms(position_guess, count)
        );
    }

    #[test]
    fn reverse_closest_farms_random_position() {
        // Get 2000 random numbers.
        let mut rng = rand::thread_rng();
        let points: Vec<u64> = rng.sample_iter(&Standard).take(2000).collect();

        let delivery_system = FoodDeliverySystem::new(points);

        for _ in 0..100 {
            let position = rng.gen();
            let closest_points = delivery_system.closest_farms(position, 10);
            let guess_pos = delivery_system
                .reverse_closest_farms(&closest_points)
                .expect("The FoodDeliverySystem::reverse_closest_farms() should return a Some(position), but None returned instead!");

            assert_eq!(closest_points, delivery_system.closest_farms(guess_pos, 10));
        }
    }

    #[test]
    fn reverse_closest_farms_random_set() {
        // Get 2000 random numbers.
        let mut rng = rand::thread_rng();
        let points: Vec<u64> = rng.sample_iter(&Standard).take(200).collect();

        let delivery_system = FoodDeliverySystem::new(points.clone());

        // Try hundred random closest points collections.
        for _ in 0..100 {
            let closest_points: Vec<u64> = points
                .iter()
                // Returns `Vec<&u64>` and thus we need to map it to `Vec<u64>`.
                .choose_multiple(&mut rng, 10)
                .iter()
                .map(|&&x| x)
                .collect();

            // Most of the time the generated closest points will be invalid, as they are selected
            // randomly and required relations/inequalities are not satisfied.
            if let Some(guess_pos) = delivery_system.reverse_closest_farms(&closest_points) {
                assert_eq!(closest_points, delivery_system.closest_farms(guess_pos, 10));
            }
        }
    }

}