#![deny(rustdoc::broken_intra_doc_links)]
use crate::{
Grid1D, Grid1DNonUniform, Grid1DUniform, IntervalClosed, IntervalFinitePositiveLength,
IntervalId, IntervalLowerClosedUpperOpen, IntervalLowerOpenUpperClosed, IntervalOpen,
NumIntervals,
grids::{
FindIntervalIdOfPoint, Grid1DIntervalBuilder, Grid1DTrait, HasCoords1D, HasDomain1D,
HasIntervalIdRange, HasIntervals,
},
intervals::{Contains, GetLowerBoundValue, GetUpperBoundValue, bounded::IntervalFromBounds},
};
use std::backtrace::Backtrace;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum ErrorsGrid1DWindow {
#[error("The requested window of intervals exceeds the bounds of the partition!")]
WindowExceedsPartitionBounds {
first_interval_id: IntervalId,
num_intervals_in_window: NumIntervals,
total_num_intervals: NumIntervals,
backtrace: Backtrace,
},
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct Grid1DWindow<'a, PartitionType: Grid1DTrait> {
partition: &'a PartitionType,
first_interval_id: IntervalId,
num_intervals_in_window: NumIntervals,
}
impl<'a, PartitionType: Grid1DTrait> Grid1DWindow<'a, PartitionType> {
pub fn try_new(
partition: &'a PartitionType,
first_interval_id: IntervalId,
num_intervals_in_window: NumIntervals,
) -> Result<Self, ErrorsGrid1DWindow> {
let total_num_intervals = partition.num_intervals();
if first_interval_id.as_ref() + num_intervals_in_window.as_ref()
> *total_num_intervals.as_ref()
{
Err(ErrorsGrid1DWindow::WindowExceedsPartitionBounds {
first_interval_id,
num_intervals_in_window,
total_num_intervals,
backtrace: Backtrace::capture(),
})
} else {
Ok(Self {
partition,
first_interval_id,
num_intervals_in_window,
})
}
}
#[must_use]
pub fn window_domain(
&self,
) -> IntervalFinitePositiveLength<<PartitionType as HasCoords1D>::CoordType> {
let coords = self.partition.coords().as_ref();
let first = *self.first_interval_id.as_ref();
let last = first + *self.num_intervals_in_window.as_ref();
let lower = coords[first].clone();
let upper = coords[last].clone();
let domain = self.partition.domain();
let is_lower_closed = domain.is_lower_bound_closed();
let window_ends_at_partition_end = last == *self.partition.num_intervals().as_ref();
let is_upper_closed = if window_ends_at_partition_end {
domain.is_upper_bound_closed()
} else {
!is_lower_closed
};
match (is_lower_closed, is_upper_closed) {
(true, true) => IntervalFinitePositiveLength::Closed(IntervalClosed::new(lower, upper)),
(false, false) => IntervalFinitePositiveLength::Open(IntervalOpen::new(lower, upper)),
(true, false) => IntervalFinitePositiveLength::LowerClosedUpperOpen(
IntervalLowerClosedUpperOpen::new(lower, upper),
),
(false, true) => IntervalFinitePositiveLength::LowerOpenUpperClosed(
IntervalLowerOpenUpperClosed::new(lower, upper),
),
}
}
}
impl<PartitionType: Grid1DTrait> HasIntervalIdRange for Grid1DWindow<'_, PartitionType> {
#[inline(always)]
fn first_interval_id(&self) -> IntervalId {
self.first_interval_id
}
#[inline(always)]
fn last_interval_id(&self) -> IntervalId {
IntervalId::new(
*self.first_interval_id.as_ref() + *self.num_intervals_in_window.as_ref() - 1,
)
}
}
impl<PartitionType: Grid1DTrait> HasIntervals for Grid1DWindow<'_, PartitionType> {
type IntervalType = <PartitionType as HasIntervals>::IntervalType;
#[inline(always)]
fn interval(&self, interval_id: &IntervalId) -> Self::IntervalType {
self.partition.interval(interval_id)
}
}
impl<Domain1D> FindIntervalIdOfPoint for Grid1DWindow<'_, Grid1DNonUniform<Domain1D>>
where
Domain1D: Grid1DIntervalBuilder,
{
type Point1DType = Domain1D::RealType;
#[inline]
fn find_interval_id_of_point(&self, x: &Self::Point1DType) -> Option<IntervalId> {
let coords = self.partition.coords().as_ref();
let window_start = *self.first_interval_id.as_ref();
let num_window_intervals = *self.num_intervals_in_window.as_ref();
let window_end = window_start + num_window_intervals;
let window_coords: &[_] = &coords[window_start..=window_end];
let is_lower_closed = self.partition.domain().is_lower_bound_closed();
let window_ends_at_partition_end = window_end == *self.partition.num_intervals().as_ref();
let is_upper_closed = if window_ends_at_partition_end {
self.partition.domain().is_upper_bound_closed()
} else {
!is_lower_closed
};
let lower = &window_coords[0];
let upper = &window_coords[num_window_intervals];
if is_lower_closed {
if x < lower {
return None;
}
} else if x <= lower {
return None;
}
if is_upper_closed {
if x > upper {
return None;
}
} else if x >= upper {
return None;
}
let idx = window_coords.partition_point(|c| c < x);
let candidate_id_in_window = if is_lower_closed {
if idx == 0 {
0 } else if idx < window_coords.len() && &window_coords[idx] == x {
idx.min(num_window_intervals - 1)
} else {
(idx - 1).min(num_window_intervals - 1)
}
} else {
(idx - 1).min(num_window_intervals - 1)
};
let interval_id = IntervalId::new(window_start + candidate_id_in_window);
let interval = self.partition.interval(&interval_id);
if interval.contains_point(x) {
Some(interval_id)
} else {
None
}
}
}
impl<Domain1D> FindIntervalIdOfPoint for Grid1DWindow<'_, Grid1DUniform<Domain1D>>
where
Domain1D: Grid1DIntervalBuilder,
{
type Point1DType = Domain1D::RealType;
#[inline(always)]
fn find_interval_id_of_point(&self, x: &Self::Point1DType) -> Option<IntervalId> {
self.partition
.find_interval_id_of_point(x)
.filter(|&id| self.range_contains_interval_id(&id))
}
}
impl<Domain1D> FindIntervalIdOfPoint for Grid1DWindow<'_, Grid1D<Domain1D>>
where
Domain1D: Grid1DIntervalBuilder,
{
type Point1DType = Domain1D::RealType;
#[inline]
fn find_interval_id_of_point(&self, x: &Self::Point1DType) -> Option<IntervalId> {
match self.partition {
Grid1D::Uniform(grid_uniform) => grid_uniform
.find_interval_id_of_point(x)
.filter(|&id| self.range_contains_interval_id(&id)),
Grid1D::NonUniform(grid_non_uniform) => {
let partition = Grid1DWindow {
partition: grid_non_uniform,
first_interval_id: self.first_interval_id,
num_intervals_in_window: self.num_intervals_in_window,
};
partition.find_interval_id_of_point(x)
}
}
}
}
#[cfg(test)]
mod tests {
use crate::{coords::*, intervals::*};
use num_valid::RealNative64StrictFinite;
use sorted_vec::partial::SortedSet;
use try_create::TryNew;
use super::*;
type Real = RealNative64StrictFinite;
#[test]
fn interval_partition_window_try_new_ok_and_membership() {
let domain = IntervalClosed::new(Real::try_new(0.0).unwrap(), Real::try_new(3.0).unwrap());
let grid = Grid1DUniform::new(domain, NumIntervals::try_new(3).unwrap());
let window =
Grid1DWindow::try_new(&grid, IntervalId::new(1), NumIntervals::try_new(2).unwrap())
.unwrap();
assert!(window.range_contains_interval_id(&IntervalId::new(1)));
assert!(window.range_contains_interval_id(&IntervalId::new(2)));
assert!(!window.range_contains_interval_id(&IntervalId::new(0)));
}
#[test]
fn interval_partition_window_try_new_err_out_of_bounds() {
let domain = IntervalClosed::new(Real::try_new(0.0).unwrap(), Real::try_new(3.0).unwrap());
let grid = Grid1DUniform::new(domain, NumIntervals::try_new(3).unwrap());
let err =
Grid1DWindow::try_new(&grid, IntervalId::new(2), NumIntervals::try_new(2).unwrap())
.unwrap_err();
assert!(matches!(
err,
ErrorsGrid1DWindow::WindowExceedsPartitionBounds { .. }
));
}
#[test]
fn interval_partition_window_find_non_uniform_left_closed() {
let coords = Coords1D::try_from(SortedSet::from_unsorted(vec![
Real::try_new(0.0).unwrap(),
Real::try_new(1.0).unwrap(),
Real::try_new(2.0).unwrap(),
Real::try_new(3.0).unwrap(),
]))
.unwrap();
let grid = Grid1DNonUniform::<IntervalClosed<Real>>::try_new_from_coords(coords).unwrap();
let window =
Grid1DWindow::try_new(&grid, IntervalId::new(1), NumIntervals::try_new(2).unwrap())
.unwrap();
assert_eq!(
window
.find_interval_id_of_point(&Real::try_new(1.0).unwrap())
.unwrap(),
IntervalId::new(1)
);
}
#[test]
fn interval_partition_window_find_non_uniform_right_closed() {
let coords = Coords1D::try_from(SortedSet::from_unsorted(vec![
Real::try_new(0.0).unwrap(),
Real::try_new(1.0).unwrap(),
Real::try_new(2.0).unwrap(),
Real::try_new(3.0).unwrap(),
]))
.unwrap();
let grid =
Grid1DNonUniform::<IntervalLowerOpenUpperClosed<Real>>::try_new_from_coords(coords)
.unwrap();
let window =
Grid1DWindow::try_new(&grid, IntervalId::new(1), NumIntervals::try_new(2).unwrap())
.unwrap();
assert_eq!(
window
.find_interval_id_of_point(&Real::try_new(2.0).unwrap())
.unwrap(),
IntervalId::new(1)
);
}
#[test]
fn interval_partition_window_find_uniform_respects_window() {
let domain = IntervalClosed::new(Real::try_new(0.0).unwrap(), Real::try_new(3.0).unwrap());
let grid = Grid1DUniform::new(domain, NumIntervals::try_new(3).unwrap());
let window =
Grid1DWindow::try_new(&grid, IntervalId::new(1), NumIntervals::try_new(1).unwrap())
.unwrap();
assert_eq!(
window
.find_interval_id_of_point(&Real::try_new(1.5).unwrap())
.unwrap(),
IntervalId::new(1)
);
assert!(
window
.find_interval_id_of_point(&Real::try_new(0.5).unwrap())
.is_none()
);
}
#[test]
fn interval_partition_window_find_grid1d_uniform_and_non_uniform() {
let uniform_domain =
IntervalClosed::new(Real::try_new(0.0).unwrap(), Real::try_new(3.0).unwrap());
let uniform_grid: Grid1D<IntervalClosed<Real>> =
Grid1D::uniform(uniform_domain, NumIntervals::try_new(3).unwrap());
let window_uniform = Grid1DWindow::try_new(
&uniform_grid,
IntervalId::new(1),
NumIntervals::try_new(1).unwrap(),
)
.unwrap();
assert_eq!(
window_uniform
.find_interval_id_of_point(&Real::try_new(1.5).unwrap())
.unwrap(),
IntervalId::new(1)
);
assert!(
window_uniform
.find_interval_id_of_point(&Real::try_new(0.5).unwrap())
.is_none()
);
let non_uniform_coords = Coords1D::try_from(SortedSet::from_unsorted(vec![
Real::try_new(0.0).unwrap(),
Real::try_new(1.0).unwrap(),
Real::try_new(2.0).unwrap(),
Real::try_new(3.0).unwrap(),
]))
.unwrap();
let non_uniform_grid =
Grid1D::<IntervalClosed<Real>>::try_from_coords(non_uniform_coords).unwrap();
let window_non_uniform = Grid1DWindow::try_new(
&non_uniform_grid,
IntervalId::new(1),
NumIntervals::try_new(2).unwrap(),
)
.unwrap();
assert_eq!(
window_non_uniform
.find_interval_id_of_point(&Real::try_new(1.0).unwrap())
.unwrap(),
IntervalId::new(1)
);
}
mod interval_partition_window_matrix {
use super::*;
fn assert_point_ids<W>(finder: &W, cases: &[(f64, Option<usize>)])
where
W: FindIntervalIdOfPoint<Point1DType = Real>,
{
for (x, expected_id) in cases {
let x_real = Real::try_new(*x).unwrap();
let actual = finder.find_interval_id_of_point(&x_real);
let expected = expected_id.map(IntervalId::new);
assert_eq!(actual, expected, "x = {x}");
}
}
fn all_probe_points_closed() -> Vec<(f64, Option<usize>)> {
vec![
(0.0, Some(0)),
(1.0, Some(1)),
(2.0, Some(2)),
(3.0, Some(2)),
(0.5, Some(0)),
(1.5, Some(1)),
(2.5, Some(2)),
]
}
fn all_probe_points_open() -> Vec<(f64, Option<usize>)> {
vec![
(0.0, None),
(1.0, Some(0)),
(2.0, Some(1)),
(3.0, None),
(0.5, Some(0)),
(1.5, Some(1)),
(2.5, Some(2)),
]
}
fn all_probe_points_lc_uo() -> Vec<(f64, Option<usize>)> {
vec![
(0.0, Some(0)),
(1.0, Some(1)),
(2.0, Some(2)),
(3.0, None),
(0.5, Some(0)),
(1.5, Some(1)),
(2.5, Some(2)),
]
}
fn all_probe_points_lo_uc() -> Vec<(f64, Option<usize>)> {
vec![
(0.0, None),
(1.0, Some(0)),
(2.0, Some(1)),
(3.0, Some(2)),
(0.5, Some(0)),
(1.5, Some(1)),
(2.5, Some(2)),
]
}
fn make_window<'a, G: Grid1DTrait>(grid: &'a G) -> Grid1DWindow<'a, G> {
Grid1DWindow::try_new(grid, IntervalId::new(0), NumIntervals::try_new(3).unwrap())
.unwrap()
}
fn assert_point_ids_in_window<'a, G: Grid1DTrait>(
grid: &'a G,
cases: &[(f64, Option<usize>)],
) where
Grid1DWindow<'a, G>: FindIntervalIdOfPoint<Point1DType = Real>,
{
let window = make_window(grid);
assert_point_ids(&window, cases);
}
mod uniform {
use super::*;
#[test]
fn interval_closed() {
let domain =
IntervalClosed::new(Real::try_new(0.0).unwrap(), Real::try_new(3.0).unwrap());
let grid = Grid1DUniform::new(domain, NumIntervals::try_new(3).unwrap());
assert_point_ids_in_window(&grid, &all_probe_points_closed());
}
#[test]
fn interval_open() {
let domain =
IntervalOpen::new(Real::try_new(0.0).unwrap(), Real::try_new(3.0).unwrap());
let grid = Grid1DUniform::new(domain, NumIntervals::try_new(3).unwrap());
assert_point_ids_in_window(&grid, &all_probe_points_open());
}
#[test]
fn interval_lower_closed_upper_open() {
let domain = IntervalLowerClosedUpperOpen::new(
Real::try_new(0.0).unwrap(),
Real::try_new(3.0).unwrap(),
);
let grid = Grid1DUniform::new(domain, NumIntervals::try_new(3).unwrap());
assert_point_ids_in_window(&grid, &all_probe_points_lc_uo());
}
#[test]
fn interval_lower_open_upper_closed() {
let domain = IntervalLowerOpenUpperClosed::new(
Real::try_new(0.0).unwrap(),
Real::try_new(3.0).unwrap(),
);
let grid = Grid1DUniform::new(domain, NumIntervals::try_new(3).unwrap());
assert_point_ids_in_window(&grid, &all_probe_points_lo_uc());
}
}
mod non_uniform {
use super::*;
fn coords_0_1_2_3() -> Coords1D<Real> {
Coords1D::try_from(SortedSet::from_unsorted(vec![
Real::try_new(0.0).unwrap(),
Real::try_new(1.0).unwrap(),
Real::try_new(2.0).unwrap(),
Real::try_new(3.0).unwrap(),
]))
.unwrap()
}
#[test]
fn interval_closed() {
let domain =
IntervalClosed::new(Real::try_new(0.0).unwrap(), Real::try_new(3.0).unwrap());
let grid = Grid1DNonUniform::try_new(domain, coords_0_1_2_3()).unwrap();
assert_point_ids_in_window(&grid, &all_probe_points_closed());
}
#[test]
fn interval_open() {
let domain =
IntervalOpen::new(Real::try_new(0.0).unwrap(), Real::try_new(3.0).unwrap());
let grid = Grid1DNonUniform::try_new(domain, coords_0_1_2_3()).unwrap();
assert_point_ids_in_window(&grid, &all_probe_points_open());
}
#[test]
fn interval_lower_closed_upper_open() {
let domain = IntervalLowerClosedUpperOpen::new(
Real::try_new(0.0).unwrap(),
Real::try_new(3.0).unwrap(),
);
let grid = Grid1DNonUniform::try_new(domain, coords_0_1_2_3()).unwrap();
assert_point_ids_in_window(&grid, &all_probe_points_lc_uo());
}
#[test]
fn interval_lower_open_upper_closed() {
let domain = IntervalLowerOpenUpperClosed::new(
Real::try_new(0.0).unwrap(),
Real::try_new(3.0).unwrap(),
);
let grid = Grid1DNonUniform::try_new(domain, coords_0_1_2_3()).unwrap();
assert_point_ids_in_window(&grid, &all_probe_points_lo_uc());
}
}
}
mod window_domain {
use super::*;
fn r(v: f64) -> Real {
Real::try_new(v).unwrap()
}
mod uniform {
use super::*;
#[test]
fn closed_interior_is_lower_closed_upper_open() {
let grid = Grid1DUniform::new(
IntervalClosed::new(r(0.0), r(3.0)),
NumIntervals::try_new(3).unwrap(),
);
let w = Grid1DWindow::try_new(
&grid,
IntervalId::new(0),
NumIntervals::try_new(2).unwrap(),
)
.unwrap();
let d = w.window_domain();
assert!(
matches!(d, IntervalFinitePositiveLength::LowerClosedUpperOpen(_)),
"expected LowerClosedUpperOpen, got {d:?}",
);
assert_eq!(*d.lower_bound_value(), r(0.0));
assert_eq!(*d.upper_bound_value(), r(2.0));
}
#[test]
fn closed_end_is_closed() {
let grid = Grid1DUniform::new(
IntervalClosed::new(r(0.0), r(3.0)),
NumIntervals::try_new(3).unwrap(),
);
let w = Grid1DWindow::try_new(
&grid,
IntervalId::new(1),
NumIntervals::try_new(2).unwrap(),
)
.unwrap();
let d = w.window_domain();
assert!(
matches!(d, IntervalFinitePositiveLength::Closed(_)),
"expected Closed, got {d:?}",
);
assert_eq!(*d.lower_bound_value(), r(1.0));
assert_eq!(*d.upper_bound_value(), r(3.0));
}
#[test]
fn open_interior_is_lower_open_upper_closed() {
let grid = Grid1DUniform::new(
IntervalOpen::new(r(0.0), r(3.0)),
NumIntervals::try_new(3).unwrap(),
);
let w = Grid1DWindow::try_new(
&grid,
IntervalId::new(0),
NumIntervals::try_new(2).unwrap(),
)
.unwrap();
let d = w.window_domain();
assert!(
matches!(d, IntervalFinitePositiveLength::LowerOpenUpperClosed(_)),
"expected LowerOpenUpperClosed, got {d:?}",
);
assert_eq!(*d.lower_bound_value(), r(0.0));
assert_eq!(*d.upper_bound_value(), r(2.0));
}
#[test]
fn open_end_is_open() {
let grid = Grid1DUniform::new(
IntervalOpen::new(r(0.0), r(3.0)),
NumIntervals::try_new(3).unwrap(),
);
let w = Grid1DWindow::try_new(
&grid,
IntervalId::new(1),
NumIntervals::try_new(2).unwrap(),
)
.unwrap();
let d = w.window_domain();
assert!(
matches!(d, IntervalFinitePositiveLength::Open(_)),
"expected Open, got {d:?}",
);
assert_eq!(*d.lower_bound_value(), r(1.0));
assert_eq!(*d.upper_bound_value(), r(3.0));
}
#[test]
fn lower_closed_upper_open_interior_is_lower_closed_upper_open() {
let grid = Grid1DUniform::new(
IntervalLowerClosedUpperOpen::new(r(0.0), r(3.0)),
NumIntervals::try_new(3).unwrap(),
);
let w = Grid1DWindow::try_new(
&grid,
IntervalId::new(0),
NumIntervals::try_new(2).unwrap(),
)
.unwrap();
assert!(matches!(
w.window_domain(),
IntervalFinitePositiveLength::LowerClosedUpperOpen(_)
));
}
#[test]
fn lower_closed_upper_open_end_is_lower_closed_upper_open() {
let grid = Grid1DUniform::new(
IntervalLowerClosedUpperOpen::new(r(0.0), r(3.0)),
NumIntervals::try_new(3).unwrap(),
);
let w = Grid1DWindow::try_new(
&grid,
IntervalId::new(1),
NumIntervals::try_new(2).unwrap(),
)
.unwrap();
assert!(matches!(
w.window_domain(),
IntervalFinitePositiveLength::LowerClosedUpperOpen(_)
));
}
#[test]
fn lower_open_upper_closed_interior_is_lower_open_upper_closed() {
let grid = Grid1DUniform::new(
IntervalLowerOpenUpperClosed::new(r(0.0), r(3.0)),
NumIntervals::try_new(3).unwrap(),
);
let w = Grid1DWindow::try_new(
&grid,
IntervalId::new(0),
NumIntervals::try_new(2).unwrap(),
)
.unwrap();
assert!(matches!(
w.window_domain(),
IntervalFinitePositiveLength::LowerOpenUpperClosed(_)
));
}
#[test]
fn lower_open_upper_closed_end_is_lower_open_upper_closed() {
let grid = Grid1DUniform::new(
IntervalLowerOpenUpperClosed::new(r(0.0), r(3.0)),
NumIntervals::try_new(3).unwrap(),
);
let w = Grid1DWindow::try_new(
&grid,
IntervalId::new(1),
NumIntervals::try_new(2).unwrap(),
)
.unwrap();
assert!(matches!(
w.window_domain(),
IntervalFinitePositiveLength::LowerOpenUpperClosed(_)
));
}
#[test]
fn full_partition_window_matches_domain() {
let grid = Grid1DUniform::new(
IntervalClosed::new(r(0.0), r(4.0)),
NumIntervals::try_new(4).unwrap(),
);
let w = Grid1DWindow::try_new(
&grid,
IntervalId::new(0),
NumIntervals::try_new(4).unwrap(),
)
.unwrap();
let d = w.window_domain();
assert!(matches!(d, IntervalFinitePositiveLength::Closed(_)));
assert_eq!(*d.lower_bound_value(), r(0.0));
assert_eq!(*d.upper_bound_value(), r(4.0));
}
}
mod non_uniform {
use super::*;
fn coords_0_1_3_6() -> Coords1D<Real> {
Coords1D::try_from(SortedSet::from_unsorted(vec![
r(0.0),
r(1.0),
r(3.0),
r(6.0),
]))
.unwrap()
}
#[test]
fn closed_interior_is_lower_closed_upper_open_with_correct_bounds() {
let grid = Grid1DNonUniform::try_new(
IntervalClosed::new(r(0.0), r(6.0)),
coords_0_1_3_6(),
)
.unwrap();
let w = Grid1DWindow::try_new(
&grid,
IntervalId::new(0),
NumIntervals::try_new(2).unwrap(),
)
.unwrap();
let d = w.window_domain();
assert!(
matches!(d, IntervalFinitePositiveLength::LowerClosedUpperOpen(_)),
"expected LowerClosedUpperOpen, got {d:?}",
);
assert_eq!(*d.lower_bound_value(), r(0.0));
assert_eq!(*d.upper_bound_value(), r(3.0)); }
#[test]
fn closed_end_is_closed_with_correct_bounds() {
let grid = Grid1DNonUniform::try_new(
IntervalClosed::new(r(0.0), r(6.0)),
coords_0_1_3_6(),
)
.unwrap();
let w = Grid1DWindow::try_new(
&grid,
IntervalId::new(1),
NumIntervals::try_new(2).unwrap(),
)
.unwrap();
let d = w.window_domain();
assert!(
matches!(d, IntervalFinitePositiveLength::Closed(_)),
"expected Closed, got {d:?}",
);
assert_eq!(*d.lower_bound_value(), r(1.0)); assert_eq!(*d.upper_bound_value(), r(6.0)); }
#[test]
fn open_interior_is_lower_open_upper_closed_with_correct_bounds() {
let grid =
Grid1DNonUniform::try_new(IntervalOpen::new(r(0.0), r(6.0)), coords_0_1_3_6())
.unwrap();
let w = Grid1DWindow::try_new(
&grid,
IntervalId::new(0),
NumIntervals::try_new(2).unwrap(),
)
.unwrap();
let d = w.window_domain();
assert!(
matches!(d, IntervalFinitePositiveLength::LowerOpenUpperClosed(_)),
"expected LowerOpenUpperClosed, got {d:?}",
);
assert_eq!(*d.lower_bound_value(), r(0.0));
assert_eq!(*d.upper_bound_value(), r(3.0)); }
#[test]
fn open_end_is_open_with_correct_bounds() {
let grid =
Grid1DNonUniform::try_new(IntervalOpen::new(r(0.0), r(6.0)), coords_0_1_3_6())
.unwrap();
let w = Grid1DWindow::try_new(
&grid,
IntervalId::new(1),
NumIntervals::try_new(2).unwrap(),
)
.unwrap();
let d = w.window_domain();
assert!(
matches!(d, IntervalFinitePositiveLength::Open(_)),
"expected Open, got {d:?}",
);
assert_eq!(*d.lower_bound_value(), r(1.0)); assert_eq!(*d.upper_bound_value(), r(6.0)); }
}
}
}