orx_v/fun/funvec.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
use crate::common_trait_helpers::debug::*;
use crate::{dim::*, Card, NVec, UnboundedCard};
use core::fmt::Debug;
use core::marker::PhantomData;
/// A functional vector of dimension `D` and cardinality `C`
/// having `T` scalar elements.
///
/// So much generics, but the important bit is:
///
/// * any `FunVec<D, T, F, C>` implements `NVec<D, T>`
///
/// hence,
///
/// * any `FunVec<D1, T, F, C>` implements `V1<T>`,
/// * any `FunVec<D2, T, F, C>` implements `V2<T>`,
/// * and so on.
///
/// Examples will hopefully clarify.
///
/// # Examples
///
/// ```
/// use orx_v::*;
/// use std::collections::HashMap;
///
/// #[derive(Clone, Copy)]
/// struct Geocode(i32, i32);
///
/// fn compute_using_coordinates<V: V1<Geocode>>(geocodes: V) { /* todo */ }
///
/// // we can pass a Vec of coordinates
/// compute_using_coordinates(&vec![Geocode(0, 0), Geocode(1, 1)]);
///
/// // or we can pass a functional vector
/// let geocodes = V.d1().fun(|[i]| Geocode(i as i32, i as i32));
/// compute_using_coordinates(geocodes);
///
/// // also see sparse (V.d1().sparse(Geocode(0, 0)))
/// let known_geocodes: HashMap<usize, Geocode> = [(3, Geocode(1, 1)), (7, Geocode(2, 1))].into_iter().collect();
/// let geocodes = V.d1().fun(|[i]| match known_geocodes.get(&i) {
/// Some(geo) => *geo,
/// None => Geocode(0, 0),
/// });
/// compute_using_coordinates(geocodes);
///
/// // the functional vectors are unbounded on construction
/// assert!(geocodes.is_unbounded());
/// assert_eq!(geocodes.card([]), usize::MAX);
/// ```
///
/// The function of the functional geocodes vector can be anything that can compute a geocode
/// based on its index. This is exactly the same in higher dimensional vectors.
///
/// If we break down all generic parameters:
/// * `D = D1` since it is a 1-dimensional vector,
/// * `T = Geocode` which is the scalar that the vector returns,
/// * `F` is any function implementing `Fn([usize; 1]) -> T`, we can never type its name,
/// * `C = UnboundedCard<D1>`.
///
/// You might guess that, all functional vectors in the above examples are capable of computing
/// a geocode for any given index. Their domains are unbounded; hence, the generic cardinality
/// parameter is `C = UnboundedCard<D1>`. This makes sense at one hand; however, having a bounded
/// domain is often more practical on the other hand. We can introduce bounds to the domain of
/// the functional vector by using:
/// * `bounded` if the vector is of dimension `D1`,
/// * `with_rectangular_bounds` or `with_variable_bounds` otherwise.
#[derive(Copy)]
pub struct FunVec<D, T, F, C = UnboundedCard<D>>
where
D: Dim,
F: Fn(D::Idx) -> T,
C: Card<D>,
{
pub(crate) fun: F,
pub(crate) card: C,
phantom: PhantomData<(D, T)>,
}
impl<D, T, F, C> FunVec<D, T, F, C>
where
D: Dim,
F: Fn(D::Idx) -> T,
C: Card<D>,
{
pub(crate) fn new(fun: F, card: C) -> Self {
Self {
fun,
card,
phantom: PhantomData,
}
}
}
impl<D, T, F, C> Clone for FunVec<D, T, F, C>
where
D: Dim,
F: Fn(D::Idx) -> T + Clone,
C: Card<D> + Clone,
{
fn clone(&self) -> Self {
Self {
fun: self.fun.clone(),
card: self.card.clone(),
phantom: Default::default(),
}
}
}
macro_rules! impl_debug {
($dim:ty, $dbg_fn:ident) => {
impl<T, F, C> Debug for FunVec<$dim, T, F, C>
where
F: Fn(<$dim as Dim>::Idx) -> T,
C: Card<$dim>,
T: Debug,
Self: NVec<$dim, T>,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
"{{ kind: FunVec, dim: D{}, is_bounded: {}, values: ",
<$dim as Dim>::dimension(),
self.is_bounded(),
)?;
$dbg_fn(f, self)?;
write!(f, " }}")
}
}
};
}
impl_debug!(D1, dbg_values_d1);
impl_debug!(D2, dbg_values_d2);
impl_debug!(D3, dbg_values_d3);
impl_debug!(D4, dbg_values_d4);