#![deny(rustdoc::broken_intra_doc_links)]
use crate::{
intervals::{
GetLowerBoundValue, GetUpperBoundValue, IntervalBoundsRuntime,
IntervalFinitePositiveLengthTrait, IntervalTrait, bounded::IntervalClosed,
},
scalars::{HashableScalar, NumIntervals, PositiveNumPoints1D},
traits::{HasCoords1D, HasDomain1D},
};
use derive_more::{AsRef, Index};
use num_valid::{
RealScalar,
core::errors::capture_backtrace,
functions::{Pow, Reciprocal},
};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use sorted_vec::partial::SortedSet;
use std::{backtrace::Backtrace, collections::HashMap, ops::Deref};
use thiserror::Error;
use try_create::TryNew;
fn merge_sorted_real_with_dedup<T: RealScalar>(left: &[T], right: &[T]) -> SortedSet<T> {
let l_length = left.len();
let r_length = right.len();
let mut out = Vec::with_capacity(l_length + r_length);
let mut i = 0;
let mut j = 0;
while i < l_length && j < r_length {
match left[i]
.partial_cmp(&right[j])
.expect("Invalid coordinate: comparison failed (NaN or non-finite) in grid coordinates")
{
std::cmp::Ordering::Less => {
out.push(left[i].clone());
i += 1;
}
std::cmp::Ordering::Greater => {
out.push(right[j].clone());
j += 1;
}
std::cmp::Ordering::Equal => {
out.push(left[i].clone());
i += 1;
j += 1;
}
}
}
out.extend_from_slice(&left[i..]);
out.extend_from_slice(&right[j..]);
unsafe { SortedSet::from_sorted(out) }
}
pub fn linspace<IntervalType: IntervalFinitePositiveLengthTrait>(
domain: &IntervalType,
num_intervals: &NumIntervals,
) -> Vec<IntervalType::RealType> {
let n_intervals = *num_intervals.as_ref();
let n_points = n_intervals + 1;
let delta_points = domain.length().into_inner()
* IntervalType::RealType::try_from_f64(n_intervals as f64)
.unwrap()
.try_reciprocal()
.unwrap();
let lower_bound = domain.lower_bound_value();
let mut coords = Vec::<_>::with_capacity(n_points);
coords.push(lower_bound.clone());
for i in 1..n_intervals {
let x =
IntervalType::RealType::try_from_f64(i as f64).unwrap() * &delta_points + lower_bound;
coords.push(x);
}
coords.push(domain.upper_bound_value().clone());
coords
}
pub fn logspace<IntervalType: IntervalFinitePositiveLengthTrait>(
base: &IntervalType::RealType,
exponent_domain: &IntervalType,
num_intervals: &NumIntervals,
) -> Vec<IntervalType::RealType> {
let exponents = linspace(exponent_domain, num_intervals);
let out: Vec<_> = exponents
.iter()
.map(|v: &IntervalType::RealType| base.clone().try_pow(v).unwrap())
.collect();
out
}
#[derive(Debug, Error)]
pub enum ErrorsCoords1D {
#[error("The input coordinates are empty!")]
EmptyObject {
backtrace: Backtrace,
},
}
#[derive(Debug, Error)]
pub enum ErrorsCoords1DInDomain<IntervalType: IntervalTrait> {
#[error("The minimum coordinate of the points is smaller than the lower bound of the domain.")]
LowerBoundViolation {
coord_min: IntervalType::RealType,
domain: IntervalType,
backtrace: Backtrace,
},
#[error("The maximum coordinate of the points is greater than the upper bound of the domain.")]
UpperBoundViolation {
coord_max: IntervalType::RealType,
domain: IntervalType,
backtrace: Backtrace,
},
}
#[derive(Debug, Clone, AsRef, Index)]
pub struct Coords1D<RealType: RealScalar> {
#[as_ref]
#[index]
values: SortedSet<RealType>,
lookup_index: HashMap<HashableScalar<RealType>, usize>,
}
impl<RealType: RealScalar> PartialEq for Coords1D<RealType> {
fn eq(&self, other: &Self) -> bool {
self.values == other.values
}
}
impl<RealType: RealScalar> Deref for Coords1D<RealType> {
type Target = [RealType];
fn deref(&self) -> &Self::Target {
self.values.as_slice()
}
}
impl<RealType: RealScalar> Coords1D<RealType> {
#[inline(always)]
fn new(coords: SortedSet<RealType>) -> Self {
let lookup_index = coords
.iter()
.cloned()
.enumerate()
.map(|(i, v)| (HashableScalar::new(v), i))
.collect::<HashMap<HashableScalar<RealType>, usize>>();
Self {
values: coords,
lookup_index,
}
}
#[inline(always)]
pub fn into_vec(self) -> Vec<RealType> {
self.values.into_vec()
}
#[inline(always)]
pub fn num_points(&self) -> PositiveNumPoints1D {
PositiveNumPoints1D::try_new(self.values.len()).unwrap()
}
#[inline(always)]
pub fn new_uniform(
domain: &impl IntervalFinitePositiveLengthTrait<RealType = RealType>,
num_intervals: &NumIntervals,
) -> Self {
let coords = linspace(domain, num_intervals);
let coords = unsafe { SortedSet::from_sorted(coords) };
Self::new(coords)
}
#[inline(always)]
pub fn first(&self) -> &RealType {
&self[0]
}
#[inline(always)]
pub fn last(&self) -> &RealType {
&self[self.len() - 1]
}
#[inline(always)]
pub fn span(&self) -> RealType {
self.last().clone() - self.first()
}
#[inline(always)]
pub fn contains(&self, value: &RealType) -> bool {
self.lookup_index
.contains_key(&HashableScalar::new(value.clone()))
}
#[inline(always)]
pub fn find_index(&self, value: &RealType) -> Option<usize> {
self.lookup_index
.get(&HashableScalar::new(value.clone()))
.copied()
}
#[inline(always)]
pub fn find_insertion_index(&self, value: &RealType) -> usize {
self.partition_point(|coord| coord < value)
}
pub fn find_floor_index(&self, value: &RealType) -> Option<usize> {
let insertion_point = self.find_insertion_index(value);
if insertion_point == 0 && &self.values[0] > value {
return None;
}
if insertion_point < self.values.len() && &self.values[insertion_point] == value {
return Some(insertion_point);
}
more_asserts::debug_assert_gt!(insertion_point, 0, "insertion_point should be > 0 here");
Some(insertion_point - 1)
}
#[inline(always)]
pub fn find_ceil_index(&self, value: &RealType) -> Option<usize> {
let insertion_point = self.find_insertion_index(value);
if insertion_point < self.values.len() {
Some(insertion_point)
} else {
None
}
}
pub fn find_range_indices(
&self,
range_interval: &IntervalClosed<RealType>,
) -> std::ops::Range<usize> {
let start_idx = self
.find_ceil_index(range_interval.lower_bound_value())
.unwrap_or(self.values.len());
let end_idx = match self.find_floor_index(range_interval.upper_bound_value()) {
Some(idx) => idx + 1,
None => 0,
};
start_idx..end_idx.max(start_idx)
}
#[inline(always)]
pub fn range(
&self,
range_interval: &IntervalClosed<RealType>,
) -> impl Iterator<Item = &RealType> {
let range_indices = self.find_range_indices(range_interval);
self[range_indices].iter()
}
pub fn find_closest(&self, value: &RealType) -> (&RealType, usize) {
let insertion_point = self.find_insertion_index(value);
if insertion_point == 0 {
(&self.values[0], 0)
} else if insertion_point >= self.values.len() {
let last_idx = self.values.len() - 1;
(&self.values[last_idx], last_idx)
} else {
let left_idx = insertion_point - 1;
let right_idx = insertion_point;
let left_dist = (value.clone() - &self.values[left_idx]).abs();
let right_dist = (self.values[right_idx].clone() - value).abs();
if left_dist <= right_dist {
(&self.values[left_idx], left_idx)
} else {
(&self.values[right_idx], right_idx)
}
}
}
#[inline(always)]
pub fn union(&self, other: &Self) -> Self {
let merged = merge_sorted_real_with_dedup(self, other);
Self::new(merged)
}
pub fn intersection(&self, other: &Self) -> Option<Self> {
let len_self = self.len();
let len_other = other.len();
let mut intersection_vec = Vec::with_capacity(len_self.min(len_other));
let mut i = 0;
let mut j = 0;
while i < self.len() && j < other.len() {
match self[i].partial_cmp(&other[j]).unwrap() {
std::cmp::Ordering::Less => i += 1,
std::cmp::Ordering::Greater => j += 1,
std::cmp::Ordering::Equal => {
intersection_vec.push(self[i].clone());
i += 1;
j += 1;
}
}
}
if intersection_vec.is_empty() {
None
} else {
Some(Self::new(unsafe {
SortedSet::from_sorted(intersection_vec)
}))
}
}
#[inline(always)]
pub fn is_subset(&self, other: &Self) -> bool {
if self.len() > other.len() {
return false;
}
self.iter().all(|val| other.contains(val))
}
}
impl<RealType: RealScalar> TryFrom<SortedSet<RealType>> for Coords1D<RealType> {
type Error = ErrorsCoords1D;
#[inline(always)]
fn try_from(coords: SortedSet<RealType>) -> Result<Self, Self::Error> {
if coords.is_empty() {
Err(ErrorsCoords1D::EmptyObject {
backtrace: capture_backtrace(),
})
} else {
Ok(Self::new(coords))
}
}
}
impl<RealType: RealScalar> Serialize for Coords1D<RealType> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.values.as_slice().serialize(serializer)
}
}
impl<'de, RealType: RealScalar> Deserialize<'de> for Coords1D<RealType> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let vec = Vec::<RealType>::deserialize(deserializer)?;
let sorted_set = SortedSet::from_unsorted(vec);
Self::try_from(sorted_set)
.map_err(|e| serde::de::Error::custom(format!("Failed to create Coords1D: {}", e)))
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Coords1DInDomain<
IntervalType: IntervalTrait,
CoordsStorage = Coords1D<<IntervalType as IntervalBoundsRuntime>::RealType>,
> where
CoordsStorage: std::borrow::Borrow<Coords1D<<IntervalType as IntervalBoundsRuntime>::RealType>>,
{
coords: CoordsStorage,
domain: IntervalType,
}
pub type Coords1DInDomainOwned<IntervalType> =
Coords1DInDomain<IntervalType, Coords1D<<IntervalType as IntervalBoundsRuntime>::RealType>>;
pub type Coords1DInDomainBorrowed<'a, IntervalType> =
Coords1DInDomain<IntervalType, &'a Coords1D<<IntervalType as IntervalBoundsRuntime>::RealType>>;
pub type Coords1DInDomainArc<IntervalType> = Coords1DInDomain<
IntervalType,
std::sync::Arc<Coords1D<<IntervalType as IntervalBoundsRuntime>::RealType>>,
>;
pub type Coords1DInDomainRc<IntervalType> = Coords1DInDomain<
IntervalType,
std::rc::Rc<Coords1D<<IntervalType as IntervalBoundsRuntime>::RealType>>,
>;
pub type Coords1DInDomainBox<IntervalType> = Coords1DInDomain<
IntervalType,
Box<Coords1D<<IntervalType as IntervalBoundsRuntime>::RealType>>,
>;
pub type Coords1DInDomainCow<'a, IntervalType> = Coords1DInDomain<
IntervalType,
std::borrow::Cow<'a, Coords1D<<IntervalType as IntervalBoundsRuntime>::RealType>>,
>;
impl<IntervalType: IntervalTrait, CoordsStorage> Coords1DInDomain<IntervalType, CoordsStorage>
where
CoordsStorage: std::borrow::Borrow<Coords1D<<IntervalType as IntervalBoundsRuntime>::RealType>>,
{
pub fn try_new(
coords: CoordsStorage,
domain: IntervalType,
) -> Result<Self, ErrorsCoords1DInDomain<IntervalType>> {
let coords_ref = coords.borrow();
let coords_min = coords_ref.first();
if !domain.contains_point(coords_min) {
return Err(ErrorsCoords1DInDomain::LowerBoundViolation {
coord_min: coords_min.clone(),
domain,
backtrace: capture_backtrace(),
});
}
let coords_max = coords_ref.last();
if !domain.contains_point(coords_max) {
return Err(ErrorsCoords1DInDomain::UpperBoundViolation {
coord_max: coords_max.clone(),
domain,
backtrace: capture_backtrace(),
});
};
Ok(Coords1DInDomain { coords, domain })
}
}
impl<IntervalType: IntervalTrait>
Coords1DInDomain<IntervalType, Coords1D<<IntervalType as IntervalBoundsRuntime>::RealType>>
{
pub fn as_borrowed(
&self,
) -> Coords1DInDomain<IntervalType, &Coords1D<<IntervalType as IntervalBoundsRuntime>::RealType>>
where
IntervalType: Clone,
{
Coords1DInDomain {
coords: &self.coords,
domain: self.domain.clone(),
}
}
}
impl<IntervalType: IntervalTrait>
Coords1DInDomain<IntervalType, &Coords1D<<IntervalType as IntervalBoundsRuntime>::RealType>>
{
pub fn to_owned_coords(
&self,
) -> Coords1DInDomain<IntervalType, Coords1D<<IntervalType as IntervalBoundsRuntime>::RealType>>
where
IntervalType: Clone,
<IntervalType as IntervalBoundsRuntime>::RealType: Clone,
{
Coords1DInDomain {
coords: self.coords.clone(),
domain: self.domain.clone(),
}
}
}
impl<IntervalType: IntervalTrait, CoordsStorage> HasDomain1D
for Coords1DInDomain<IntervalType, CoordsStorage>
where
CoordsStorage: std::borrow::Borrow<Coords1D<<IntervalType as IntervalBoundsRuntime>::RealType>>,
{
type Domain1D = IntervalType;
fn domain(&self) -> &IntervalType {
&self.domain
}
}
impl<IntervalType: IntervalTrait, CoordsStorage> HasCoords1D
for Coords1DInDomain<IntervalType, CoordsStorage>
where
CoordsStorage: std::borrow::Borrow<Coords1D<<IntervalType as IntervalBoundsRuntime>::RealType>>,
{
type Point1DType = <IntervalType as IntervalBoundsRuntime>::RealType;
fn coords(&self) -> &Coords1D<<IntervalType as IntervalBoundsRuntime>::RealType> {
self.coords.borrow()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::intervals::*;
use sorted_vec::partial::SortedSet;
use std::ops::Deref;
mod native64_strict_finite {
use super::*;
use num_valid::RealNative64StrictFinite;
type Real = RealNative64StrictFinite;
mod coords1d {
use super::*;
#[test]
fn test_coords1d_creation() {
let sorted_set = SortedSet::from_unsorted(vec![
Real::try_new(1.0).unwrap(),
Real::try_new(0.0).unwrap(),
Real::try_new(2.0).unwrap(),
]);
let coords = Coords1D::try_from(sorted_set).unwrap();
assert_eq!(
coords.deref(),
&[
Real::try_new(0.0).unwrap(),
Real::try_new(1.0).unwrap(),
Real::try_new(2.0).unwrap()
]
);
}
#[test]
fn test_coords1d_empty_creation() {
let empty_set = SortedSet::<Real>::new();
let coords1d = Coords1D::try_from(empty_set);
matches!(coords1d, Err(ErrorsCoords1D::EmptyObject { .. }));
}
#[test]
fn test_new_uniform() {
let domain =
IntervalClosed::new(Real::try_new(0.0).unwrap(), Real::try_new(10.0).unwrap());
let coords = Coords1D::new_uniform(&domain, &NumIntervals::try_new(5).unwrap());
assert_eq!(
coords.deref(),
&[
Real::try_new(0.0).unwrap(),
Real::try_new(2.0).unwrap(),
Real::try_new(4.0).unwrap(),
Real::try_new(6.0).unwrap(),
Real::try_new(8.0).unwrap(),
Real::try_new(10.0).unwrap()
]
);
assert_eq!(coords.len(), 6);
}
#[test]
fn test_first_last_span() {
let coords = Coords1D::try_from(SortedSet::from_unsorted(vec![
Real::try_new(1.0).unwrap(),
Real::try_new(3.0).unwrap(),
Real::try_new(5.0).unwrap(),
Real::try_new(10.0).unwrap(),
]))
.unwrap();
assert_eq!(coords.first(), &Real::try_new(1.0).unwrap());
assert_eq!(coords.last(), &Real::try_new(10.0).unwrap());
assert_eq!(coords.span(), Real::try_new(9.0).unwrap());
}
#[test]
fn test_contains_and_find_index() {
let coords = Coords1D::try_from(SortedSet::from_unsorted(vec![
Real::try_new(1.0).unwrap(),
Real::try_new(3.0).unwrap(),
Real::try_new(5.0).unwrap(),
Real::try_new(7.0).unwrap(),
]))
.unwrap();
assert!(coords.contains(&Real::try_new(3.0).unwrap()));
assert!(coords.contains(&Real::try_new(7.0).unwrap()));
assert!(!coords.contains(&Real::try_new(4.0).unwrap()));
assert!(!coords.contains(&Real::try_new(0.0).unwrap()));
assert_eq!(coords.find_index(&Real::try_new(1.0).unwrap()), Some(0));
assert_eq!(coords.find_index(&Real::try_new(5.0).unwrap()), Some(2));
assert_eq!(coords.find_index(&Real::try_new(6.0).unwrap()), None);
}
#[test]
fn test_find_insertion_index() {
let coords = Coords1D::try_from(SortedSet::from_unsorted(vec![
Real::try_new(10.0).unwrap(),
Real::try_new(20.0).unwrap(),
Real::try_new(30.0).unwrap(),
]))
.unwrap();
assert_eq!(coords.find_insertion_index(&Real::try_new(5.0).unwrap()), 0); assert_eq!(
coords.find_insertion_index(&Real::try_new(10.0).unwrap()),
0
); assert_eq!(
coords.find_insertion_index(&Real::try_new(15.0).unwrap()),
1
); assert_eq!(
coords.find_insertion_index(&Real::try_new(20.0).unwrap()),
1
); assert_eq!(
coords.find_insertion_index(&Real::try_new(35.0).unwrap()),
3
); }
#[test]
fn test_find_floor_index() {
let coords = Coords1D::try_from(SortedSet::from_unsorted(vec![
Real::try_new(10.0).unwrap(),
Real::try_new(20.0).unwrap(),
Real::try_new(30.0).unwrap(),
]))
.unwrap();
assert_eq!(coords.find_floor_index(&Real::try_new(5.0).unwrap()), None);
assert_eq!(
coords.find_floor_index(&Real::try_new(10.0).unwrap()),
Some(0)
);
assert_eq!(
coords.find_floor_index(&Real::try_new(15.0).unwrap()),
Some(0)
);
assert_eq!(
coords.find_floor_index(&Real::try_new(29.9).unwrap()),
Some(1)
);
assert_eq!(
coords.find_floor_index(&Real::try_new(30.0).unwrap()),
Some(2)
);
assert_eq!(
coords.find_floor_index(&Real::try_new(35.0).unwrap()),
Some(2)
);
}
#[test]
fn test_find_ceil_index() {
let coords = Coords1D::try_from(SortedSet::from_unsorted(vec![
Real::try_new(10.0).unwrap(),
Real::try_new(20.0).unwrap(),
Real::try_new(30.0).unwrap(),
]))
.unwrap();
assert_eq!(
coords.find_ceil_index(&Real::try_new(5.0).unwrap()),
Some(0)
);
assert_eq!(
coords.find_ceil_index(&Real::try_new(10.0).unwrap()),
Some(0)
);
assert_eq!(
coords.find_ceil_index(&Real::try_new(10.1).unwrap()),
Some(1)
);
assert_eq!(
coords.find_ceil_index(&Real::try_new(30.0).unwrap()),
Some(2)
);
assert_eq!(coords.find_ceil_index(&Real::try_new(35.0).unwrap()), None);
}
#[test]
fn test_find_range_indices_and_range() {
let coords = Coords1D::try_from(SortedSet::from_unsorted(vec![
Real::try_new(1.0).unwrap(),
Real::try_new(2.0).unwrap(),
Real::try_new(3.0).unwrap(),
Real::try_new(4.0).unwrap(),
Real::try_new(5.0).unwrap(),
Real::try_new(6.0).unwrap(),
]))
.unwrap();
let range_interval = IntervalClosed::try_new(
Real::try_new(2.5).unwrap(),
Real::try_new(5.5).unwrap(),
)
.unwrap();
let indices = coords.find_range_indices(&range_interval);
assert_eq!(indices, 2..5);
let values: Vec<Real> = coords.range(&range_interval).cloned().collect();
assert_eq!(
values,
vec![
Real::try_new(3.0).unwrap(),
Real::try_new(4.0).unwrap(),
Real::try_new(5.0).unwrap()
]
);
let empty_interval = IntervalClosed::try_new(
Real::try_new(7.0).unwrap(),
Real::try_new(8.0).unwrap(),
)
.unwrap();
assert_eq!(coords.find_range_indices(&empty_interval), 6..6);
assert_eq!(coords.range(&empty_interval).count(), 0);
}
#[test]
fn test_find_closest() {
let coords = Coords1D::try_from(SortedSet::from_unsorted(vec![
Real::try_new(10.0).unwrap(),
Real::try_new(20.0).unwrap(),
Real::try_new(30.0).unwrap(),
]))
.unwrap();
assert_eq!(
coords.find_closest(&Real::try_new(5.0).unwrap()),
(&Real::try_new(10.0).unwrap(), 0)
); assert_eq!(
coords.find_closest(&Real::try_new(14.0).unwrap()),
(&Real::try_new(10.0).unwrap(), 0)
); assert_eq!(
coords.find_closest(&Real::try_new(16.0).unwrap()),
(&Real::try_new(20.0).unwrap(), 1)
); assert_eq!(
coords.find_closest(&Real::try_new(20.0).unwrap()),
(&Real::try_new(20.0).unwrap(), 1)
); assert_eq!(
coords.find_closest(&Real::try_new(35.0).unwrap()),
(&Real::try_new(30.0).unwrap(), 2)
); }
#[test]
fn test_set_operations() {
let coords1 = Coords1D::try_from(SortedSet::from_unsorted(vec![
Real::try_new(1.0).unwrap(),
Real::try_new(2.0).unwrap(),
Real::try_new(3.0).unwrap(),
Real::try_new(4.0).unwrap(),
]))
.unwrap();
let coords2 = Coords1D::try_from(SortedSet::from_unsorted(vec![
Real::try_new(3.0).unwrap(),
Real::try_new(4.0).unwrap(),
Real::try_new(5.0).unwrap(),
Real::try_new(6.0).unwrap(),
]))
.unwrap();
let coords3 = Coords1D::try_from(SortedSet::from_unsorted(vec![
Real::try_new(1.0).unwrap(),
Real::try_new(2.0).unwrap(),
]))
.unwrap();
let union = coords1.union(&coords2);
assert_eq!(
union.deref(),
&[
Real::try_new(1.0).unwrap(),
Real::try_new(2.0).unwrap(),
Real::try_new(3.0).unwrap(),
Real::try_new(4.0).unwrap(),
Real::try_new(5.0).unwrap(),
Real::try_new(6.0).unwrap()
]
);
let intersection = coords1.intersection(&coords2).unwrap();
assert_eq!(
intersection.deref(),
&[Real::try_new(3.0).unwrap(), Real::try_new(4.0).unwrap()]
);
assert!(coords1.intersection(&coords3).is_some());
assert!(coords2.intersection(&coords3).is_none());
assert!(coords3.is_subset(&coords1));
assert!(!coords1.is_subset(&coords3));
assert!(!coords1.is_subset(&coords2));
}
#[test]
fn test_into_vec() {
let coords = Coords1D::try_from(SortedSet::from_unsorted(vec![
Real::try_new(10.0).unwrap(),
Real::try_new(20.0).unwrap(),
Real::try_new(30.0).unwrap(),
]))
.unwrap();
let vec = coords.into_vec();
assert_eq!(
vec,
vec![
Real::try_new(10.0).unwrap(),
Real::try_new(20.0).unwrap(),
Real::try_new(30.0).unwrap()
]
);
}
#[test]
fn test_coords1d_num_points() {
let coords = Coords1D::try_from(SortedSet::from_unsorted(vec![
Real::try_new(1.0).unwrap(),
Real::try_new(2.0).unwrap(),
Real::try_new(3.0).unwrap(),
Real::try_new(4.0).unwrap(),
Real::try_new(5.0).unwrap(),
]))
.unwrap();
assert_eq!(
coords.num_points(),
PositiveNumPoints1D::try_new(5).unwrap()
);
}
#[test]
fn test_coords1d_deref() {
let coords = Coords1D::try_from(SortedSet::from_unsorted(vec![
Real::try_new(3.0).unwrap(),
Real::try_new(1.0).unwrap(),
Real::try_new(2.0).unwrap(),
]))
.unwrap();
let slice: &[Real] = &coords;
assert_eq!(
slice,
&[
Real::try_new(1.0).unwrap(),
Real::try_new(2.0).unwrap(),
Real::try_new(3.0).unwrap()
]
);
}
#[test]
fn test_coords1d_iterator() {
let coords = Coords1D::try_from(SortedSet::from_unsorted(vec![
Real::try_new(1.0).unwrap(),
Real::try_new(3.0).unwrap(),
Real::try_new(2.0).unwrap(),
]))
.unwrap();
let collected: Vec<Real> = coords.iter().cloned().collect();
assert_eq!(
collected,
vec![
Real::try_new(1.0).unwrap(),
Real::try_new(2.0).unwrap(),
Real::try_new(3.0).unwrap()
]
);
}
#[test]
fn test_coords1d_index() {
let coords = Coords1D::try_from(SortedSet::from_unsorted(vec![
Real::try_new(10.0).unwrap(),
Real::try_new(30.0).unwrap(),
Real::try_new(20.0).unwrap(),
]))
.unwrap();
assert_eq!(coords[0], Real::try_new(10.0).unwrap());
assert_eq!(coords[1], Real::try_new(20.0).unwrap());
assert_eq!(coords[2], Real::try_new(30.0).unwrap());
}
#[test]
fn test_coords1d_len() {
let coords = Coords1D::try_from(SortedSet::from_unsorted(vec![
Real::try_new(1.0).unwrap(),
Real::try_new(2.0).unwrap(),
Real::try_new(3.0).unwrap(),
]))
.unwrap();
assert_eq!(coords.len(), 3);
}
#[test]
fn test_coords1d_edge_cases_for_search() {
let coords =
Coords1D::try_from(SortedSet::from_unsorted(vec![Real::try_new(1.0).unwrap()]))
.unwrap();
assert_eq!(coords.find_floor_index(&Real::try_new(0.5).unwrap()), None);
assert_eq!(
coords.find_floor_index(&Real::try_new(1.0).unwrap()),
Some(0)
);
assert_eq!(
coords.find_floor_index(&Real::try_new(1.5).unwrap()),
Some(0)
);
assert_eq!(
coords.find_ceil_index(&Real::try_new(0.5).unwrap()),
Some(0)
);
assert_eq!(
coords.find_ceil_index(&Real::try_new(1.0).unwrap()),
Some(0)
);
assert_eq!(coords.find_ceil_index(&Real::try_new(1.5).unwrap()), None);
}
#[test]
fn test_coords1d_binary_search() {
let coords = Coords1D::try_from(SortedSet::from_unsorted(vec![
Real::try_new(1.0).unwrap(),
Real::try_new(3.0).unwrap(),
Real::try_new(5.0).unwrap(),
Real::try_new(7.0).unwrap(),
Real::try_new(9.0).unwrap(),
]))
.unwrap();
assert_eq!(
coords.binary_search_by(|probe| probe
.partial_cmp(&Real::try_new(3.0).unwrap())
.unwrap()),
Ok(1)
);
assert_eq!(
coords.binary_search_by(|probe| probe
.partial_cmp(&Real::try_new(7.0).unwrap())
.unwrap()),
Ok(3)
);
assert_eq!(
coords.binary_search_by(|probe| probe
.partial_cmp(&Real::try_new(4.0).unwrap())
.unwrap()),
Err(2)
);
assert_eq!(
coords.binary_search_by(|probe| probe
.partial_cmp(&Real::try_new(0.0).unwrap())
.unwrap()),
Err(0)
);
assert_eq!(
coords.binary_search_by(|probe| probe
.partial_cmp(&Real::try_new(10.0).unwrap())
.unwrap()),
Err(5)
);
}
#[test]
fn test_coords1d_find_index() {
let coords = Coords1D::try_from(SortedSet::from_unsorted(vec![
Real::try_new(1.0).unwrap(),
Real::try_new(3.0).unwrap(),
Real::try_new(5.0).unwrap(),
Real::try_new(7.0).unwrap(),
Real::try_new(9.0).unwrap(),
]))
.unwrap();
assert_eq!(coords.find_index(&Real::try_new(3.0).unwrap()), Some(1));
assert_eq!(coords.find_index(&Real::try_new(7.0).unwrap()), Some(3));
assert_eq!(coords.find_index(&Real::try_new(4.0).unwrap()), None);
assert_eq!(coords.find_index(&Real::try_new(0.0).unwrap()), None);
assert_eq!(coords.find_index(&Real::try_new(10.0).unwrap()), None);
}
#[test]
fn test_coords1d_partition_point() {
let coords = Coords1D::try_from(SortedSet::from_unsorted(vec![
Real::try_new(2.0).unwrap(),
Real::try_new(4.0).unwrap(),
Real::try_new(6.0).unwrap(),
Real::try_new(8.0).unwrap(),
]))
.unwrap();
assert_eq!(
coords.partition_point(|x| x < &Real::try_new(0.0).unwrap()),
0
);
assert_eq!(
coords.partition_point(|x| x < &Real::try_new(3.0).unwrap()),
1
);
assert_eq!(
coords.partition_point(|x| x < &Real::try_new(5.0).unwrap()),
2
);
assert_eq!(
coords.partition_point(|x| x < &Real::try_new(10.0).unwrap()),
4
);
}
#[test]
fn test_coords1d_complex_set_operations() {
let coords1 = Coords1D::try_from(SortedSet::from_unsorted(vec![
Real::try_new(1.0).unwrap(),
Real::try_new(3.0).unwrap(),
Real::try_new(5.0).unwrap(),
]))
.unwrap();
let coords2 = Coords1D::try_from(SortedSet::from_unsorted(vec![
Real::try_new(2.0).unwrap(),
Real::try_new(3.0).unwrap(),
Real::try_new(4.0).unwrap(),
]))
.unwrap();
let coords3 = Coords1D::try_from(SortedSet::from_unsorted(vec![
Real::try_new(5.0).unwrap(),
Real::try_new(6.0).unwrap(),
Real::try_new(7.0).unwrap(),
]))
.unwrap();
let union_12 = coords1.union(&coords2);
let union_all = union_12.union(&coords3);
assert_eq!(
union_all.deref(),
&[
Real::try_new(1.0).unwrap(),
Real::try_new(2.0).unwrap(),
Real::try_new(3.0).unwrap(),
Real::try_new(4.0).unwrap(),
Real::try_new(5.0).unwrap(),
Real::try_new(6.0).unwrap(),
Real::try_new(7.0).unwrap()
]
);
let small =
Coords1D::try_from(SortedSet::from_unsorted(vec![Real::try_new(3.0).unwrap()]))
.unwrap();
assert!(small.is_subset(&coords1));
assert!(small.is_subset(&coords2));
assert!(!small.is_subset(&coords3));
let coords4 = Coords1D::try_from(SortedSet::from_unsorted(vec![
Real::try_new(10.0).unwrap(),
Real::try_new(11.0).unwrap(),
]))
.unwrap();
assert!(coords1.intersection(&coords4).is_none());
}
#[test]
fn test_coords1d_serialization() {
let coords = Coords1D::try_from(SortedSet::from_unsorted(vec![
Real::try_new(1.0).unwrap(),
Real::try_new(3.0).unwrap(),
Real::try_new(2.0).unwrap(),
Real::try_new(5.0).unwrap(),
]))
.unwrap();
let json = serde_json::to_string(&coords).unwrap();
let deserialized: Coords1D<Real> = serde_json::from_str(&json).unwrap();
assert_eq!(coords, deserialized);
assert_eq!(
deserialized.deref(),
&[
Real::try_new(1.0).unwrap(),
Real::try_new(2.0).unwrap(),
Real::try_new(3.0).unwrap(),
Real::try_new(5.0).unwrap()
]
);
}
#[test]
fn test_coords1d_deserialization_empty_error() {
let json = "[]";
let result: Result<Coords1D<Real>, _> = serde_json::from_str(json);
assert!(result.is_err());
}
}
mod points_in_interval {
use super::*;
#[test]
fn test_points_in_interval_creation() {
let coords = Coords1D::try_from(SortedSet::from_unsorted(vec![
Real::try_new(0.0).unwrap(),
Real::try_new(0.5).unwrap(),
Real::try_new(1.0).unwrap(),
]))
.unwrap();
let domain =
IntervalClosed::new(Real::try_new(0.0).unwrap(), Real::try_new(1.0).unwrap());
let points = Coords1DInDomain::try_new(coords.clone(), domain.clone()).unwrap();
assert_eq!(points.coords(), &coords);
assert_eq!(points.domain(), &domain);
}
#[test]
fn test_points_in_interval_lower_bound_violation() {
let coords = Coords1D::try_from(SortedSet::from_unsorted(vec![
Real::try_new(-0.5).unwrap(),
Real::try_new(0.5).unwrap(),
Real::try_new(1.0).unwrap(),
]))
.unwrap();
let domain =
IntervalClosed::new(Real::try_new(0.0).unwrap(), Real::try_new(1.0).unwrap());
let pts = Coords1DInDomain::try_new(coords, domain.clone());
let _c = Real::try_new(-0.5).unwrap();
matches!(
pts,
Err(ErrorsCoords1DInDomain::LowerBoundViolation { coord_min: _c, .. })
);
}
#[test]
fn test_points_in_interval_upper_bound_violation() {
let coords = Coords1D::try_from(SortedSet::from_unsorted(vec![
Real::try_new(0.0).unwrap(),
Real::try_new(0.5).unwrap(),
Real::try_new(1.5).unwrap(),
]))
.unwrap();
let domain =
IntervalClosed::new(Real::try_new(0.0).unwrap(), Real::try_new(1.0).unwrap());
let pts = Coords1DInDomain::try_new(coords, domain.clone());
let _c = Real::try_new(1.5).unwrap();
matches!(
pts,
Err(ErrorsCoords1DInDomain::UpperBoundViolation { coord_max: _c, .. })
);
}
}
}
mod tests_coords1d_in_domain_generic {
use super::*;
#[test]
fn test_owned_coordinates() {
let coords = Coords1D::try_from(SortedSet::from_unsorted(vec![0.0, 0.5, 1.0])).unwrap();
let domain = IntervalClosed::new(0.0, 1.0);
let points_owned = Coords1DInDomain::try_new(coords, domain).unwrap();
assert_eq!(points_owned.coords().as_ref().as_slice(), &[0.0, 0.5, 1.0]);
assert_eq!(points_owned.domain(), &IntervalClosed::new(0.0, 1.0));
}
#[test]
fn test_borrowed_coordinates() {
let coords = Coords1D::try_from(SortedSet::from_unsorted(vec![0.0, 0.5, 1.0])).unwrap();
let domain = IntervalClosed::new(0.0, 1.0);
let points_borrowed = Coords1DInDomain::try_new(&coords, domain).unwrap();
assert_eq!(
points_borrowed.coords().as_ref().as_slice(),
&[0.0, 0.5, 1.0]
);
assert_eq!(points_borrowed.domain(), &IntervalClosed::new(0.0, 1.0));
}
#[test]
fn test_owned_to_borrowed_conversion() {
let coords = Coords1D::try_from(SortedSet::from_unsorted(vec![0.0, 0.5, 1.0])).unwrap();
let domain = IntervalClosed::new(0.0, 1.0);
let points_owned = Coords1DInDomain::try_new(coords, domain).unwrap();
let points_borrowed = points_owned.as_borrowed();
assert_eq!(points_owned.coords(), points_borrowed.coords());
assert_eq!(points_owned.domain(), points_borrowed.domain());
}
#[test]
fn test_borrowed_to_owned_conversion() {
let coords = Coords1D::try_from(SortedSet::from_unsorted(vec![0.0, 0.5, 1.0])).unwrap();
let domain = IntervalClosed::new(0.0, 1.0);
let points_borrowed = Coords1DInDomain::try_new(&coords, domain).unwrap();
let points_owned = points_borrowed.to_owned_coords();
assert_eq!(points_borrowed.coords(), points_owned.coords());
assert_eq!(points_borrowed.domain(), points_owned.domain());
}
#[test]
fn test_type_aliases() {
let coords = Coords1D::try_from(SortedSet::from_unsorted(vec![0.0, 0.5, 1.0])).unwrap();
let domain = IntervalClosed::new(0.0, 1.0);
let _owned: Coords1DInDomainOwned<IntervalClosed<f64>> =
Coords1DInDomain::try_new(coords.clone(), domain.clone()).unwrap();
let _borrowed: Coords1DInDomainBorrowed<IntervalClosed<f64>> =
Coords1DInDomain::try_new(&coords, domain).unwrap();
}
#[test]
fn test_validation_works_with_borrowed() {
let coords = Coords1D::try_from(SortedSet::from_unsorted(vec![0.0, 0.5, 2.0])).unwrap();
let domain = IntervalClosed::new(0.0, 1.0);
let result_owned = Coords1DInDomain::try_new(coords.clone(), domain.clone());
let result_borrowed = Coords1DInDomain::try_new(&coords, domain);
assert!(result_owned.is_err());
assert!(result_borrowed.is_err());
if let Err(ErrorsCoords1DInDomain::UpperBoundViolation { coord_max, .. }) = result_owned
{
assert_eq!(coord_max, 2.0);
} else {
panic!("Expected UpperBoundViolation");
}
}
#[test]
fn test_arc_coordinates() {
use std::sync::Arc;
let coords = Coords1D::try_from(SortedSet::from_unsorted(vec![0.0, 0.5, 1.0])).unwrap();
let domain = IntervalClosed::new(0.0, 1.0);
let arc_coords = Arc::new(coords);
let points_arc = Coords1DInDomain::try_new(arc_coords.clone(), domain).unwrap();
assert_eq!(points_arc.coords().as_ref().as_slice(), &[0.0, 0.5, 1.0]);
assert_eq!(points_arc.domain(), &IntervalClosed::new(0.0, 1.0));
let domain2 = IntervalClosed::new(0.0, 1.0);
let points_arc2 = Coords1DInDomain::try_new(arc_coords, domain2).unwrap();
assert_eq!(points_arc.coords(), points_arc2.coords());
}
#[test]
fn test_rc_coordinates() {
use std::rc::Rc;
let coords = Coords1D::try_from(SortedSet::from_unsorted(vec![0.0, 0.5, 1.0])).unwrap();
let domain = IntervalClosed::new(0.0, 1.0);
let rc_coords = Rc::new(coords);
let points_rc = Coords1DInDomain::try_new(rc_coords.clone(), domain).unwrap();
assert_eq!(points_rc.coords().as_ref().as_slice(), &[0.0, 0.5, 1.0]);
assert_eq!(points_rc.domain(), &IntervalClosed::new(0.0, 1.0));
let domain2 = IntervalClosed::new(0.0, 1.0);
let points_rc2 = Coords1DInDomain::try_new(rc_coords, domain2).unwrap();
assert_eq!(points_rc.coords(), points_rc2.coords());
}
#[test]
fn test_box_coordinates() {
let coords = Coords1D::try_from(SortedSet::from_unsorted(vec![0.0, 0.5, 1.0])).unwrap();
let domain = IntervalClosed::new(0.0, 1.0);
let boxed_coords = Box::new(coords);
let points_box = Coords1DInDomain::try_new(boxed_coords, domain).unwrap();
assert_eq!(points_box.coords().as_ref().as_slice(), &[0.0, 0.5, 1.0]);
assert_eq!(points_box.domain(), &IntervalClosed::new(0.0, 1.0));
}
#[test]
fn test_cow_coordinates() {
use std::borrow::Cow;
let coords = Coords1D::try_from(SortedSet::from_unsorted(vec![0.0, 0.5, 1.0])).unwrap();
let domain = IntervalClosed::new(0.0, 1.0);
let cow_owned = Cow::Owned(coords.clone());
let points_cow_owned = Coords1DInDomain::try_new(cow_owned, domain.clone()).unwrap();
let cow_borrowed = Cow::Borrowed(&coords);
let points_cow_borrowed = Coords1DInDomain::try_new(cow_borrowed, domain).unwrap();
assert_eq!(points_cow_owned.coords(), points_cow_borrowed.coords());
}
#[test]
fn test_smart_pointer_type_aliases() {
use std::borrow::Cow;
use std::rc::Rc;
use std::sync::Arc;
let coords = Coords1D::try_from(SortedSet::from_unsorted(vec![0.0, 0.5, 1.0])).unwrap();
let domain = IntervalClosed::new(0.0, 1.0);
let arc_coords = Arc::new(coords.clone());
let _points_arc: Coords1DInDomainArc<IntervalClosed<f64>> =
Coords1DInDomain::try_new(arc_coords, domain.clone()).unwrap();
let rc_coords = Rc::new(coords.clone());
let _points_rc: Coords1DInDomainRc<IntervalClosed<f64>> =
Coords1DInDomain::try_new(rc_coords, domain.clone()).unwrap();
let box_coords = Box::new(coords.clone());
let _points_box: Coords1DInDomainBox<IntervalClosed<f64>> =
Coords1DInDomain::try_new(box_coords, domain.clone()).unwrap();
let cow_coords = Cow::Owned(coords);
let _points_cow: Coords1DInDomainCow<IntervalClosed<f64>> =
Coords1DInDomain::try_new(cow_coords, domain).unwrap();
}
#[test]
fn test_smart_pointer_trait_methods() {
use std::sync::Arc;
let coords = Coords1D::try_from(SortedSet::from_unsorted(vec![0.0, 0.5, 1.0])).unwrap();
let domain = IntervalClosed::new(0.0, 1.0);
let arc_coords = Arc::new(coords.clone());
let points_arc = Coords1DInDomain::try_new(arc_coords, domain.clone()).unwrap();
let points_owned = Coords1DInDomain::try_new(coords.clone(), domain.clone()).unwrap();
let points_borrowed = Coords1DInDomain::try_new(&coords, domain).unwrap();
assert_eq!(points_arc.coords(), points_owned.coords());
assert_eq!(points_arc.coords(), points_borrowed.coords());
assert_eq!(points_arc.domain(), points_owned.domain());
assert_eq!(points_arc.domain(), points_borrowed.domain());
}
}
mod tests_transformed_points1d_generic {
use super::*;
use crate::traits::{
TransformedPoints1D, TransformedPoints1DArc, TransformedPoints1DBorrowed,
TransformedPoints1DOwned, TransformedPoints1DRc,
};
use std::sync::Arc;
struct TestTransformOwned {
param_points: Coords1DInDomain<IntervalClosed<f64>>,
phys_points: Coords1DInDomain<IntervalClosed<f64>>,
}
impl TransformedPoints1D<Coords1D<f64>, Coords1D<f64>> for TestTransformOwned {
type ParamDomainType = IntervalClosed<f64>;
type PhysDomainType = IntervalClosed<f64>;
fn points_param_interval(
&self,
) -> &Coords1DInDomain<Self::ParamDomainType, Coords1D<f64>> {
&self.param_points
}
fn points_phys_interval(
&self,
) -> &Coords1DInDomain<Self::PhysDomainType, Coords1D<f64>> {
&self.phys_points
}
}
struct TestTransformBorrowed<'a> {
param_points: Coords1DInDomain<IntervalClosed<f64>, &'a Coords1D<f64>>,
phys_points: Coords1DInDomain<IntervalClosed<f64>, &'a Coords1D<f64>>,
}
impl<'a> TransformedPoints1D<&'a Coords1D<f64>, &'a Coords1D<f64>> for TestTransformBorrowed<'a> {
type ParamDomainType = IntervalClosed<f64>;
type PhysDomainType = IntervalClosed<f64>;
fn points_param_interval(
&self,
) -> &Coords1DInDomain<Self::ParamDomainType, &'a Coords1D<f64>> {
&self.param_points
}
fn points_phys_interval(
&self,
) -> &Coords1DInDomain<Self::PhysDomainType, &'a Coords1D<f64>> {
&self.phys_points
}
}
struct TestTransformArc {
param_points: Coords1DInDomain<IntervalClosed<f64>, Arc<Coords1D<f64>>>,
phys_points: Coords1DInDomain<IntervalClosed<f64>, Arc<Coords1D<f64>>>,
}
impl TransformedPoints1D<Arc<Coords1D<f64>>, Arc<Coords1D<f64>>> for TestTransformArc {
type ParamDomainType = IntervalClosed<f64>;
type PhysDomainType = IntervalClosed<f64>;
fn points_param_interval(
&self,
) -> &Coords1DInDomain<Self::ParamDomainType, Arc<Coords1D<f64>>> {
&self.param_points
}
fn points_phys_interval(
&self,
) -> &Coords1DInDomain<Self::PhysDomainType, Arc<Coords1D<f64>>> {
&self.phys_points
}
}
#[test]
fn test_transformed_points_owned_storage() {
let param_coords =
Coords1D::try_from(SortedSet::from_unsorted(vec![-1.0, 0.0, 1.0])).unwrap();
let phys_coords =
Coords1D::try_from(SortedSet::from_unsorted(vec![0.0, 0.5, 1.0])).unwrap();
let param_domain = IntervalClosed::new(-1.0, 1.0);
let phys_domain = IntervalClosed::new(0.0, 1.0);
let transform = TestTransformOwned {
param_points: Coords1DInDomain::try_new(param_coords, param_domain).unwrap(),
phys_points: Coords1DInDomain::try_new(phys_coords, phys_domain).unwrap(),
};
assert_eq!(
transform
.points_param_interval()
.coords()
.as_ref()
.as_slice(),
&[-1.0, 0.0, 1.0]
);
assert_eq!(
transform
.points_phys_interval()
.coords()
.as_ref()
.as_slice(),
&[0.0, 0.5, 1.0]
);
}
#[test]
fn test_transformed_points_borrowed_storage() {
let param_coords =
Coords1D::try_from(SortedSet::from_unsorted(vec![-1.0, 0.0, 1.0])).unwrap();
let phys_coords =
Coords1D::try_from(SortedSet::from_unsorted(vec![0.0, 0.5, 1.0])).unwrap();
let param_domain = IntervalClosed::new(-1.0, 1.0);
let phys_domain = IntervalClosed::new(0.0, 1.0);
let transform = TestTransformBorrowed {
param_points: Coords1DInDomain::try_new(¶m_coords, param_domain).unwrap(),
phys_points: Coords1DInDomain::try_new(&phys_coords, phys_domain).unwrap(),
};
assert_eq!(
transform
.points_param_interval()
.coords()
.as_ref()
.as_slice(),
&[-1.0, 0.0, 1.0]
);
assert_eq!(
transform
.points_phys_interval()
.coords()
.as_ref()
.as_slice(),
&[0.0, 0.5, 1.0]
);
}
#[test]
fn test_transformed_points_arc_storage() {
let param_coords = Arc::new(
Coords1D::try_from(SortedSet::from_unsorted(vec![-1.0, 0.0, 1.0])).unwrap(),
);
let phys_coords = Arc::new(
Coords1D::try_from(SortedSet::from_unsorted(vec![0.0, 0.5, 1.0])).unwrap(),
);
let param_domain = IntervalClosed::new(-1.0, 1.0);
let phys_domain = IntervalClosed::new(0.0, 1.0);
let transform = TestTransformArc {
param_points: Coords1DInDomain::try_new(param_coords.clone(), param_domain)
.unwrap(),
phys_points: Coords1DInDomain::try_new(phys_coords.clone(), phys_domain).unwrap(),
};
assert_eq!(
transform
.points_param_interval()
.coords()
.as_ref()
.as_slice(),
&[-1.0, 0.0, 1.0]
);
assert_eq!(
transform
.points_phys_interval()
.coords()
.as_ref()
.as_slice(),
&[0.0, 0.5, 1.0]
);
let _param_clone = transform.param_points.clone();
assert!(Arc::strong_count(¶m_coords) >= 2); }
#[test]
fn test_type_aliases_compile() {
type _OwnedAlias = TransformedPoints1DOwned<IntervalClosed<f64>, IntervalClosed<f64>>;
type _BorrowedAlias<'a> =
TransformedPoints1DBorrowed<'a, IntervalClosed<f64>, IntervalClosed<f64>>;
type _ArcAlias = TransformedPoints1DArc<IntervalClosed<f64>, IntervalClosed<f64>>;
type _RcAlias = TransformedPoints1DRc<IntervalClosed<f64>, IntervalClosed<f64>>;
}
}
mod test_logspace {
use super::*;
use crate::{logspace, scalars::NumIntervals};
use try_create::TryNew;
#[test]
fn test_logspace_basic() {
let base = 10.0;
let exp_domain = IntervalClosed::new(0.0, 2.0); let coords = logspace(&base, &exp_domain, &NumIntervals::try_new(2).unwrap());
assert_eq!(coords.len(), 3);
assert!((coords[0] - 1.0).abs() < 1e-10); assert!((coords[1] - 10.0).abs() < 1e-10); assert!((coords[2] - 100.0).abs() < 1e-10); }
#[test]
fn test_logspace_base_2() {
let base = 2.0;
let exp_domain = IntervalClosed::new(0.0, 3.0); let coords = logspace(&base, &exp_domain, &NumIntervals::try_new(3).unwrap());
assert_eq!(coords.len(), 4);
assert!((coords[0] - 1.0).abs() < 1e-10); assert!((coords[1] - 2.0).abs() < 1e-10); assert!((coords[2] - 4.0).abs() < 1e-10); assert!((coords[3] - 8.0).abs() < 1e-10); }
#[test]
fn test_logspace_negative_exponents() {
let base = 10.0;
let exp_domain = IntervalClosed::new(-2.0, 0.0); let coords = logspace(&base, &exp_domain, &NumIntervals::try_new(2).unwrap());
assert_eq!(coords.len(), 3);
assert!((coords[0] - 0.01).abs() < 1e-10); assert!((coords[1] - 0.1).abs() < 1e-10); assert!((coords[2] - 1.0).abs() < 1e-10); }
#[test]
fn test_logspace_fractional_base() {
let base = 0.5;
let exp_domain = IntervalClosed::new(0.0, 2.0); let coords = logspace(&base, &exp_domain, &NumIntervals::try_new(2).unwrap());
assert_eq!(coords.len(), 3);
assert!((coords[0] - 1.0).abs() < 1e-10); assert!((coords[1] - 0.5).abs() < 1e-10); assert!((coords[2] - 0.25).abs() < 1e-10); }
#[test]
fn test_logspace_single_interval() {
let base = 10.0;
let exp_domain = IntervalClosed::new(0.0, 1.0);
let coords = logspace(&base, &exp_domain, &NumIntervals::try_new(1).unwrap());
assert_eq!(coords.len(), 2);
assert!((coords[0] - 1.0).abs() < 1e-10); assert!((coords[1] - 10.0).abs() < 1e-10); }
#[test]
fn test_logspace_many_intervals() {
let base = 2.0;
let exp_domain = IntervalClosed::new(0.0, 1.0);
let coords = logspace(&base, &exp_domain, &NumIntervals::try_new(10).unwrap());
assert_eq!(coords.len(), 11);
assert!((coords[0] - 1.0).abs() < 1e-10); assert!((coords[10] - 2.0).abs() < 1e-10);
for i in 0..coords.len() - 1 {
assert!(coords[i] < coords[i + 1]);
}
}
#[test]
fn test_logspace_with_half_open_interval() {
let base = 10.0;
let exp_domain = IntervalLowerClosedUpperOpen::new(0.0, 2.0);
let coords = logspace(&base, &exp_domain, &NumIntervals::try_new(2).unwrap());
assert_eq!(coords.len(), 3);
more_asserts::assert_lt!((coords[0] - 1.0).abs(), 1e-10);
more_asserts::assert_lt!((coords[2] - 100.0).abs(), 1e-10);
}
}
mod test_find_range_indices_edge_cases {
use super::*;
use sorted_vec::partial::SortedSet;
#[test]
fn test_find_range_indices_all_coords_above_upper_bound() {
let coords =
Coords1D::try_from(SortedSet::from_unsorted(vec![10.0, 20.0, 30.0, 40.0, 50.0]))
.unwrap();
let range = coords.find_range_indices(&IntervalClosed::new(0.0, 5.0));
let indices: Vec<usize> = range.collect();
assert!(indices.is_empty());
}
#[test]
fn test_find_range_indices_all_coords_below_lower_bound() {
let coords =
Coords1D::try_from(SortedSet::from_unsorted(vec![1.0, 2.0, 3.0, 4.0, 5.0]))
.unwrap();
let range = coords.find_range_indices(&IntervalClosed::new(10.0, 20.0));
let indices: Vec<usize> = range.collect();
assert!(indices.is_empty());
}
#[test]
fn test_find_range_indices_partial_overlap() {
let coords =
Coords1D::try_from(SortedSet::from_unsorted(vec![1.0, 2.0, 3.0, 4.0, 5.0]))
.unwrap();
let range = coords.find_range_indices(&IntervalClosed::new(2.5, 4.5));
let indices: Vec<usize> = range.collect();
assert_eq!(indices, vec![2, 3]);
}
#[test]
fn test_find_range_indices_exact_boundaries() {
let coords =
Coords1D::try_from(SortedSet::from_unsorted(vec![1.0, 2.0, 3.0, 4.0, 5.0]))
.unwrap();
let range = coords.find_range_indices(&IntervalClosed::new(2.0, 4.0));
let indices: Vec<usize> = range.collect();
assert_eq!(indices, vec![1, 2, 3]);
}
}
}