use std::ops::{Index, IndexMut};
use crate::errors::QlResult;
use crate::math::array::Array;
use crate::math::timegrid::TimeGrid;
use crate::require;
use crate::types::{Real, Size, Time};
#[derive(Clone, Debug, PartialEq, Default)]
pub struct Path {
time_grid: TimeGrid,
values: Array,
}
impl Path {
pub fn new(time_grid: TimeGrid, values: Array) -> QlResult<Self> {
let values = if values.is_empty() {
Array::with_size(time_grid.size())
} else {
values
};
require!(
values.size() == time_grid.size(),
"different number of times and asset values"
);
Ok(Path { time_grid, values })
}
pub fn empty(&self) -> bool {
self.time_grid.empty()
}
pub fn length(&self) -> Size {
self.time_grid.size()
}
pub fn at(&self, i: Size) -> Option<Real> {
self.values.get(i).copied()
}
pub fn at_mut(&mut self, i: Size) -> Option<&mut Real> {
self.values.get_mut(i)
}
pub fn front(&self) -> Real {
self.values[0]
}
pub fn front_mut(&mut self) -> &mut Real {
&mut self.values[0]
}
pub fn back(&self) -> Real {
self.values[self.values.size() - 1]
}
pub fn back_mut(&mut self) -> &mut Real {
let last = self.values.size() - 1;
&mut self.values[last]
}
pub fn time(&self, i: Size) -> Time {
self.time_grid[i]
}
pub fn time_grid(&self) -> &TimeGrid {
&self.time_grid
}
pub fn values(&self) -> &[Real] {
&self.values
}
}
impl Index<Size> for Path {
type Output = Real;
fn index(&self, i: Size) -> &Real {
&self.values[i]
}
}
impl IndexMut<Size> for Path {
fn index_mut(&mut self, i: Size) -> &mut Real {
&mut self.values[i]
}
}
#[cfg(test)]
mod tests {
use super::*;
fn grid() -> TimeGrid {
TimeGrid::new(1.0, 4).unwrap()
}
#[test]
fn empty_values_default_to_grid_sized_zeros() {
let path = Path::new(grid(), Array::new()).unwrap();
assert_eq!(path.length(), 5);
assert_eq!(path.values(), &[0.0; 5]);
assert!(!path.empty());
}
#[test]
fn provided_values_are_kept() {
let values = Array::from([100.0, 101.0, 102.0, 103.0, 104.0]);
let path = Path::new(grid(), values).unwrap();
assert_eq!(path.front(), 100.0);
assert_eq!(path.back(), 104.0);
assert_eq!(path[2], 102.0);
assert_eq!(path.at(4), Some(104.0));
assert_eq!(path.at(5), None);
}
#[test]
fn size_mismatch_is_rejected() {
let err = Path::new(grid(), Array::from([1.0, 2.0, 3.0])).unwrap_err();
assert_eq!(err.message(), "different number of times and asset values");
}
#[test]
fn mutable_accessors_write_through() {
let mut path = Path::new(grid(), Array::new()).unwrap();
*path.front_mut() = 100.0;
*path.back_mut() = 200.0;
path[2] = 150.0;
*path.at_mut(1).unwrap() = 125.0;
assert_eq!(path.values(), &[100.0, 125.0, 150.0, 0.0, 200.0]);
assert!(path.at_mut(5).is_none());
}
#[test]
fn time_reads_the_grid() {
let path = Path::new(grid(), Array::new()).unwrap();
for i in 0..path.length() {
assert_eq!(path.time(i), path.time_grid()[i]);
}
assert_eq!(path.time(2), 0.5);
}
}