use std::{
fmt,
ops::{Index, IndexMut},
};
#[derive(Clone, Default)]
pub struct Triangular<T>
where
T: Copy + Sized,
{
pub(crate) dimension: usize,
pub(crate) data: Vec<T>,
}
impl<T> Triangular<T>
where
T: Copy + Sized,
{
pub fn with_dimension(dimension: usize, default: T) -> Self {
let len = match dimension {
0 => 0,
_ => (dimension * (dimension - 1)) / 2,
};
Self {
dimension,
data: vec![default; len],
}
}
#[inline]
pub fn offset(&self, i: usize, j: usize) -> usize {
debug_assert!(i < j);
debug_assert!(i < self.dimension);
debug_assert!(j < self.dimension);
if i == 0 {
return j - 1;
}
let last_index = (i * self.dimension) - (i * i + i) / 2;
last_index + (j - i - 1)
}
}
impl<T> From<&Vec<T>> for Triangular<T>
where
T: Copy + Sized,
{
#[allow(clippy::cast_sign_loss)]
fn from(vec: &Vec<T>) -> Self {
let dimension = (((2 * vec.len()) as f32).sqrt() as usize) + 1;
Self { dimension, data: vec.clone() }
}
}
impl<T> Index<(usize, usize)> for Triangular<T>
where
T: Copy + Sized,
{
type Output = T;
fn index(&self, index: (usize, usize)) -> &T {
let offset = self.offset(index.0, index.1);
&self.data[offset]
}
}
impl<T> Index<usize> for Triangular<T>
where
T: Copy + Sized,
{
type Output = T;
fn index(&self, index: usize) -> &T {
&self.data[index]
}
}
impl<T> IndexMut<(usize, usize)> for Triangular<T>
where
T: Copy + Sized,
{
fn index_mut(&mut self, index: (usize, usize)) -> &mut T {
let offset = self.offset(index.0, index.1);
&mut self.data[offset]
}
}
impl<T> fmt::Debug for Triangular<T>
where
T: Copy + Sized,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "(Triangular {}, [data])", self.dimension)
}
}
#[cfg(test)]
mod tests {
use super::Triangular;
#[test]
fn test_offset() {
let matrix = Triangular::with_dimension(4, 0);
assert_eq!(matrix.offset(0, 1), 0);
assert_eq!(matrix.offset(0, 3), 2);
assert_eq!(matrix.offset(2, 3), 5);
let matrix = Triangular::with_dimension(5, 0);
assert_eq!(matrix.offset(0, 1), 0);
assert_eq!(matrix.offset(1, 4), 6);
assert_eq!(matrix.offset(2, 3), 7);
assert_eq!(matrix.offset(3, 4), 9);
}
#[test]
fn test_index() {
let mut matrix = Triangular::with_dimension(5, 0);
matrix[(2, 3)] = 667;
assert_eq!(matrix.data[7], 667);
}
}