use crate::{ReferenceSolveError, complex::Complex64};
#[derive(Clone, Debug, PartialEq)]
pub enum HostPhasorFieldError {
ZeroDimension {
axis: &'static str,
},
ShapeMismatch {
rows: usize,
columns: usize,
real_len: usize,
imaginary_len: usize,
},
NonFinite {
component: &'static str,
index: usize,
value: f64,
},
}
#[derive(Clone, Debug, PartialEq)]
pub struct HostPhasorField {
rows: usize,
columns: usize,
real: Vec<f64>,
imaginary: Vec<f64>,
}
impl HostPhasorField {
pub fn from_component_planes(
rows: usize,
columns: usize,
real: Vec<f64>,
imaginary: Vec<f64>,
) -> Result<Self, HostPhasorFieldError> {
if rows == 0 {
return Err(HostPhasorFieldError::ZeroDimension { axis: "rows" });
}
if columns == 0 {
return Err(HostPhasorFieldError::ZeroDimension { axis: "columns" });
}
let Some(cells) = rows.checked_mul(columns) else {
return Err(HostPhasorFieldError::ShapeMismatch {
rows,
columns,
real_len: real.len(),
imaginary_len: imaginary.len(),
});
};
if real.len() != cells || imaginary.len() != cells {
return Err(HostPhasorFieldError::ShapeMismatch {
rows,
columns,
real_len: real.len(),
imaginary_len: imaginary.len(),
});
}
for (component, values) in [("real", &real), ("imaginary", &imaginary)] {
if let Some((index, value)) = values
.iter()
.copied()
.enumerate()
.find(|(_, value)| !value.is_finite())
{
return Err(HostPhasorFieldError::NonFinite {
component,
index,
value,
});
}
}
Ok(Self {
rows,
columns,
real,
imaginary,
})
}
pub(crate) fn try_zeroed(rows: usize, columns: usize) -> Result<Self, ReferenceSolveError> {
let cells = rows
.checked_mul(columns)
.expect("SamplingPlane has already checked rows * columns");
let real = zeroed_component("real", cells)?;
let imaginary = zeroed_component("imaginary", cells)?;
Ok(Self {
rows,
columns,
real,
imaginary,
})
}
pub(crate) fn set_index(&mut self, index: usize, value: Complex64) {
let (real, imaginary) = value.components();
self.real[index] = real;
self.imaginary[index] = imaginary;
}
#[cfg(test)]
pub(crate) fn from_test_components(
rows: usize,
columns: usize,
real: Vec<f64>,
imaginary: Vec<f64>,
) -> Self {
let cells = rows.checked_mul(columns).expect("test shape must fit");
assert_eq!(real.len(), cells);
assert_eq!(imaginary.len(), cells);
assert!(real.iter().chain(&imaginary).all(|value| value.is_finite()));
Self {
rows,
columns,
real,
imaginary,
}
}
pub fn rows(&self) -> usize {
self.rows
}
pub fn columns(&self) -> usize {
self.columns
}
pub fn len(&self) -> usize {
self.real.len()
}
pub fn is_empty(&self) -> bool {
self.real.is_empty()
}
pub fn real(&self) -> &[f64] {
&self.real
}
pub fn imaginary(&self) -> &[f64] {
&self.imaginary
}
pub fn cell(&self, row: usize, column: usize) -> Option<(f64, f64)> {
let index = row.checked_mul(self.columns)?.checked_add(column)?;
(row < self.rows && column < self.columns)
.then(|| Complex64::new(self.real[index], self.imaginary[index]).components())
}
pub fn into_component_planes(self) -> (usize, usize, Vec<f64>, Vec<f64>) {
(self.rows, self.columns, self.real, self.imaginary)
}
}
fn zeroed_component(
component: &'static str,
cells: usize,
) -> Result<Vec<f64>, ReferenceSolveError> {
let mut values = Vec::new();
values
.try_reserve_exact(cells)
.map_err(|_| ReferenceSolveError::AllocationFailed { component, cells })?;
values.resize(cells, 0.0);
Ok(values)
}
#[cfg(test)]
mod tests {
use super::HostPhasorField;
#[test]
fn component_planes_share_one_row_major_shape() {
let field = HostPhasorField {
rows: 2,
columns: 3,
real: vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0],
imaginary: vec![10.0, 11.0, 12.0, 13.0, 14.0, 15.0],
};
assert_eq!(field.rows(), 2);
assert_eq!(field.columns(), 3);
assert_eq!(field.len(), 6);
assert!(!field.is_empty());
assert_eq!(field.cell(1, 2), Some((5.0, 15.0)));
assert_eq!(field.cell(2, 0), None);
assert_eq!(field.cell(0, 3), None);
assert_eq!(field.real(), &[0.0, 1.0, 2.0, 3.0, 4.0, 5.0]);
assert_eq!(field.imaginary(), &[10.0, 11.0, 12.0, 13.0, 14.0, 15.0]);
}
}