#![deny(rustdoc::broken_intra_doc_links)]
use crate::{
Grid1DIndexSpaces, Side, SubIntervalInPartition,
coords::Coords1D,
intervals::{
GetLowerBoundValue, GetUpperBoundValue, Interval, IntervalClosed, IntervalFiniteLength,
IntervalFinitePositiveLength, IntervalFinitePositiveLengthTrait,
IntervalLowerClosedUpperOpen, IntervalLowerOpenUpperClosed, IntervalOpen, IntervalTrait,
bounded::IntervalFromBounds, operations::IntervalOperations,
},
operations::refinement::{Grid1DNonUniformRefinement, Grid1DUniformRefinement},
scalars::{CoordId, IntervalId, NumIntervals, PositiveNumPoints1D},
topology::Adjacency1D,
};
use duplicate::duplicate_item;
use more_asserts::debug_assert_lt;
use num_valid::{RealScalar, scalars::PositiveRealScalar};
use rayon::prelude::*;
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use try_create::TryNew;
pub trait HasDomain1D: Sized {
type Domain1D: IntervalTrait;
fn domain(&self) -> &Self::Domain1D;
}
pub trait HasCoords1D {
type CoordType: RealScalar;
#[inline(always)]
fn coord(&self, coord_id: CoordId) -> &Self::CoordType {
&self.coords()[coord_id]
}
fn coords(&self) -> &Coords1D<Self::CoordType>;
#[inline]
fn num_points(&self) -> PositiveNumPoints1D {
self.coords().num_points()
}
}
pub trait Grid1DIntervalBuilder: IntervalFinitePositiveLengthTrait {
type FirstIntervalInPartition: IntervalFinitePositiveLengthTrait<RealType = Self::RealType>
+ Into<Interval<Self::RealType>>
+ Into<IntervalFinitePositiveLength<Self::RealType>>;
type MiddleIntervalInPartition: IntervalFinitePositiveLengthTrait<RealType = Self::RealType>
+ Into<Interval<Self::RealType>>
+ Into<IntervalFinitePositiveLength<Self::RealType>>;
type LastIntervalInPartition: IntervalFinitePositiveLengthTrait<RealType = Self::RealType>
+ Into<Interval<Self::RealType>>
+ Into<IntervalFinitePositiveLength<Self::RealType>>;
fn build_single_interval(self) -> SubIntervalInPartition<Self> {
SubIntervalInPartition::Single(self)
}
fn build_first_interval(
&self,
coords: &Coords1D<Self::RealType>,
) -> SubIntervalInPartition<Self>;
fn build_middle_interval(
&self,
coords: &Coords1D<Self::RealType>,
interval_id: &IntervalId,
) -> SubIntervalInPartition<Self>;
fn build_last_interval(
&self,
coords: &Coords1D<Self::RealType>,
) -> SubIntervalInPartition<Self>;
}
#[duplicate_item(
I type_first_interval type_middle_interval type_last_interval;
[IntervalClosed] [IntervalLowerClosedUpperOpen] [IntervalLowerClosedUpperOpen] [IntervalClosed];
[IntervalOpen] [IntervalLowerOpenUpperClosed] [IntervalLowerOpenUpperClosed] [IntervalOpen];
[IntervalLowerClosedUpperOpen] [IntervalLowerClosedUpperOpen] [IntervalLowerClosedUpperOpen] [IntervalLowerClosedUpperOpen];
[IntervalLowerOpenUpperClosed] [IntervalLowerOpenUpperClosed] [IntervalLowerOpenUpperClosed] [IntervalLowerOpenUpperClosed];
)]
impl<RealType: RealScalar> Grid1DIntervalBuilder for I<RealType> {
type FirstIntervalInPartition = type_first_interval<RealType>;
type MiddleIntervalInPartition = type_middle_interval<RealType>;
type LastIntervalInPartition = type_last_interval<RealType>;
fn build_first_interval(
&self,
coords: &Coords1D<Self::RealType>,
) -> SubIntervalInPartition<Self> {
let coords = coords.as_ref();
SubIntervalInPartition::First(
<Self as Grid1DIntervalBuilder>::FirstIntervalInPartition::new(
coords[0].clone(),
coords[1].clone(),
),
)
}
fn build_middle_interval(
&self,
coords: &Coords1D<Self::RealType>,
interval_id: &IntervalId,
) -> SubIntervalInPartition<Self> {
let coords = coords.as_ref();
let i = *interval_id.as_ref();
SubIntervalInPartition::Middle(
<Self as Grid1DIntervalBuilder>::MiddleIntervalInPartition::new(
coords[i].clone(),
coords[i + 1].clone(),
),
)
}
fn build_last_interval(
&self,
coords: &Coords1D<Self::RealType>,
) -> SubIntervalInPartition<Self> {
let coords = coords.as_ref();
let last_id = coords.len() - 1;
SubIntervalInPartition::Last(
<Self as Grid1DIntervalBuilder>::LastIntervalInPartition::new(
coords[last_id - 1].clone(),
coords[last_id].clone(),
),
)
}
}
impl<RealType: RealScalar> Grid1DIntervalBuilder for IntervalFinitePositiveLength<RealType> {
type FirstIntervalInPartition = IntervalFinitePositiveLength<RealType>;
type MiddleIntervalInPartition = IntervalFinitePositiveLength<RealType>;
type LastIntervalInPartition = IntervalFinitePositiveLength<RealType>;
fn build_first_interval(
&self,
coords: &Coords1D<Self::RealType>,
) -> SubIntervalInPartition<Self> {
let coords = coords.as_ref();
let lower_bound = coords[0].clone();
let upper_bound = coords[1].clone();
let first_interval = match self {
IntervalFinitePositiveLength::Open(_)
| IntervalFinitePositiveLength::LowerOpenUpperClosed(_) => {
IntervalLowerOpenUpperClosed::new(lower_bound, upper_bound).into()
}
IntervalFinitePositiveLength::Closed(_)
| IntervalFinitePositiveLength::LowerClosedUpperOpen(_) => {
IntervalLowerClosedUpperOpen::new(lower_bound, upper_bound).into()
}
};
SubIntervalInPartition::First(first_interval)
}
fn build_middle_interval(
&self,
coords: &Coords1D<Self::RealType>,
interval_id: &IntervalId,
) -> SubIntervalInPartition<Self> {
let coords = coords.as_ref();
let i = *interval_id.as_ref();
let lower_bound = coords[i].clone();
let upper_bound = coords[i + 1].clone();
let middle_interval = match self {
IntervalFinitePositiveLength::Open(_)
| IntervalFinitePositiveLength::LowerOpenUpperClosed(_) => {
IntervalLowerOpenUpperClosed::new(lower_bound, upper_bound).into()
}
IntervalFinitePositiveLength::Closed(_)
| IntervalFinitePositiveLength::LowerClosedUpperOpen(_) => {
IntervalLowerClosedUpperOpen::new(lower_bound, upper_bound).into()
}
};
SubIntervalInPartition::Middle(middle_interval)
}
fn build_last_interval(
&self,
coords: &Coords1D<Self::RealType>,
) -> SubIntervalInPartition<Self> {
let coords = coords.as_ref();
let last_id = coords.len() - 1;
let lower_bound = coords[last_id - 1].clone();
let upper_bound = coords[last_id].clone();
let last_interval = match self {
IntervalFinitePositiveLength::Open(_) => {
IntervalOpen::new(lower_bound, upper_bound).into()
}
IntervalFinitePositiveLength::LowerOpenUpperClosed(_) => {
IntervalLowerOpenUpperClosed::new(lower_bound, upper_bound).into()
}
IntervalFinitePositiveLength::Closed(_) => {
IntervalClosed::new(lower_bound, upper_bound).into()
}
IntervalFinitePositiveLength::LowerClosedUpperOpen(_) => {
IntervalLowerClosedUpperOpen::new(lower_bound, upper_bound).into()
}
};
SubIntervalInPartition::Last(last_interval)
}
}
pub trait FindIntervalIdOfPoint {
type Point1DType: RealScalar;
fn find_interval_id_of_point(&self, x: &Self::Point1DType) -> Option<IntervalId>;
fn find_intervals_for_points(&self, points: &[Self::Point1DType]) -> Vec<Option<IntervalId>> {
points
.iter()
.map(|point| self.find_interval_id_of_point(point))
.collect()
}
fn find_intervals_for_points_parallel(
&self,
points: &[Self::Point1DType],
) -> Vec<Option<IntervalId>>
where
Self: Sync,
Self::Point1DType: Send + Sync,
{
points
.par_iter()
.map(|point| self.find_interval_id_of_point(point))
.collect()
}
}
pub trait HasIntervalIdRange {
fn first_interval_id(&self) -> IntervalId;
fn last_interval_id(&self) -> IntervalId;
#[inline]
fn num_intervals(&self) -> NumIntervals {
NumIntervals::try_new(
self.last_interval_id().into_inner() - self.first_interval_id().into_inner() + 1,
)
.unwrap()
}
#[inline]
fn range_contains_interval_id(&self, interval_id: &IntervalId) -> bool {
let first_id = self.first_interval_id();
let last_id = self.last_interval_id();
interval_id >= &first_id && interval_id <= &last_id
}
}
pub trait HasCoordIdRange {
fn first_coord_id(&self) -> CoordId;
fn last_coord_id(&self) -> CoordId;
#[inline]
fn num_coord_ids(&self) -> PositiveNumPoints1D {
PositiveNumPoints1D::try_new(
self.last_coord_id().into_inner() - self.first_coord_id().into_inner() + 1,
)
.unwrap()
}
#[inline]
fn range_contains_coord_id(&self, coord_id: &CoordId) -> bool {
let first_id = self.first_coord_id();
let last_id = self.last_coord_id();
coord_id >= &first_id && coord_id <= &last_id
}
}
pub trait HasIntervals: HasIntervalIdRange {
type IntervalType: IntervalTrait;
fn interval(&self, interval_id: &IntervalId) -> Self::IntervalType;
fn iter_intervals(&self) -> impl Iterator<Item = (IntervalId, Self::IntervalType)> + '_ {
let first_id = self.first_interval_id().into_inner();
let last_id = self.last_interval_id().into_inner();
(first_id..=last_id).map(move |i| {
let id = IntervalId::new(i);
let interval = self.interval(&id);
(id, interval)
})
}
}
impl<G: Grid1DTrait> HasIntervals for G {
type IntervalType = SubIntervalInPartition<G::Domain1D>;
fn interval(&self, i: &IntervalId) -> SubIntervalInPartition<G::Domain1D> {
debug_assert!(
self.range_contains_interval_id(i),
"Interval ID {:?} is out of range [{:?}, {:?}]",
i,
self.first_interval_id(),
self.last_interval_id()
);
let domain = self.domain();
let num_intervals = *self.num_intervals().as_ref();
if num_intervals == 1 {
return domain.clone().build_single_interval();
}
let coords = self.coords();
let i_usize = *i.as_ref();
if i_usize == 0 {
domain.build_first_interval(coords)
} else if i_usize == num_intervals - 1 {
domain.build_last_interval(coords)
} else {
domain.build_middle_interval(coords, i)
}
}
}
pub trait Grid1DTrait:
HasCoords1D
+ HasIntervals<IntervalType = SubIntervalInPartition<Self::Domain1D>>
+ FindIntervalIdOfPoint<Point1DType = <Self as HasCoords1D>::CoordType>
+ HasDomain1D<Domain1D: Grid1DIntervalBuilder<RealType = <Self as HasCoords1D>::CoordType>>
+ Serialize
+ for<'a> Deserialize<'a>
where
Self::Domain1D: Grid1DIntervalBuilder<RealType = Self::CoordType>,
{
fn index_spaces(&self) -> &Grid1DIndexSpaces;
fn get_interval_id_left_neighbor(&self, interval_id: &IntervalId) -> Option<IntervalId> {
self.index_spaces()
.interval_index_space()
.left_neighbor(interval_id)
}
fn get_interval_id_right_neighbor(&self, interval_id: &IntervalId) -> Option<IntervalId> {
self.index_spaces()
.interval_index_space()
.right_neighbor(interval_id)
}
fn get_interval_id_neighbor(
&self,
interval_id: &IntervalId,
side: &Side,
) -> Option<IntervalId> {
self.index_spaces()
.interval_index_space()
.neighbor(interval_id, side)
}
fn interval_length(&self, interval_id: &IntervalId) -> PositiveRealScalar<Self::Point1DType> {
let coords = self.coords().as_ref();
let i = *interval_id.as_ref();
debug_assert_lt!(i, coords.len() - 1, "Interval ID out of bounds");
PositiveRealScalar::try_new(coords[i + 1].clone() - &coords[i])
.expect("Non-positive interval length!")
}
fn max_interval_length(&self) -> PositiveRealScalar<Self::Point1DType> {
(0..*self.num_intervals().as_ref())
.map(|i| self.interval_length(&IntervalId::new(i)))
.max_by(|a, b| a.as_ref().partial_cmp(b.as_ref()).unwrap())
.unwrap()
}
fn min_interval_length(&self) -> PositiveRealScalar<Self::Point1DType> {
(0..*self.num_intervals().as_ref())
.map(|i| self.interval_length(&IntervalId::new(i)))
.min_by(|a, b| a.as_ref().partial_cmp(b.as_ref()).unwrap())
.unwrap()
}
fn uniformity_ratio(&self) -> PositiveRealScalar<Self::Point1DType> {
let max_len = self.max_interval_length();
let min_len = self.min_interval_length();
PositiveRealScalar::try_new(max_len.as_ref().clone() / min_len.as_ref())
.expect("Uniformity ratio must be positive")
}
fn intervals_in_intersection<
IntervalType: IntervalFinitePositiveLengthTrait<RealType = Self::Point1DType>,
>(
&self,
domain_in: &IntervalType,
) -> Vec<(IntervalId, IntervalFinitePositiveLength<Self::Point1DType>)> {
let overlap = match self.domain().intersection(domain_in) {
Some(Interval::FiniteLength(IntervalFiniteLength::PositiveLength(p))) => p,
_ => return Vec::new(),
};
let id_min = self
.find_interval_id_of_point(overlap.lower_bound_value())
.expect("Lower bound of overlap must be in domain");
let id_max = self
.find_interval_id_of_point(overlap.upper_bound_value())
.expect("Upper bound of overlap must be in domain");
let id_min_as_usize = *id_min.as_ref();
let id_max_as_usize = *id_max.as_ref();
let n_max_intersecting_intervals = id_max_as_usize - id_min_as_usize + 1;
let mut intersecting_intervals = Vec::with_capacity(n_max_intersecting_intervals);
for i in id_min_as_usize..=id_max_as_usize {
let current_id = IntervalId::new(i);
let grid_interval = self.interval(¤t_id);
if let Some(Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
intersection,
))) = grid_interval.intersection(&overlap)
{
intersecting_intervals.push((current_id, intersection));
}
}
debug_assert!(
!intersecting_intervals.is_empty(),
"Expected at least one interval with positive length in the intersection"
);
intersecting_intervals
}
type UniformlyRefinedGrid1DType: Grid1DTrait<
CoordType = Self::CoordType,
Point1DType = Self::Point1DType,
Domain1D = Self::Domain1D,
>;
fn refine_uniform(
self,
num_extra_points_each_interval: &PositiveNumPoints1D,
) -> Grid1DUniformRefinement<Self>;
fn refine(
self,
intervals_to_refine: &BTreeMap<IntervalId, PositiveNumPoints1D>,
) -> Grid1DNonUniformRefinement<Self>;
}
#[cfg(test)]
mod tests {
use super::*;
use crate::intervals::{Contains, bounded::IntervalFromBounds};
use crate::{Grid1D, Grid1DNonUniform, Grid1DUniform, IndexSpace1D, Topology1D};
use try_create::TryNew;
#[test]
fn test_has_domain_1d() {
let domain = IntervalClosed::new(0.0, 10.0);
let grid = Grid1DUniform::new(domain.clone(), NumIntervals::try_new(5).unwrap());
assert_eq!(grid.domain(), &domain);
}
#[test]
fn test_has_coords_1d() {
let grid = Grid1DUniform::new(
IntervalClosed::new(0.0, 1.0),
NumIntervals::try_new(4).unwrap(),
);
assert_eq!(grid.num_points().as_ref(), &5);
assert_eq!(grid.coords().len(), 5);
}
#[test]
fn test_has_coords_1d_coord_uniform() {
let grid = Grid1DUniform::new(
IntervalClosed::new(0.0, 1.0),
NumIntervals::try_new(4).unwrap(),
);
assert_eq!(grid.coord(CoordId::new(0)), &0.0);
assert_eq!(grid.coord(CoordId::new(2)), &0.5);
assert_eq!(grid.coord(CoordId::new(4)), &1.0);
}
#[test]
fn test_has_coords_1d_coord_non_uniform() {
let grid = Grid1DNonUniform::<IntervalClosed<f64>>::try_new_from_coords(
Coords1D::try_from(sorted_vec::partial::SortedSet::from_unsorted(vec![
0.0, 0.25, 1.0,
]))
.unwrap(),
)
.unwrap();
assert_eq!(grid.coord(CoordId::new(0)), &0.0);
assert_eq!(grid.coord(CoordId::new(1)), &0.25);
assert_eq!(grid.coord(CoordId::new(2)), &1.0);
}
#[test]
fn test_build_interval_in_partition_closed() {
let partition_domain = IntervalClosed::new(0.0, 3.0);
let coords = Coords1D::new_uniform(&partition_domain, &NumIntervals::try_new(3).unwrap());
let first = partition_domain.build_first_interval(&coords);
let middle = partition_domain.build_middle_interval(&coords, &IntervalId::new(1));
let last = partition_domain.build_last_interval(&coords);
assert!(first.contains_point(&0.0));
assert!(!first.contains_point(&1.0));
assert!(middle.contains_point(&1.0));
assert!(!middle.contains_point(&2.0));
assert!(last.contains_point(&2.0));
assert!(last.contains_point(&3.0));
}
#[test]
fn test_build_interval_in_partition_open() {
let partition_domain = IntervalOpen::new(0.0, 3.0);
let coords = Coords1D::new_uniform(&partition_domain, &NumIntervals::try_new(3).unwrap());
let first = partition_domain.build_first_interval(&coords);
let middle = partition_domain.build_middle_interval(&coords, &IntervalId::new(1));
let last = partition_domain.build_last_interval(&coords);
assert!(!first.contains_point(&0.0));
assert!(first.contains_point(&1.0));
assert!(!middle.contains_point(&1.0));
assert!(middle.contains_point(&2.0));
assert!(!last.contains_point(&2.0));
assert!(!last.contains_point(&3.0));
}
#[test]
fn test_build_interval_in_partition_lower_closed_upper_open() {
let partition_domain = IntervalLowerClosedUpperOpen::new(0.0, 3.0);
let coords = Coords1D::new_uniform(&partition_domain, &NumIntervals::try_new(3).unwrap());
let first = partition_domain.build_first_interval(&coords);
let middle = partition_domain.build_middle_interval(&coords, &IntervalId::new(1));
let last = partition_domain.build_last_interval(&coords);
assert!(first.contains_point(&0.0));
assert!(!first.contains_point(&1.0));
assert!(middle.contains_point(&1.0));
assert!(!middle.contains_point(&2.0));
assert!(last.contains_point(&2.0));
assert!(!last.contains_point(&3.0));
}
#[test]
fn test_build_interval_in_partition_lower_open_upper_closed() {
let partition_domain = IntervalLowerOpenUpperClosed::new(0.0, 3.0);
let coords = Coords1D::new_uniform(&partition_domain, &NumIntervals::try_new(3).unwrap());
let first = partition_domain.build_first_interval(&coords);
let middle = partition_domain.build_middle_interval(&coords, &IntervalId::new(1));
let last = partition_domain.build_last_interval(&coords);
assert!(!first.contains_point(&0.0));
assert!(first.contains_point(&1.0));
assert!(!middle.contains_point(&1.0));
assert!(middle.contains_point(&2.0));
assert!(!last.contains_point(&2.0));
assert!(last.contains_point(&3.0));
}
#[test]
fn test_build_interval_in_partition_enum_first_middle_open_branch() {
let coords = Coords1D::new_uniform(
&IntervalClosed::new(0.0, 3.0),
&NumIntervals::try_new(3).unwrap(),
);
let domain_enum: IntervalFinitePositiveLength<f64> = IntervalOpen::new(0.0, 3.0).into();
let first = domain_enum.build_first_interval(&coords);
let middle = domain_enum.build_middle_interval(&coords, &IntervalId::new(1));
match first {
SubIntervalInPartition::First(interval) => {
assert!(matches!(
interval,
IntervalFinitePositiveLength::LowerOpenUpperClosed(_)
));
}
_ => panic!("Expected first interval"),
}
match middle {
SubIntervalInPartition::Middle(interval) => {
assert!(matches!(
interval,
IntervalFinitePositiveLength::LowerOpenUpperClosed(_)
));
}
_ => panic!("Expected middle interval"),
}
}
#[test]
fn test_build_interval_in_partition_enum_first_middle_closed_branch() {
let coords = Coords1D::new_uniform(
&IntervalClosed::new(0.0, 3.0),
&NumIntervals::try_new(3).unwrap(),
);
let domain_enum: IntervalFinitePositiveLength<f64> = IntervalClosed::new(0.0, 3.0).into();
let first = domain_enum.build_first_interval(&coords);
let middle = domain_enum.build_middle_interval(&coords, &IntervalId::new(1));
match first {
SubIntervalInPartition::First(interval) => {
assert!(matches!(
interval,
IntervalFinitePositiveLength::LowerClosedUpperOpen(_)
));
}
_ => panic!("Expected first interval"),
}
match middle {
SubIntervalInPartition::Middle(interval) => {
assert!(matches!(
interval,
IntervalFinitePositiveLength::LowerClosedUpperOpen(_)
));
}
_ => panic!("Expected middle interval"),
}
}
#[test]
fn test_build_interval_in_partition_enum_last_open_branch() {
let coords = Coords1D::new_uniform(
&IntervalClosed::new(0.0, 3.0),
&NumIntervals::try_new(3).unwrap(),
);
let domain_enum: IntervalFinitePositiveLength<f64> = IntervalOpen::new(0.0, 3.0).into();
let last = domain_enum.build_last_interval(&coords);
match last {
SubIntervalInPartition::Last(interval) => {
assert!(matches!(interval, IntervalFinitePositiveLength::Open(_)));
}
_ => panic!("Expected last interval"),
}
}
#[test]
fn test_build_interval_in_partition_enum_last_lower_open_upper_closed_branch() {
let coords = Coords1D::new_uniform(
&IntervalClosed::new(0.0, 3.0),
&NumIntervals::try_new(3).unwrap(),
);
let domain_enum: IntervalFinitePositiveLength<f64> =
IntervalLowerOpenUpperClosed::new(0.0, 3.0).into();
let last = domain_enum.build_last_interval(&coords);
match last {
SubIntervalInPartition::Last(interval) => {
assert!(matches!(
interval,
IntervalFinitePositiveLength::LowerOpenUpperClosed(_)
));
}
_ => panic!("Expected last interval"),
}
}
#[test]
fn test_build_interval_in_partition_enum_last_closed_branch() {
let coords = Coords1D::new_uniform(
&IntervalClosed::new(0.0, 3.0),
&NumIntervals::try_new(3).unwrap(),
);
let domain_enum: IntervalFinitePositiveLength<f64> = IntervalClosed::new(0.0, 3.0).into();
let last = domain_enum.build_last_interval(&coords);
match last {
SubIntervalInPartition::Last(interval) => {
assert!(matches!(interval, IntervalFinitePositiveLength::Closed(_)));
}
_ => panic!("Expected last interval"),
}
}
#[test]
fn test_build_interval_in_partition_enum_last_lower_closed_upper_open_branch() {
let coords = Coords1D::new_uniform(
&IntervalClosed::new(0.0, 3.0),
&NumIntervals::try_new(3).unwrap(),
);
let domain_enum: IntervalFinitePositiveLength<f64> =
IntervalLowerClosedUpperOpen::new(0.0, 3.0).into();
let last = domain_enum.build_last_interval(&coords);
match last {
SubIntervalInPartition::Last(interval) => {
assert!(matches!(
interval,
IntervalFinitePositiveLength::LowerClosedUpperOpen(_)
));
}
_ => panic!("Expected last interval"),
}
}
#[test]
fn test_interval_partition_num_intervals() {
let grid = Grid1D::uniform(
IntervalClosed::new(0.0, 10.0),
NumIntervals::try_new(5).unwrap(),
);
assert_eq!(grid.num_intervals().as_ref(), &5);
assert_eq!(grid.num_points().as_ref(), &6);
}
#[test]
fn test_interval_partition_find_interval() {
let grid = Grid1DUniform::new(
IntervalClosed::new(0.0, 4.0),
NumIntervals::try_new(4).unwrap(),
);
assert_eq!(
grid.find_interval_id_of_point(&0.5).unwrap(),
IntervalId::new(0)
);
assert_eq!(
grid.find_interval_id_of_point(&1.5).unwrap(),
IntervalId::new(1)
);
assert_eq!(
grid.find_interval_id_of_point(&3.5).unwrap(),
IntervalId::new(3)
);
assert_eq!(
grid.find_interval_id_of_point(&1.0).unwrap(),
IntervalId::new(1)
);
assert_eq!(
grid.find_interval_id_of_point(&2.0).unwrap(),
IntervalId::new(2)
);
assert_eq!(
grid.find_interval_id_of_point(&0.0).unwrap(),
IntervalId::new(0)
);
assert_eq!(
grid.find_interval_id_of_point(&4.0).unwrap(),
IntervalId::new(3)
);
}
#[test]
fn test_interval_partition_try_find() {
let grid = Grid1DUniform::new(
IntervalClosed::new(0.0, 2.0),
NumIntervals::try_new(2).unwrap(),
);
assert_eq!(
grid.find_interval_id_of_point(&1.0),
Some(IntervalId::new(1)) );
assert_eq!(grid.find_interval_id_of_point(&-1.0), None);
assert_eq!(grid.find_interval_id_of_point(&3.0), None);
}
#[test]
fn test_interval_partition_uniformity() {
let uniform_grid = Grid1DUniform::new(
IntervalClosed::new(0.0, 1.0),
NumIntervals::try_new(10).unwrap(),
);
assert_eq!(uniform_grid.uniformity_ratio().as_ref(), &1.0);
assert_eq!(
uniform_grid.min_interval_length(),
uniform_grid.max_interval_length()
);
}
#[test]
fn test_side_helpers() {
assert!(Side::Left.is_left());
assert!(!Side::Left.is_right());
assert!(Side::Right.is_right());
assert!(!Side::Right.is_left());
}
#[test]
fn test_interval_neighbors_real_line_no_wraparound() {
let grid = Grid1DUniform::new(
IntervalClosed::new(0.0, 1.0),
NumIntervals::try_new(4).unwrap(),
);
let grid_index_spaces = grid.index_spaces();
assert_eq!(
grid_index_spaces.interval_index_space().topology(),
&Topology1D::RealLine
);
assert_eq!(
grid_index_spaces.coord_index_space().topology(),
&Topology1D::RealLine
);
assert_eq!(
grid.get_interval_id_left_neighbor(&IntervalId::new(0)),
None
);
assert_eq!(
grid.get_interval_id_right_neighbor(&IntervalId::new(0)),
Some(IntervalId::new(1))
);
assert_eq!(
grid.get_interval_id_left_neighbor(&IntervalId::new(3)),
Some(IntervalId::new(2))
);
assert_eq!(
grid.get_interval_id_right_neighbor(&IntervalId::new(3)),
None
);
assert_eq!(
grid.get_interval_id_neighbor(&IntervalId::new(2), &Side::Left),
Some(IntervalId::new(1))
);
assert_eq!(
grid.get_interval_id_neighbor(&IntervalId::new(2), &Side::Right),
Some(IntervalId::new(3))
);
}
#[test]
fn test_interval_neighbors_circle_wraparound_uniform() {
let grid = Grid1DUniform::new_periodic(
IntervalLowerClosedUpperOpen::new(0.0, 1.0),
NumIntervals::try_new(4).unwrap(),
);
let grid_index_spaces = grid.index_spaces();
assert_eq!(
grid_index_spaces.interval_index_space().topology(),
&Topology1D::Circle
);
assert_eq!(
grid_index_spaces.coord_index_space().topology(),
&Topology1D::Circle
);
assert_eq!(
grid.get_interval_id_left_neighbor(&IntervalId::new(0)),
Some(IntervalId::new(3))
);
assert_eq!(
grid.get_interval_id_right_neighbor(&IntervalId::new(3)),
Some(IntervalId::new(0))
);
assert_eq!(
grid.get_interval_id_neighbor(&IntervalId::new(0), &Side::Left),
Some(IntervalId::new(3))
);
assert_eq!(
grid.get_interval_id_neighbor(&IntervalId::new(3), &Side::Right),
Some(IntervalId::new(0))
);
}
#[test]
fn test_interval_neighbors_circle_wraparound_non_uniform() {
let coords = Coords1D::try_from(sorted_vec::partial::SortedSet::from_unsorted(vec![
0.0, 0.2, 0.7, 1.0,
]))
.unwrap();
let grid =
Grid1DNonUniform::<IntervalLowerClosedUpperOpen<f64>>::try_new_periodic_from_coords(
coords,
)
.unwrap();
let grid_index_spaces = grid.index_spaces();
assert_eq!(
grid_index_spaces.interval_index_space().topology(),
&Topology1D::Circle
);
assert_eq!(
grid_index_spaces.coord_index_space().topology(),
&Topology1D::Circle
);
assert_eq!(
grid.get_interval_id_left_neighbor(&IntervalId::new(0)),
Some(IntervalId::new(2))
);
assert_eq!(
grid.get_interval_id_right_neighbor(&IntervalId::new(2)),
Some(IntervalId::new(0))
);
}
}