use crate::dataset::Monotonicity;
use crate::dtype::DType;

/// In-memory index for coordinate values along a single dimension.
///
/// Stores all coordinate values in a type-erased byte buffer and provides
/// label→index lookups. When coordinates are monotonic, lookups use binary
/// search (O(log n)); otherwise they fall back to linear scan (O(n)).
///
/// Typical sizes are small enough to fit entirely in memory:
/// - ERA5 latitude: 721 × 8B = ~6 KB
/// - ERA5 longitude: 1440 × 8B = ~12 KB
/// - ERA5 time (hourly, 150 years): 1.3M × 8B = ~10 MB
#[derive(Debug, Clone)]
pub struct CoordinateIndex {
    pub dim_name: String,
    pub dtype: DType,
    pub monotonicity: Monotonicity,
    values: Vec<u8>,
    element_size: usize,
    len: usize,
}

impl CoordinateIndex {
    /// Creates a coordinate index from f64 values.
    pub fn from_f64(dim_name: &str, values: &[f64], mono: Monotonicity) -> Self {
        let bytes: Vec<u8> = values.iter().flat_map(|v| v.to_le_bytes()).collect();
        Self {
            dim_name: dim_name.to_string(),
            dtype: DType::Float64,
            monotonicity: mono,
            values: bytes,
            element_size: 8,
            len: values.len(),
        }
    }

    /// Creates a coordinate index from i64 values (typically timestamps).
    pub fn from_i64(dim_name: &str, values: &[i64], mono: Monotonicity) -> Self {
        let bytes: Vec<u8> = values.iter().flat_map(|v| v.to_le_bytes()).collect();
        Self {
            dim_name: dim_name.to_string(),
            dtype: DType::Int64,
            monotonicity: mono,
            values: bytes,
            element_size: 8,
            len: values.len(),
        }
    }

    /// Number of coordinate values.
    pub fn len(&self) -> usize {
        self.len
    }

    /// Whether the index is empty.
    pub fn is_empty(&self) -> bool {
        self.len == 0
    }

    /// Get the f64 value at a given index position (public).
    pub fn value_f64(&self, idx: usize) -> f64 {
        debug_assert!(idx < self.len);
        let offset = idx * self.element_size;
        let bytes: [u8; 8] = self.values[offset..offset + 8].try_into().unwrap();
        f64::from_le_bytes(bytes)
    }

    /// Get the i64 value at a given index position (public).
    pub fn value_i64(&self, idx: usize) -> i64 {
        debug_assert!(idx < self.len);
        let offset = idx * self.element_size;
        let bytes: [u8; 8] = self.values[offset..offset + 8].try_into().unwrap();
        i64::from_le_bytes(bytes)
    }

    /// Format the coordinate value at position `idx` as a display string.
    pub fn format_value(&self, idx: usize) -> String {
        match &self.dtype {
            DType::Float64 => {
                let v = self.value_f64(idx);
                // Use a reasonable number of decimal places.
                if v == v.round() {
                    format!("{v:.1}")
                } else {
                    format!("{v}")
                }
            }
            DType::Int64 => {
                let v = self.value_i64(idx);
                format!("{v}")
            }
            _ => format!("{idx}"),
        }
    }

    /// Get the f64 value at a given index position.
    fn get_f64(&self, idx: usize) -> f64 {
        debug_assert!(idx < self.len);
        let offset = idx * self.element_size;
        let bytes: [u8; 8] = self.values[offset..offset + 8].try_into().unwrap();
        f64::from_le_bytes(bytes)
    }

    /// Get the i64 value at a given index position.
    fn get_i64(&self, idx: usize) -> i64 {
        debug_assert!(idx < self.len);
        let offset = idx * self.element_size;
        let bytes: [u8; 8] = self.values[offset..offset + 8].try_into().unwrap();
        i64::from_le_bytes(bytes)
    }

    /// Find exact match for an f64 value. Returns the integer index.
    pub fn lookup_exact_f64(&self, value: f64) -> Option<i64> {
        if self.len == 0 {
            return None;
        }
        match self.monotonicity {
            Monotonicity::Ascending => {
                // Binary search in ascending order.
                let mut lo = 0usize;
                let mut hi = self.len;
                while lo < hi {
                    let mid = lo + (hi - lo) / 2;
                    let v = self.get_f64(mid);
                    if v == value {
                        return Some(mid as i64);
                    } else if v < value {
                        lo = mid + 1;
                    } else {
                        hi = mid;
                    }
                }
                None
            }
            Monotonicity::Descending => {
                // Binary search in descending order.
                let mut lo = 0usize;
                let mut hi = self.len;
                while lo < hi {
                    let mid = lo + (hi - lo) / 2;
                    let v = self.get_f64(mid);
                    if v == value {
                        return Some(mid as i64);
                    } else if v > value {
                        lo = mid + 1;
                    } else {
                        hi = mid;
                    }
                }
                None
            }
            Monotonicity::Unknown => {
                // Linear scan.
                for i in 0..self.len {
                    if self.get_f64(i) == value {
                        return Some(i as i64);
                    }
                }
                None
            }
        }
    }

    /// Find the index of the nearest f64 value.
    pub fn lookup_nearest_f64(&self, value: f64) -> i64 {
        if self.len == 0 {
            return 0;
        }
        if self.len == 1 {
            return 0;
        }
        match self.monotonicity {
            Monotonicity::Ascending => {
                // Binary search for insertion point, then check neighbors.
                let mut lo = 0usize;
                let mut hi = self.len;
                while lo < hi {
                    let mid = lo + (hi - lo) / 2;
                    if self.get_f64(mid) < value {
                        lo = mid + 1;
                    } else {
                        hi = mid;
                    }
                }
                // lo is the insertion point; check lo-1 and lo.
                if lo == 0 {
                    0
                } else if lo >= self.len {
                    (self.len - 1) as i64
                } else {
                    let d_lo = (self.get_f64(lo) - value).abs();
                    let d_prev = (self.get_f64(lo - 1) - value).abs();
                    if d_prev <= d_lo {
                        (lo - 1) as i64
                    } else {
                        lo as i64
                    }
                }
            }
            Monotonicity::Descending => {
                // Binary search in descending: find insertion point.
                let mut lo = 0usize;
                let mut hi = self.len;
                while lo < hi {
                    let mid = lo + (hi - lo) / 2;
                    if self.get_f64(mid) > value {
                        lo = mid + 1;
                    } else {
                        hi = mid;
                    }
                }
                if lo == 0 {
                    0
                } else if lo >= self.len {
                    (self.len - 1) as i64
                } else {
                    let d_lo = (self.get_f64(lo) - value).abs();
                    let d_prev = (self.get_f64(lo - 1) - value).abs();
                    if d_prev <= d_lo {
                        (lo - 1) as i64
                    } else {
                        lo as i64
                    }
                }
            }
            Monotonicity::Unknown => {
                // Linear scan for minimum distance.
                let mut best_idx = 0;
                let mut best_dist = (self.get_f64(0) - value).abs();
                for i in 1..self.len {
                    let dist = (self.get_f64(i) - value).abs();
                    if dist < best_dist {
                        best_dist = dist;
                        best_idx = i;
                    }
                }
                best_idx as i64
            }
        }
    }

    /// Find the range of indices where f64 values fall within `[lo, hi]` (inclusive).
    /// Returns `(start_index, end_index)` inclusive, or `None` if no values in range.
    pub fn lookup_range_f64(&self, lo: f64, hi: f64) -> Option<(i64, i64)> {
        if self.len == 0 || lo > hi {
            return None;
        }
        match self.monotonicity {
            Monotonicity::Ascending => {
                // Binary search for first >= lo.
                let start = self.lower_bound_asc_f64(lo);
                if start >= self.len {
                    return None;
                }
                // Binary search for last <= hi.
                let end = self.upper_bound_asc_f64(hi);
                if end < start {
                    return None;
                }
                Some((start as i64, end as i64))
            }
            Monotonicity::Descending => {
                // In descending: values go high→low.
                // Find first index where value <= hi (start of matching region).
                let start = self.lower_bound_desc_f64(hi);
                if start >= self.len {
                    return None;
                }
                // Find last index where value >= lo (end of matching region).
                let end = self.upper_bound_desc_f64(lo);
                if end >= self.len {
                    return None;
                }
                // Verify the values at found indices are actually in range.
                if self.get_f64(start) > hi || self.get_f64(start) < lo {
                    return None;
                }
                if self.get_f64(end) > hi || self.get_f64(end) < lo {
                    return None;
                }
                if end < start {
                    return None;
                }
                Some((start as i64, end as i64))
            }
            Monotonicity::Unknown => {
                // Linear scan.
                let mut first = None;
                let mut last = None;
                for i in 0..self.len {
                    let v = self.get_f64(i);
                    if v >= lo && v <= hi {
                        if first.is_none() {
                            first = Some(i);
                        }
                        last = Some(i);
                    }
                }
                match (first, last) {
                    (Some(f), Some(l)) => Some((f as i64, l as i64)),
                    _ => None,
                }
            }
        }
    }

    /// Find exact match for an i64 value. Returns the integer index.
    pub fn lookup_exact_i64(&self, value: i64) -> Option<i64> {
        if self.len == 0 {
            return None;
        }
        match self.monotonicity {
            Monotonicity::Ascending => {
                let mut lo = 0usize;
                let mut hi = self.len;
                while lo < hi {
                    let mid = lo + (hi - lo) / 2;
                    let v = self.get_i64(mid);
                    if v == value {
                        return Some(mid as i64);
                    } else if v < value {
                        lo = mid + 1;
                    } else {
                        hi = mid;
                    }
                }
                None
            }
            Monotonicity::Descending => {
                let mut lo = 0usize;
                let mut hi = self.len;
                while lo < hi {
                    let mid = lo + (hi - lo) / 2;
                    let v = self.get_i64(mid);
                    if v == value {
                        return Some(mid as i64);
                    } else if v > value {
                        lo = mid + 1;
                    } else {
                        hi = mid;
                    }
                }
                None
            }
            Monotonicity::Unknown => {
                for i in 0..self.len {
                    if self.get_i64(i) == value {
                        return Some(i as i64);
                    }
                }
                None
            }
        }
    }

    /// Find the index of the nearest i64 value.
    pub fn lookup_nearest_i64(&self, value: i64) -> i64 {
        if self.len == 0 {
            return 0;
        }
        if self.len == 1 {
            return 0;
        }
        match self.monotonicity {
            Monotonicity::Ascending => {
                let mut lo = 0usize;
                let mut hi = self.len;
                while lo < hi {
                    let mid = lo + (hi - lo) / 2;
                    if self.get_i64(mid) < value {
                        lo = mid + 1;
                    } else {
                        hi = mid;
                    }
                }
                if lo == 0 {
                    0
                } else if lo >= self.len {
                    (self.len - 1) as i64
                } else {
                    let d_lo = (self.get_i64(lo) - value).unsigned_abs();
                    let d_prev = (self.get_i64(lo - 1) - value).unsigned_abs();
                    if d_prev <= d_lo {
                        (lo - 1) as i64
                    } else {
                        lo as i64
                    }
                }
            }
            Monotonicity::Descending => {
                let mut lo = 0usize;
                let mut hi = self.len;
                while lo < hi {
                    let mid = lo + (hi - lo) / 2;
                    if self.get_i64(mid) > value {
                        lo = mid + 1;
                    } else {
                        hi = mid;
                    }
                }
                if lo == 0 {
                    0
                } else if lo >= self.len {
                    (self.len - 1) as i64
                } else {
                    let d_lo = (self.get_i64(lo) - value).unsigned_abs();
                    let d_prev = (self.get_i64(lo - 1) - value).unsigned_abs();
                    if d_prev <= d_lo {
                        (lo - 1) as i64
                    } else {
                        lo as i64
                    }
                }
            }
            Monotonicity::Unknown => {
                let mut best_idx = 0;
                let mut best_dist = (self.get_i64(0) - value).unsigned_abs();
                for i in 1..self.len {
                    let dist = (self.get_i64(i) - value).unsigned_abs();
                    if dist < best_dist {
                        best_dist = dist;
                        best_idx = i;
                    }
                }
                best_idx as i64
            }
        }
    }

    /// Find the range of indices where i64 values fall within `[lo, hi]` (inclusive).
    pub fn lookup_range_i64(&self, lo: i64, hi: i64) -> Option<(i64, i64)> {
        if self.len == 0 || lo > hi {
            return None;
        }
        match self.monotonicity {
            Monotonicity::Ascending => {
                let start = self.lower_bound_asc_i64(lo);
                if start >= self.len {
                    return None;
                }
                let end = self.upper_bound_asc_i64(hi);
                if end < start {
                    return None;
                }
                Some((start as i64, end as i64))
            }
            Monotonicity::Descending => {
                let start = self.lower_bound_desc_i64(hi);
                if start >= self.len {
                    return None;
                }
                let end = self.upper_bound_desc_i64(lo);
                if end >= self.len {
                    return None;
                }
                if self.get_i64(start) > hi || self.get_i64(start) < lo {
                    return None;
                }
                if self.get_i64(end) > hi || self.get_i64(end) < lo {
                    return None;
                }
                if end < start {
                    return None;
                }
                Some((start as i64, end as i64))
            }
            Monotonicity::Unknown => {
                let mut first = None;
                let mut last = None;
                for i in 0..self.len {
                    let v = self.get_i64(i);
                    if v >= lo && v <= hi {
                        if first.is_none() {
                            first = Some(i);
                        }
                        last = Some(i);
                    }
                }
                match (first, last) {
                    (Some(f), Some(l)) => Some((f as i64, l as i64)),
                    _ => None,
                }
            }
        }
    }

    // --- Binary search helpers for ascending f64 ---

    /// First index where value >= target (ascending order).
    fn lower_bound_asc_f64(&self, target: f64) -> usize {
        let mut lo = 0usize;
        let mut hi = self.len;
        while lo < hi {
            let mid = lo + (hi - lo) / 2;
            if self.get_f64(mid) < target {
                lo = mid + 1;
            } else {
                hi = mid;
            }
        }
        lo
    }

    /// Last index where value <= target (ascending order).
    fn upper_bound_asc_f64(&self, target: f64) -> usize {
        let mut lo = 0usize;
        let mut hi = self.len;
        while lo < hi {
            let mid = lo + (hi - lo) / 2;
            if self.get_f64(mid) <= target {
                lo = mid + 1;
            } else {
                hi = mid;
            }
        }
        lo.saturating_sub(1)
    }

    // --- Binary search helpers for descending f64 ---

    /// First index where value <= target (descending order).
    fn lower_bound_desc_f64(&self, target: f64) -> usize {
        let mut lo = 0usize;
        let mut hi = self.len;
        while lo < hi {
            let mid = lo + (hi - lo) / 2;
            if self.get_f64(mid) > target {
                lo = mid + 1;
            } else {
                hi = mid;
            }
        }
        lo
    }

    /// Last index where value >= target (descending order).
    fn upper_bound_desc_f64(&self, target: f64) -> usize {
        let mut lo = 0usize;
        let mut hi = self.len;
        while lo < hi {
            let mid = lo + (hi - lo) / 2;
            if self.get_f64(mid) >= target {
                lo = mid + 1;
            } else {
                hi = mid;
            }
        }
        lo.saturating_sub(1)
    }

    // --- Binary search helpers for ascending i64 ---

    fn lower_bound_asc_i64(&self, target: i64) -> usize {
        let mut lo = 0usize;
        let mut hi = self.len;
        while lo < hi {
            let mid = lo + (hi - lo) / 2;
            if self.get_i64(mid) < target {
                lo = mid + 1;
            } else {
                hi = mid;
            }
        }
        lo
    }

    fn upper_bound_asc_i64(&self, target: i64) -> usize {
        let mut lo = 0usize;
        let mut hi = self.len;
        while lo < hi {
            let mid = lo + (hi - lo) / 2;
            if self.get_i64(mid) <= target {
                lo = mid + 1;
            } else {
                hi = mid;
            }
        }
        lo.saturating_sub(1)
    }

    // --- Binary search helpers for descending i64 ---

    fn lower_bound_desc_i64(&self, target: i64) -> usize {
        let mut lo = 0usize;
        let mut hi = self.len;
        while lo < hi {
            let mid = lo + (hi - lo) / 2;
            if self.get_i64(mid) > target {
                lo = mid + 1;
            } else {
                hi = mid;
            }
        }
        lo
    }

    fn upper_bound_desc_i64(&self, target: i64) -> usize {
        let mut lo = 0usize;
        let mut hi = self.len;
        while lo < hi {
            let mid = lo + (hi - lo) / 2;
            if self.get_i64(mid) >= target {
                lo = mid + 1;
            } else {
                hi = mid;
            }
        }
        lo.saturating_sub(1)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    // --- f64 ascending tests ---

    #[test]
    fn exact_f64_ascending() {
        // Ascending latitudes: -90.0, -89.75, ..., +90.0
        let lats: Vec<f64> = (0..721).map(|i| -90.0 + i as f64 * 0.25).collect();
        let idx = CoordinateIndex::from_f64("latitude", &lats, Monotonicity::Ascending);

        assert_eq!(idx.len(), 721);
        assert!(!idx.is_empty());
        assert_eq!(idx.lookup_exact_f64(-90.0), Some(0));
        assert_eq!(idx.lookup_exact_f64(-89.75), Some(1));
        assert_eq!(idx.lookup_exact_f64(90.0), Some(720));
        assert_eq!(idx.lookup_exact_f64(999.0), None);
    }

    #[test]
    fn nearest_f64_ascending() {
        let vals = vec![1.0, 2.0, 3.0, 5.0, 10.0];
        let idx = CoordinateIndex::from_f64("x", &vals, Monotonicity::Ascending);

        assert_eq!(idx.lookup_nearest_f64(1.0), 0); // exact
        assert_eq!(idx.lookup_nearest_f64(1.3), 0); // closer to 1.0
        assert_eq!(idx.lookup_nearest_f64(1.6), 1); // closer to 2.0
        assert_eq!(idx.lookup_nearest_f64(0.0), 0); // below range
        assert_eq!(idx.lookup_nearest_f64(100.0), 4); // above range
        assert_eq!(idx.lookup_nearest_f64(7.0), 3); // closer to 5.0 than 10.0
    }

    #[test]
    fn range_f64_ascending() {
        let vals: Vec<f64> = (0..100).map(|i| i as f64).collect();
        let idx = CoordinateIndex::from_f64("x", &vals, Monotonicity::Ascending);

        assert_eq!(idx.lookup_range_f64(10.0, 20.0), Some((10, 20)));
        assert_eq!(idx.lookup_range_f64(0.0, 0.0), Some((0, 0)));
        assert_eq!(idx.lookup_range_f64(99.0, 99.0), Some((99, 99)));
        assert_eq!(idx.lookup_range_f64(200.0, 300.0), None); // out of range
        assert_eq!(idx.lookup_range_f64(50.0, 30.0), None); // lo > hi
    }

    // --- f64 descending tests ---

    #[test]
    fn exact_f64_descending() {
        let vals: Vec<f64> = (0..721).map(|i| 90.0 - i as f64 * 0.25).collect();
        let idx = CoordinateIndex::from_f64("latitude", &vals, Monotonicity::Descending);

        assert_eq!(idx.lookup_exact_f64(90.0), Some(0));
        assert_eq!(idx.lookup_exact_f64(89.75), Some(1));
        assert_eq!(idx.lookup_exact_f64(-90.0), Some(720));
        assert_eq!(idx.lookup_exact_f64(999.0), None);
    }

    #[test]
    fn nearest_f64_descending() {
        let vals = vec![10.0, 5.0, 3.0, 2.0, 1.0];
        let idx = CoordinateIndex::from_f64("x", &vals, Monotonicity::Descending);

        assert_eq!(idx.lookup_nearest_f64(10.0), 0); // exact
        assert_eq!(idx.lookup_nearest_f64(4.0), 1); // closer to 5.0
        assert_eq!(idx.lookup_nearest_f64(0.0), 4); // below range
        assert_eq!(idx.lookup_nearest_f64(100.0), 0); // above range
    }

    #[test]
    fn range_f64_descending() {
        // Descending: [100, 99, 98, ..., 1, 0]
        let vals: Vec<f64> = (0..=100).rev().map(|i| i as f64).collect();
        let idx = CoordinateIndex::from_f64("x", &vals, Monotonicity::Descending);

        // Range [20, 30] → indices [70, 80] in descending array
        assert_eq!(idx.lookup_range_f64(20.0, 30.0), Some((70, 80)));
        assert_eq!(idx.lookup_range_f64(100.0, 100.0), Some((0, 0)));
        assert_eq!(idx.lookup_range_f64(200.0, 300.0), None);
    }

    // --- i64 ascending tests ---

    #[test]
    fn exact_i64_ascending() {
        let times: Vec<i64> = (0..8760).map(|i| 1704067200 + i * 3600).collect();
        let idx = CoordinateIndex::from_i64("time", &times, Monotonicity::Ascending);

        assert_eq!(idx.lookup_exact_i64(1704067200), Some(0));
        assert_eq!(idx.lookup_exact_i64(1704067200 + 3600), Some(1));
        assert_eq!(idx.lookup_exact_i64(999), None);
    }

    #[test]
    fn nearest_i64_ascending() {
        let vals = vec![10i64, 20, 30, 50, 100];
        let idx = CoordinateIndex::from_i64("t", &vals, Monotonicity::Ascending);

        assert_eq!(idx.lookup_nearest_i64(10), 0);
        assert_eq!(idx.lookup_nearest_i64(24), 1); // closer to 20
        assert_eq!(idx.lookup_nearest_i64(26), 2); // closer to 30
        assert_eq!(idx.lookup_nearest_i64(0), 0);
        assert_eq!(idx.lookup_nearest_i64(1000), 4);
    }

    #[test]
    fn range_i64_ascending() {
        let vals: Vec<i64> = (0..100).collect();
        let idx = CoordinateIndex::from_i64("t", &vals, Monotonicity::Ascending);

        assert_eq!(idx.lookup_range_i64(10, 20), Some((10, 20)));
        assert_eq!(idx.lookup_range_i64(200, 300), None);
    }

    // --- i64 descending tests ---

    #[test]
    fn exact_i64_descending() {
        let vals: Vec<i64> = (0..100).rev().collect();
        let idx = CoordinateIndex::from_i64("t", &vals, Monotonicity::Descending);

        assert_eq!(idx.lookup_exact_i64(99), Some(0));
        assert_eq!(idx.lookup_exact_i64(0), Some(99));
        assert_eq!(idx.lookup_exact_i64(200), None);
    }

    // --- Unknown monotonicity tests ---

    #[test]
    fn exact_f64_unknown() {
        let vals = vec![3.0, 1.0, 4.0, 1.5, 9.0, 2.6];
        let idx = CoordinateIndex::from_f64("x", &vals, Monotonicity::Unknown);

        assert_eq!(idx.lookup_exact_f64(3.0), Some(0));
        assert_eq!(idx.lookup_exact_f64(9.0), Some(4));
        assert_eq!(idx.lookup_exact_f64(7.0), None);
    }

    #[test]
    fn nearest_f64_unknown() {
        let vals = vec![3.0, 1.0, 4.0, 1.5, 9.0, 2.6];
        let idx = CoordinateIndex::from_f64("x", &vals, Monotonicity::Unknown);

        assert_eq!(idx.lookup_nearest_f64(3.0), 0); // exact
        assert_eq!(idx.lookup_nearest_f64(8.0), 4); // closest to 9.0
    }

    #[test]
    fn range_f64_unknown() {
        let vals = vec![3.0, 1.0, 4.0, 1.5, 9.0, 2.6];
        let idx = CoordinateIndex::from_f64("x", &vals, Monotonicity::Unknown);

        // Values in [1.0, 3.0]: indices 0 (3.0), 1 (1.0), 3 (1.5), 5 (2.6)
        // first=0, last=5
        assert_eq!(idx.lookup_range_f64(1.0, 3.0), Some((0, 5)));
    }

    // --- Edge cases ---

    #[test]
    fn empty_index() {
        let idx = CoordinateIndex::from_f64("x", &[], Monotonicity::Ascending);
        assert!(idx.is_empty());
        assert_eq!(idx.len(), 0);
        assert_eq!(idx.lookup_exact_f64(1.0), None);
        assert_eq!(idx.lookup_nearest_f64(1.0), 0);
        assert_eq!(idx.lookup_range_f64(0.0, 1.0), None);
    }

    #[test]
    fn single_element() {
        let idx = CoordinateIndex::from_f64("x", &[42.0], Monotonicity::Ascending);
        assert_eq!(idx.len(), 1);
        assert_eq!(idx.lookup_exact_f64(42.0), Some(0));
        assert_eq!(idx.lookup_exact_f64(0.0), None);
        assert_eq!(idx.lookup_nearest_f64(100.0), 0);
        assert_eq!(idx.lookup_range_f64(40.0, 50.0), Some((0, 0)));
    }
}
