use core::convert::TryFrom;
use core::array::TryFromSliceError;
use core::ops::{Deref, DerefMut};
#[cfg(any(feature = "x", feature = "y", feature = "z", feature = "w"))]
use core::ops::AddAssign;
#[cfg(any(feature = "appliers", feature = "var-dims"))]
use arrayvec::ArrayVec;
#[cfg(any(feature = "appliers", feature = "var-dims"))]
use crate::utils::ARRVEC_CAP;
#[cfg(any(feature = "appliers", feature = "var-dims"))]
use crate::utils::arrvec_into_inner;
#[cfg(feature = "appliers")]
use crate::utils::{ApplyFn, ApplyDimsFn, ApplyValsFn, ApplyPointFn};
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct PointND<T, const N: usize>([T; N]);
impl<T, const N: usize> PointND<T, N>
where T: Copy {
pub fn from_slice(slice: &[T]) -> Self {
let arr: [T; N] = slice.try_into().unwrap();
PointND::from(arr)
}
pub fn fill(value: T) -> Self {
PointND::from([value; N])
}
}
impl<T, const N: usize> PointND<T, N> {
pub fn dims(&self) -> usize {
self.0.len()
}
pub fn into_arr(self) -> [T; N] {
self.0
}
#[cfg(any(feature = "appliers", feature = "var-dims"))]
fn _check_arrvec_cap(&self, cap: usize, method_name: &str) {
if cap > ARRVEC_CAP {
panic!("Attempted to call {}() on PointND with more than u32::MAX dimensions", method_name);
}
}
#[cfg(feature = "appliers")]
pub fn apply<U>(self, modifier: ApplyFn<T, U>) -> PointND<U, N> {
self._check_arrvec_cap(N, "apply");
let mut arr_v = ArrayVec::<U, N>::new();
let mut this = ArrayVec::from(self.into_arr());
for _ in 0..N {
let item = this.pop_at(0).unwrap();
arr_v.push(modifier(item));
}
PointND::from(
arrvec_into_inner(arr_v, "apply")
)
}
#[cfg(feature = "appliers")]
pub fn apply_dims(self, dims: &[usize], modifier: ApplyDimsFn<T>) -> Self {
self._check_arrvec_cap(N, "apply_dims");
let mut arr_v = ArrayVec::<T, N>::new();
let mut this = ArrayVec::from(self.into_arr());
for i in 0..N {
let item = this.pop_at(0).unwrap();
if dims.contains(&i) {
arr_v.push(modifier(item));
} else {
arr_v.push(item);
}
}
PointND::from(
arrvec_into_inner(arr_v, "apply_dims")
)
}
#[cfg(feature = "appliers")]
pub fn apply_vals<U, V>(
self,
values: [V; N],
modifier: ApplyValsFn<T, U, V>
) -> PointND<U, N> {
self._check_arrvec_cap(N, "apply_vals");
let mut arr_v = ArrayVec::<U, N>::new();
let mut vals = ArrayVec::from(values);
let mut this = ArrayVec::from(self.into_arr());
for _ in 0..N {
let a = this.pop_at(0).unwrap();
let b = vals.pop_at(0).unwrap();
arr_v.push(modifier(a, b));
}
PointND::from(
arrvec_into_inner(arr_v, "apply_vals() or apply_point")
)
}
#[cfg(feature = "appliers")]
pub fn apply_point<U, V>(
self,
other: PointND<V, N>,
modifier: ApplyPointFn<T, U, V>
) -> PointND<U, N> {
self._check_arrvec_cap(N, "apply_point");
self.apply_vals(other.into_arr(), modifier)
}
#[cfg(feature = "var-dims")]
pub fn extend<const L: usize, const M: usize>(self, values: [T; L]) -> PointND<T, M> {
self._check_arrvec_cap(N, "extend");
if N + L > ARRVEC_CAP {
panic!("Attempted to extend() a PointND to more than u32::MAX dimensions");
}
let mut arr_v = ArrayVec::<T, M>::new();
let mut this = ArrayVec::from(self.into_arr());
let mut vals = ArrayVec::from(values);
for _ in 0..N { arr_v.push(this.pop_at(0).unwrap()); }
for _ in 0..L { arr_v.push(vals.pop_at(0).unwrap()); }
PointND::from(
arrvec_into_inner(arr_v, "extend")
)
}
#[cfg(feature = "var-dims")]
pub fn retain<const M: usize>(self, dims: usize) -> PointND<T, M> {
self._check_arrvec_cap(N, "retain");
if dims > N || M > N {
panic!("Attempted to contract PointND to more dimensions than it had originally. Try \
passing a usize value that is less than the dimensions of the original point");
}
let mut arr_v = ArrayVec::<T, M>::new();
let mut this = ArrayVec::from(self.into_arr());
for _ in 0..dims {
let item = this.pop_at(0).unwrap();
arr_v.push(item);
}
PointND::from(
arrvec_into_inner(arr_v, "retain")
)
}
}
impl<T, const N: usize> Deref for PointND<T, N> {
type Target = [T; N];
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T, const N: usize> DerefMut for PointND<T, N> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
#[cfg(feature = "x")]
impl<T> PointND<T, 1> {
pub fn x(&self) -> &T { &self[0] }
pub fn set_x(&mut self, new_value: T) { self[0] = new_value; }
}
#[cfg(feature = "y")]
impl<T> PointND<T, 2> {
pub fn x(&self) -> &T { &self[0] }
pub fn y(&self) -> &T { &self[1] }
pub fn set_x(&mut self, new_value: T) { self[0] = new_value; }
pub fn set_y(&mut self, new_value: T) { self[1] = new_value; }
}
#[cfg(feature = "z")]
impl<T> PointND<T, 3> {
pub fn x(&self) -> &T { &self[0] }
pub fn y(&self) -> &T { &self[1] }
pub fn z(&self) -> &T { &self[2] }
pub fn set_x(&mut self, new_value: T) { self[0] = new_value; }
pub fn set_y(&mut self, new_value: T) { self[1] = new_value; }
pub fn set_z(&mut self, new_value: T) { self[2] = new_value; }
}
#[cfg(feature = "w")]
impl<T> PointND<T, 4> {
pub fn x(&self) -> &T { &self[0] }
pub fn y(&self) -> &T { &self[1] }
pub fn z(&self) -> &T { &self[2] }
pub fn w(&self) -> &T { &self[3] }
pub fn set_x(&mut self, new_value: T) { self[0] = new_value; }
pub fn set_y(&mut self, new_value: T) { self[1] = new_value; }
pub fn set_z(&mut self, new_value: T) { self[2] = new_value; }
pub fn set_w(&mut self, new_value: T) { self[3] = new_value; }
}
#[cfg(feature = "x")]
impl<T> PointND<T, 1>
where T: AddAssign {
pub fn shift_x(&mut self, delta: T) { self[0] += delta; }
}
#[cfg(feature = "y")]
impl<T> PointND<T, 2>
where T: AddAssign {
pub fn shift_x(&mut self, delta: T) { self[0] += delta; }
pub fn shift_y(&mut self, delta: T) { self[1] += delta; }
}
#[cfg(feature = "z")]
impl<T> PointND<T, 3>
where T: AddAssign {
pub fn shift_x(&mut self, delta: T) { self[0] += delta; }
pub fn shift_y(&mut self, delta: T) { self[1] += delta; }
pub fn shift_z(&mut self, delta: T) { self[2] += delta; }
}
#[cfg(feature = "w")]
impl<T> PointND<T, 4>
where T: AddAssign {
pub fn shift_x(&mut self, delta: T) { self[0] += delta; }
pub fn shift_y(&mut self, delta: T) { self[1] += delta; }
pub fn shift_z(&mut self, delta: T) { self[2] += delta; }
pub fn shift_w(&mut self, delta: T) { self[3] += delta; }
}
impl<T, const N: usize> From<[T; N]> for PointND<T, N> {
fn from(array: [T; N]) -> Self {
PointND(array)
}
}
impl<T, const N: usize> From<PointND<T, N>> for [T; N] {
fn from(point: PointND<T, N>) -> Self {
point.into_arr()
}
}
impl<T, const N: usize> TryFrom<&[T]> for PointND<T, N>
where T: Copy {
type Error = TryFromSliceError;
fn try_from(slice: &[T]) -> Result<Self, Self::Error> {
let res: Result<[T; N], _> = slice.try_into();
match res {
Ok(arr) => Ok( PointND(arr) ),
Err(err) => Err( err )
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(test)]
mod iterating {
use super::*;
#[test]
fn can_iter() {
let arr = [0, 1, 2, 3];
let p = PointND::<u8, 4>::from_slice(&arr);
for (i, item) in p.iter().enumerate() {
assert_eq!(arr[i], *item);
}
let mut p = PointND::<u8, 4>::from_slice(&arr);
for item in p.iter_mut() {
*item = 10;
}
for i in p.into_iter() {
assert_eq!(i, 10u8);
}
}
}
#[cfg(test)]
mod constructors {
use super::*;
#[test]
fn from_slice_works() {
let arr = [0.0, 0.1, 0.2];
let p = PointND::<f64, 3>::from_slice(&arr);
for i in 0..p.dims() {
assert_eq!(arr[i], p[i]);
}
}
#[test]
fn fill_works() {
let fill_val = 21u8;
let p = PointND::<u8, 5>::fill(fill_val);
for i in p.into_iter() {
assert_eq!(i, fill_val);
}
}
}
#[cfg(test)]
mod indexing {
use super::*;
#[test]
fn can_get_slice_by_range_index() {
let p = PointND::from([0,1,2,3,4]);
let slice = &p[0..3];
assert_eq!(slice, [0,1,2]);
}
#[test]
#[should_panic]
fn cannot_get_out_of_bounds_index() {
let p = PointND::from([0,1,2]);
let _x = p[p.dims() + 1];
}
#[test]
fn can_set_value_by_index() {
let mut p = PointND::from([0,1,2]);
let new_val = 9999;
p[1] = new_val;
assert_eq!(p.into_arr(), [0, new_val, 2]);
}
}
#[cfg(test)]
#[cfg(feature = "appliers")]
mod appliers {
use super::*;
#[test]
fn can_apply() {
let arr = [0,1,2,3];
let p = PointND::<u8, 4>
::from(arr)
.apply(|a| a * 2);
assert_eq!(p.into_arr(), [0, 2, 4, 6]);
}
#[test]
fn can_apply_dims() {
let p = PointND::from([-2,-1,0,1,2])
.apply_dims(&[0, 3], |item| item - 10);
assert_eq!(p.into_arr(), [-12,-1, 0, -9, 2]);
}
#[test]
fn can_apply_vals() {
let p = PointND::from([0,1,2])
.apply_vals([Some(10), None, Some(20)],
|a, b| {
if let Some(i) = b {
a + i
} else {
a
}
});
assert_eq!(p.into_arr(), [10, 1, 22]);
}
#[test]
fn can_apply_point() {
let p1 = PointND::from([0, 1, 2, 3]);
let p2 = PointND::from([0, -1, -2, -3]);
let p3 = p1.apply_point(p2, |a, b| a - b );
assert_eq!(p3.into_arr(), [0, 2, 4, 6]);
}
#[test]
fn can_apply_noclone_items() {
#[derive(Debug, Eq, PartialEq)]
enum X { A, B, C }
let p = PointND
::from([X::A, X::B, X::C])
.apply(|x| {
match x {
X::A => X::B,
X::B => X::C,
X::C => X::A,
}
});
assert_eq!(p.into_arr(), [X::B, X::C, X::A]);
}
}
#[cfg(test)]
#[cfg(feature = "var-dims")]
mod extenders {
use super::*;
#[test]
fn can_extend() {
let zero = PointND::<i32, 0>::from([]);
assert_eq!(zero.dims(), 0);
let two = zero.clone().extend([0,1]);
assert_eq!(two.dims(), 2);
assert_eq!(two.into_arr(), [0, 1]);
let five = PointND
::from([0,1,2])
.extend([3,4]);
assert_eq!(five.dims(), 5);
assert_eq!(five.clone().into_arr(), [0,1,2,3,4]);
let sum = five.apply_point(PointND::from([0,1,2,3,4]), |a, b| a + b);
assert_eq!(sum.into_arr(), [0,2,4,6,8]);
let huge = PointND
::from([0; 100])
.extend([1; 1_000]) as PointND<i32, 1_100>;
assert_eq!(huge.dims(), 1_100);
}
#[test]
fn can_extend_nothing() {
let arr: [i32; 0] = [];
let zero = PointND
::from(arr)
.extend::<0, 0>(arr);
assert_eq!(zero.dims(), 0);
}
}
#[cfg(test)]
#[cfg(feature = "var-dims")]
mod retain {
use super::*;
#[test]
fn can_retain_n() {
let p = PointND
::from([0,1,2,3])
.retain(3);
assert_eq!(p.dims(), 3);
assert_eq!(p.into_arr(), [0,1,2]);
}
#[test]
fn can_retain_zero() {
let p = PointND
::from([0,1,2,3])
.retain(0);
assert_eq!(p.dims(), 0);
assert_eq!(p.into_arr(), []);
}
#[test]
fn can_retain_same() {
let p = PointND
::from([0,1,2,3])
.retain(4);
assert_eq!(p.dims(), 4);
assert_eq!(p.into_arr(), [0,1,2,3]);
}
#[test]
#[should_panic]
#[allow(unused_variables)]
fn cannot_retain_more_dimensions() {
let p = PointND
::from([0,1,2,3])
.retain::<1000>(1000);
}
}
#[cfg(test)]
#[cfg(any(feature = "x", feature = "y", feature = "z", feature = "w"))]
mod conv_methods {
use super::*;
#[cfg(test)]
#[cfg(any(feature = "x", feature = "y", feature = "z", feature = "w"))]
mod get {
use super::*;
#[test]
#[cfg(feature = "x")]
fn getter_for_1d_points_work() {
let arr = [0];
let p = PointND::from(arr);
assert_eq!(*p.x(), arr[0]);
}
#[test]
#[cfg(feature = "y")]
fn getters_for_2d_points_work() {
let arr = [0,1];
let p = PointND::from(arr);
assert_eq!(*p.x(), arr[0]);
assert_eq!(*p.y(), arr[1]);
}
#[test]
#[cfg(feature = "z")]
fn getters_for_3d_points_work() {
let arr = [0,1,2];
let p = PointND::from(arr);
assert_eq!(*p.x(), arr[0]);
assert_eq!(*p.y(), arr[1]);
assert_eq!(*p.z(), arr[2]);
}
#[test]
#[cfg(feature = "w")]
fn getters_for_4d_points_work() {
let arr = [0,1,2,3];
let p = PointND::from(arr);
assert_eq!(*p.x(), arr[0]);
assert_eq!(*p.y(), arr[1]);
assert_eq!(*p.z(), arr[2]);
assert_eq!(*p.w(), arr[3]);
}
}
#[cfg(test)]
#[cfg(any(feature = "x", feature = "y", feature = "z", feature = "w"))]
mod set {
use super::*;
#[test]
#[cfg(feature = "x")]
fn setter_for_1d_points_work() {
let old_vals = [0];
let new_val = 4;
let mut p = PointND::from(old_vals);
p.set_x(new_val);
assert_eq!(*p.x(), new_val);
}
#[test]
#[cfg(feature = "y")]
fn setters_for_2d_points_work() {
let old_vals = [0,1];
let new_vals = [4,5];
let mut p = PointND::from(old_vals);
p.set_x(new_vals[0]);
p.set_y(new_vals[1]);
assert_eq!(*p.x(), new_vals[0]);
assert_eq!(*p.y(), new_vals[1]);
}
#[test]
#[cfg(feature = "z")]
fn setters_for_3d_points_work() {
let old_vals = [0,1,2];
let new_vals = [4,5,6];
let mut p = PointND::from(old_vals);
p.set_x(new_vals[0]);
p.set_y(new_vals[1]);
p.set_z(new_vals[2]);
assert_eq!(*p.x(), new_vals[0]);
assert_eq!(*p.y(), new_vals[1]);
assert_eq!(*p.z(), new_vals[2]);
}
#[test]
#[cfg(feature = "w")]
fn setters_for_4d_points_work() {
let old_vals = [0,1,2,3];
let new_vals = [4,5,6,7];
let mut p = PointND::from(old_vals);
p.set_x(new_vals[0]);
p.set_y(new_vals[1]);
p.set_z(new_vals[2]);
p.set_w(new_vals[3]);
assert_eq!(*p.x(), new_vals[0]);
assert_eq!(*p.y(), new_vals[1]);
assert_eq!(*p.z(), new_vals[2]);
assert_eq!(*p.w(), new_vals[3]);
}
}
#[cfg(test)]
#[cfg(any(feature = "x", feature = "y", feature = "z", feature = "w"))]
mod shift {
use super::*;
#[test]
#[cfg(feature = "x")]
fn can_shift_1d_points() {
let mut p = PointND::from([0.1]);
p.shift_x(1.23);
assert_eq!(p.into_arr(), [1.33]);
}
#[test]
#[cfg(feature = "y")]
fn can_shift_2d_points() {
let mut p = PointND::from([12, 345]);
p.shift_x(-22);
p.shift_y(-345);
assert_eq!(p.into_arr(), [-10, 0]);
}
#[test]
#[cfg(feature = "z")]
fn can_shift_3d_points() {
let mut p = PointND::from([42.4, 2.85, 75.01]);
p.shift_x(40.6);
p.shift_y(-2.85);
p.shift_z(24.99);
assert_eq!(p.into_arr(), [83.0, 0.0, 100.0]);
}
#[test]
#[cfg(feature = "w")]
fn can_shift_4d_points() {
let mut p = PointND::from([0,1,2,3]);
p.shift_x(10);
p.shift_y(-2);
p.shift_z(5);
p.shift_w(0);
assert_eq!(p.into_arr(), [10, -1, 7, 3]);
}
}
}
#[cfg(test)]
mod from_and_into {
use super::*;
#[test]
fn from_array_works() {
let p = PointND::from([0,1,2]);
assert_eq!(p.dims(), 3);
let p: PointND<i32, 4> = [22; 4].into();
assert_eq!(p.into_arr(), [22; 4]);
}
#[test]
fn into_array_works() {
let arr: [i32; 3] = PointND::fill(10).into();
assert_eq!(arr, [10, 10, 10]);
}
}
#[cfg(test)]
mod try_from_and_try_into {
use super::*;
#[test]
fn can_try_from_array() {
let arr = [0,1,2,3,4,5];
let p: Result<PointND<_, 6>, _> = arr.try_into();
assert!(p.is_ok());
}
#[test]
fn can_try_from_slice_of_same_len() {
let slice = &[0,1,2,3,4][..];
let p: Result<PointND<_, 5>, _> = slice.try_into();
assert!(p.is_ok());
}
#[test]
fn cannot_try_from_slice_of_different_length() {
let slice = &[0,1,2,3,4][..];
let p: Result<PointND<_, 10921>, _> = slice.try_into();
assert!(p.is_err());
}
}
}