use crate::simd_wide;
#[inline(always)]
pub fn simd_sin_8(angles: [f64; 8]) -> [f64; 8] {
simd_wide::simd_sin_8_wide(angles)
}
#[inline(always)]
pub fn simd_cos_8(angles: [f64; 8]) -> [f64; 8] {
simd_wide::simd_cos_8_wide(angles)
}
#[inline(always)]
pub fn simd_sin_cos_8(angles: [f64; 8]) -> ([f64; 8], [f64; 8]) {
simd_wide::simd_sin_cos_8_wide(angles)
}
#[inline(always)]
pub fn simd_atan2_8(y: [f64; 8], x: [f64; 8]) -> [f64; 8] {
simd_wide::simd_atan2_8_wide(y, x)
}
#[inline(always)]
pub fn simd_asin_8(x: [f64; 8]) -> [f64; 8] {
simd_wide::simd_asin_8_wide(x)
}
#[inline(always)]
pub fn simd_acos_8(x: [f64; 8]) -> [f64; 8] {
simd_wide::simd_acos_8_wide(x)
}
#[inline(always)]
pub fn simd_sqrt_8(x: [f64; 8]) -> [f64; 8] {
simd_wide::simd_sqrt_8_wide(x)
}
#[inline(always)]
pub fn simd_pow_8(x: [f64; 8], exp: f64) -> [f64; 8] {
[
x[0].powf(exp),
x[1].powf(exp),
x[2].powf(exp),
x[3].powf(exp),
x[4].powf(exp),
x[5].powf(exp),
x[6].powf(exp),
x[7].powf(exp),
]
}
#[inline]
pub fn simd_ln_8(x: [f64; 8]) -> [f64; 8] {
[
x[0].ln(),
x[1].ln(),
x[2].ln(),
x[3].ln(),
x[4].ln(),
x[5].ln(),
x[6].ln(),
x[7].ln(),
]
}
#[inline]
pub fn simd_recip_8(x: [f64; 8]) -> [f64; 8] {
x.iter().map(|v| 1.0 / v).collect::<Vec<_>>()[..]
.try_into()
.unwrap()
}
#[inline]
pub fn simd_abs_8(x: [f64; 8]) -> [f64; 8] {
simd_wide::simd_abs_8_wide(x)
}
#[inline]
pub fn simd_clamp_8(x: [f64; 8], min: f64, max: f64) -> [f64; 8] {
simd_wide::simd_clamp_8_wide(x, min, max)
}
#[inline]
pub fn simd_mul_8(a: [f64; 8], b: [f64; 8]) -> [f64; 8] {
simd_wide::simd_mul_8_wide(a, b)
}
#[inline]
pub fn simd_add_8(a: [f64; 8], b: [f64; 8]) -> [f64; 8] {
simd_wide::simd_add_8_wide(a, b)
}
#[inline]
pub fn simd_madd_8(a: [f64; 8], b: [f64; 8], c: [f64; 8]) -> [f64; 8] {
simd_wide::simd_madd_8_wide(a, b, c)
}
pub fn simd_normalize_vec3_8(
x: [f64; 8],
y: [f64; 8],
z: [f64; 8],
) -> ([f64; 8], [f64; 8], [f64; 8]) {
let mag_sq = simd_madd_8(x, x, simd_madd_8(y, y, simd_mul_8(z, z)));
let mag = simd_sqrt_8(mag_sq);
let mag_inv = simd_recip_8(mag);
(
simd_mul_8(x, mag_inv),
simd_mul_8(y, mag_inv),
simd_mul_8(z, mag_inv),
)
}
pub fn simd_dot3_8(
a_x: [f64; 8],
a_y: [f64; 8],
a_z: [f64; 8],
b_x: [f64; 8],
b_y: [f64; 8],
b_z: [f64; 8],
) -> [f64; 8] {
simd_madd_8(a_x, b_x, simd_madd_8(a_y, b_y, simd_mul_8(a_z, b_z)))
}
pub fn simd_cross_8(
a_x: [f64; 8],
a_y: [f64; 8],
a_z: [f64; 8],
b_x: [f64; 8],
b_y: [f64; 8],
b_z: [f64; 8],
) -> ([f64; 8], [f64; 8], [f64; 8]) {
let c_x = simd_add_8(
simd_mul_8(a_y, b_z),
simd_mul_8(simd_mul_8(a_z, b_y), [-1.0; 8]),
);
let c_y = simd_add_8(
simd_mul_8(a_z, b_x),
simd_mul_8(simd_mul_8(a_x, b_z), [-1.0; 8]),
);
let c_z = simd_add_8(
simd_mul_8(a_x, b_y),
simd_mul_8(simd_mul_8(a_y, b_x), [-1.0; 8]),
);
(c_x, c_y, c_z)
}
#[cfg(test)]
mod tests {
use super::*;
use std::f64::consts::PI;
const EPSILON: f64 = 1e-14;
#[test]
fn test_simd_sin_8() {
let angles = [
0.0,
PI / 6.0,
PI / 4.0,
PI / 3.0,
PI / 2.0,
PI,
-PI / 6.0,
-PI / 2.0,
];
let result = simd_sin_8(angles);
for i in 0..8 {
let expected = angles[i].sin();
assert!(
(result[i] - expected).abs() < EPSILON,
"sin mismatch at index {}",
i
);
}
}
#[test]
fn test_simd_cos_8() {
let angles = [
0.0,
PI / 6.0,
PI / 4.0,
PI / 3.0,
PI / 2.0,
PI,
-PI / 6.0,
-PI / 2.0,
];
let result = simd_cos_8(angles);
for i in 0..8 {
let expected = angles[i].cos();
assert!(
(result[i] - expected).abs() < EPSILON,
"cos mismatch at index {}",
i
);
}
}
#[test]
fn test_simd_sin_cos_8() {
let angles = [
0.0,
PI / 6.0,
PI / 4.0,
PI / 3.0,
PI / 2.0,
PI,
-PI / 6.0,
-PI / 2.0,
];
let (sines, cosines) = simd_sin_cos_8(angles);
for i in 0..8 {
let (expected_sin, expected_cos) = angles[i].sin_cos();
assert!(
(sines[i] - expected_sin).abs() < EPSILON,
"sin mismatch at index {}",
i
);
assert!(
(cosines[i] - expected_cos).abs() < EPSILON,
"cos mismatch at index {}",
i
);
}
}
#[test]
fn test_simd_atan2_8() {
let y = [1.0, 1.0, 0.0, -1.0, -1.0, -1.0, 0.0, 1.0];
let x = [1.0, 0.0, 1.0, 1.0, 0.0, -1.0, -1.0, -1.0];
let result = simd_atan2_8(y, x);
for i in 0..8 {
let expected = y[i].atan2(x[i]);
assert!(
(result[i] - expected).abs() < EPSILON,
"atan2 mismatch at index {}",
i
);
}
}
#[test]
fn test_simd_asin_8() {
let x = [-1.0, -0.5, 0.0, 0.5, 1.0, -0.707, 0.707, 0.866];
let result = simd_asin_8(x);
for i in 0..8 {
let expected = x[i].asin();
assert!(
(result[i] - expected).abs() < EPSILON,
"asin mismatch at index {}",
i
);
}
}
#[test]
fn test_simd_normalize_vec3_8() {
let x = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
let y = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0];
let z = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0];
let (nx, ny, nz) = simd_normalize_vec3_8(x, y, z);
for i in 0..8 {
let mag_sq = nx[i] * nx[i] + ny[i] * ny[i] + nz[i] * nz[i];
assert!(
(mag_sq - 1.0).abs() < EPSILON,
"magnitude mismatch at index {}",
i
);
assert!(
(nx[i] - 1.0).abs() < EPSILON,
"normalized x mismatch at index {}",
i
);
}
}
#[test]
fn test_simd_dot3_8() {
let a_x = [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0];
let a_y = [0.0; 8];
let a_z = [0.0; 8];
let b_x = [1.0, 0.0, 0.0, 2.0, 3.0, -1.0, 0.5, 10.0];
let b_y = [0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0];
let b_z = [0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0];
let result = simd_dot3_8(a_x, a_y, a_z, b_x, b_y, b_z);
for i in 0..8 {
assert!(
(result[i] - b_x[i]).abs() < EPSILON,
"dot product mismatchat index {}",
i
);
}
}
}
#[inline]
pub fn simd_sph_to_vec_8(theta: [f64; 8], phi: [f64; 8]) -> ([f64; 8], [f64; 8], [f64; 8]) {
let sin_theta = simd_sin_8(theta);
let cos_theta = simd_cos_8(theta);
let sin_phi = simd_sin_8(phi);
let cos_phi = simd_cos_8(phi);
let x = simd_mul_8(sin_theta, cos_phi);
let y = simd_mul_8(sin_theta, sin_phi);
let z = cos_theta;
(x, y, z)
}
#[inline]
pub fn simd_vec_to_sph_8(x: [f64; 8], y: [f64; 8], z: [f64; 8]) -> ([f64; 8], [f64; 8]) {
let z_clamped = simd_clamp_8(z, -1.0, 1.0);
let theta = simd_acos_8(z_clamped);
let phi = simd_atan2_8(y, x);
(theta, phi)
}
#[inline]
pub fn simd_matvec3_8(
mat: [[f64; 3]; 3],
x: [f64; 8],
y: [f64; 8],
z: [f64; 8],
) -> ([f64; 8], [f64; 8], [f64; 8]) {
let x_new = simd_add_8(
simd_add_8(simd_mul_8(x, [mat[0][0]; 8]), simd_mul_8(y, [mat[0][1]; 8])),
simd_mul_8(z, [mat[0][2]; 8]),
);
let y_new = simd_add_8(
simd_add_8(simd_mul_8(x, [mat[1][0]; 8]), simd_mul_8(y, [mat[1][1]; 8])),
simd_mul_8(z, [mat[1][2]; 8]),
);
let z_new = simd_add_8(
simd_add_8(simd_mul_8(x, [mat[2][0]; 8]), simd_mul_8(y, [mat[2][1]; 8])),
simd_mul_8(z, [mat[2][2]; 8]),
);
(x_new, y_new, z_new)
}
#[cfg(test)]
mod healpix_tests {
use super::*;
use std::f64::consts::PI;
#[test]
fn test_simd_sph_to_vec_8() {
let theta = [
0.0,
PI / 2.0,
PI,
0.0,
PI / 4.0,
PI / 4.0,
PI / 3.0,
PI / 6.0,
];
let phi = [0.0, 0.0, 0.0, PI / 2.0, 0.0, PI / 2.0, PI / 4.0, PI / 3.0];
let (x, y, z) = simd_sph_to_vec_8(theta, phi);
assert!((x[0] - 0.0).abs() < 1e-14);
assert!((y[0] - 0.0).abs() < 1e-14);
assert!((z[0] - 1.0).abs() < 1e-14);
assert!((x[1] - 1.0).abs() < 1e-14);
assert!((y[1] - 0.0).abs() < 1e-14);
assert!((z[1] - 0.0).abs() < 1e-14);
assert!((x[2] - 0.0).abs() < 1e-14);
assert!((y[2] - 0.0).abs() < 1e-14);
assert!((z[2] - (-1.0)).abs() < 1e-14);
assert!((x[3] - 0.0).abs() < 1e-14);
assert!((y[3] - 0.0).abs() < 1e-14);
assert!((z[3] - 1.0).abs() < 1e-14);
}
#[test]
fn test_simd_vec_to_sph_8_roundtrip() {
let theta_in = [0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 0.1, std::f64::consts::PI];
let phi_in = [0.0, PI / 4.0, PI / 2.0, PI, 3.0 * PI / 2.0, 0.1, 0.2, 0.3];
let (x, y, z) = simd_sph_to_vec_8(theta_in, phi_in);
let (theta_out, phi_out) = simd_vec_to_sph_8(x, y, z);
for i in 0..8 {
assert!(
(theta_out[i] - theta_in[i]).abs() < 1e-12,
"Theta mismatch at {}: {} vs {}",
i,
theta_out[i],
theta_in[i]
);
let phi_diff = (phi_out[i] - phi_in[i]).abs();
let phi_diff_wrapped = (2.0 * PI - phi_diff).min(phi_diff);
assert!(
phi_diff_wrapped < 1e-12 || theta_in[i].sin().abs() < 1e-10,
"Phi mismatch at {}: {} vs {} (theta_sin={})",
i,
phi_out[i],
phi_in[i],
theta_in[i].sin()
);
}
}
#[test]
fn test_simd_matvec3_8_identity() {
let identity = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]];
let x_in = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
let y_in = [0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5];
let z_in = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8];
let (x_out, y_out, z_out) = simd_matvec3_8(identity, x_in, y_in, z_in);
for i in 0..8 {
assert!((x_out[i] - x_in[i]).abs() < 1e-14);
assert!((y_out[i] - y_in[i]).abs() < 1e-14);
assert!((z_out[i] - z_in[i]).abs() < 1e-14);
}
}
#[test]
fn test_simd_matvec3_8_scaling() {
let scale_matrix = [[2.0, 0.0, 0.0], [0.0, 3.0, 0.0], [0.0, 0.0, 5.0]];
let x_in = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
let y_in = [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0];
let z_in = [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0];
let (x_out, y_out, z_out) = simd_matvec3_8(scale_matrix, x_in, y_in, z_in);
for i in 0..8 {
assert!((x_out[i] - 2.0 * x_in[i]).abs() < 1e-14);
assert!((y_out[i] - 3.0 * y_in[i]).abs() < 1e-14);
assert!((z_out[i] - 5.0 * z_in[i]).abs() < 1e-14);
}
}
}
#[inline]
pub fn simd_linear_scale_8(
values: [f64; 8],
min: f64,
max: f64,
mask: [bool; 8],
) -> ([f64; 8], [bool; 8]) {
let inv_range = if max > min {
1.0 / (max - min)
} else {
0.0 };
let mut result = [0.0; 8];
let out_mask = mask;
for i in 0..8 {
if !mask[i] {
continue;
}
if max <= min {
result[i] = 0.5;
} else if values[i] <= min {
result[i] = 0.0;
} else if values[i] >= max {
result[i] = 1.0;
} else {
result[i] = (values[i] - min) * inv_range;
}
}
(result, out_mask)
}
#[inline]
pub fn simd_log_scale_8(
values: [f64; 8],
log_min: f64,
log_range: f64,
mask: [bool; 8],
) -> ([f64; 8], [bool; 8]) {
let mut result = [0.0; 8];
let mut out_mask = mask;
for i in 0..8 {
if !mask[i] {
continue;
}
if values[i] <= 0.0 {
out_mask[i] = false;
continue;
}
if log_range <= 0.0 {
result[i] = 0.5;
} else {
let log_val = values[i].ln();
result[i] = ((log_val - log_min) / log_range).clamp(0.0, 1.0);
}
}
(result, out_mask)
}
#[inline]
pub fn simd_symlog_scale_8(
values: [f64; 8],
linthresh: f64,
min: f64,
max: f64,
mask: [bool; 8],
) -> ([f64; 8], [bool; 8]) {
let f = |x: f64| {
if x.abs() < linthresh {
x / linthresh
} else {
x.signum() * (x.abs() / linthresh).ln()
}
};
let f_min = f(min);
let f_max = f(max);
let f_range = f_max - f_min;
let safe_range = if f_range.abs() > 1e-10 { f_range } else { 1.0 };
let mut result = [0.0; 8];
let out_mask = mask;
for i in 0..8 {
if !mask[i] {
continue;
}
let f_val = f(values[i]);
result[i] = ((f_val - f_min) / safe_range).clamp(0.0, 1.0);
}
(result, out_mask)
}
#[inline]
pub fn simd_asinh_scale_8(
values: [f64; 8],
scale: f64,
min: f64,
max: f64,
mask: [bool; 8],
) -> ([f64; 8], [bool; 8]) {
let min_val = (min / scale).asinh();
let max_val = (max / scale).asinh();
let range = max_val - min_val;
let safe_range = if range.abs() > 1e-10 { range } else { 1.0 };
let mut result = [0.0; 8];
let out_mask = mask;
for i in 0..8 {
if !mask[i] {
continue;
}
let asinh_val = (values[i] / scale).asinh();
result[i] = ((asinh_val - min_val) / safe_range).clamp(0.0, 1.0);
}
(result, out_mask)
}
#[inline]
pub fn simd_plancklog_scale_8(
values: [f64; 8],
linthresh: f64,
min: f64,
max: f64,
mask: [bool; 8],
) -> ([f64; 8], [bool; 8]) {
let f = |x: f64| {
if x.abs() < linthresh {
x / linthresh
} else {
x.signum() * (1.0 + (x.abs() / linthresh).ln())
}
};
let f_min = f(min);
let f_max = f(max);
let f_range = f_max - f_min;
let safe_range = if f_range.abs() > 1e-10 { f_range } else { 1.0 };
let mut result = [0.0; 8];
let out_mask = mask;
for i in 0..8 {
if !mask[i] {
continue;
}
let f_val = f(values[i]);
result[i] = ((f_val - f_min) / safe_range).clamp(0.0, 1.0);
}
(result, out_mask)
}
#[inline]
pub fn simd_colormap_sample_8(
normalized: [f64; 8],
lut: &[[u8; 3]; 256],
mask: [bool; 8],
) -> ([u8; 24], [bool; 8]) {
let mut rgb_buffer = [0u8; 24];
for i in 0..8 {
if !mask[i] {
rgb_buffer[i * 3] = 0;
rgb_buffer[i * 3 + 1] = 0;
rgb_buffer[i * 3 + 2] = 0;
continue;
}
let idx = (normalized[i].clamp(0.0, 1.0) * 255.0) as usize;
let rgb = lut[idx];
rgb_buffer[i * 3] = rgb[0];
rgb_buffer[i * 3 + 1] = rgb[1];
rgb_buffer[i * 3 + 2] = rgb[2];
}
(rgb_buffer, mask)
}
#[inline]
pub fn simd_gamma_correct_8(
values: [f64; 8],
gamma_inv: f64,
mask: [bool; 8],
) -> ([f64; 8], [bool; 8]) {
let mut result = [0.0; 8];
for i in 0..8 {
if !mask[i] {
continue;
}
result[i] = values[i].powf(gamma_inv);
}
(result, mask)
}
#[cfg(test)]
mod scaling_tests {
use super::*;
#[test]
fn test_simd_linear_scale_8() {
let values = [0.0, 2.5, 5.0, 7.5, 10.0, 1.0, 3.0, 9.0];
let mask = [true; 8];
let (result, out_mask) = simd_linear_scale_8(values, 0.0, 10.0, mask);
let expected = [0.0, 0.25, 0.5, 0.75, 1.0, 0.1, 0.3, 0.9];
for i in 0..8 {
assert!(
(result[i] - expected[i]).abs() < 1e-14,
"Linear scale mismatch at {}: {} vs {}",
i,
result[i],
expected[i]
);
assert!(out_mask[i], "Mask should remain true at {}", i);
}
}
#[test]
fn test_simd_linear_scale_clamping() {
let values = [-5.0, 0.0, 5.0, 10.0, 15.0, 20.0, 2.5, 7.5];
let mask = [true; 8];
let (result, _) = simd_linear_scale_8(values, 0.0, 10.0, mask);
assert_eq!(result[0], 0.0); assert_eq!(result[1], 0.0); assert_eq!(result[2], 0.5); assert_eq!(result[3], 1.0); assert_eq!(result[4], 1.0); assert_eq!(result[5], 1.0); }
#[test]
fn test_simd_log_scale_8() {
let values = [1.0, 10.0, 100.0, 1000.0, 5.0, 50.0, 10.0, 100.0];
let log_min = 1.0_f64.ln(); let log_max = 100.0_f64.ln(); let log_range = log_max - log_min;
let mask = [true; 8];
let (result, out_mask) = simd_log_scale_8(values, log_min, log_range, mask);
for item in &out_mask {
assert!(*item, "All positive values should remain valid");
}
assert!(
(result[0] - 0.0).abs() < 1e-14,
"log scale of min should be 0"
); assert!(
(result[2] - 1.0).abs() < 1e-14,
"log scale of max should be 1"
); assert!(
(result[3] - 1.0).abs() < 1e-14,
"log scale of 1000 should clamp to 1"
);
assert!(result[0] < result[1]); assert!(result[1] < result[2]); }
#[test]
fn test_simd_gamma_correct_8() {
let values = [0.0, 0.25, 0.5, 0.75, 1.0, 0.1, 0.9, 0.5];
let mask = [true; 8];
let gamma = 2.0; let gamma_inv = 1.0 / gamma;
let (result, out_mask) = simd_gamma_correct_8(values, gamma_inv, mask);
assert!((result[0] - 0.0).abs() < 1e-14); assert!((result[1] - 0.5).abs() < 1e-14); assert!((result[2] - (0.5_f64).sqrt()).abs() < 1e-14); assert!((result[4] - 1.0).abs() < 1e-14);
for (i, item) in out_mask.iter().enumerate() {
assert!(*item, "Mask should remain true at {}", i);
}
}
#[test]
fn test_simd_colormap_sample_8_lookup() {
let mut lut = [[0u8; 3]; 256];
for (i, item) in lut.iter_mut().enumerate() {
let val = i as u8;
*item = [val, val, val]; }
let normalized = [0.0, 0.25, 0.5, 0.75, 1.0, 0.1, 0.9, 0.5];
let mask = [true; 8];
let (rgb_buffer, out_mask) = simd_colormap_sample_8(normalized, &lut, mask);
assert_eq!(rgb_buffer[0], 0);
assert_eq!(rgb_buffer[1], 0);
assert_eq!(rgb_buffer[2], 0);
let idx_white = 4 * 3;
assert_eq!(rgb_buffer[idx_white], 255);
assert_eq!(rgb_buffer[idx_white + 1], 255);
assert_eq!(rgb_buffer[idx_white + 2], 255);
for item in &out_mask {
assert!(*item);
}
}
#[test]
fn test_simd_colormap_sample_8_invalid_pixels() {
let lut = [[100u8; 3]; 256];
let normalized = [0.5; 8];
let mut mask = [true; 8];
mask[2] = false; mask[5] = false;
let (rgb_buffer, out_mask) = simd_colormap_sample_8(normalized, &lut, mask);
assert_eq!(rgb_buffer[0], 100);
let idx_invalid = 2 * 3;
assert_eq!(rgb_buffer[idx_invalid], 0);
assert_eq!(rgb_buffer[idx_invalid + 1], 0);
assert_eq!(rgb_buffer[idx_invalid + 2], 0);
assert!(out_mask[0]);
assert!(!out_mask[2]);
assert!(!out_mask[5]);
}
}
#[inline]
pub fn simd_batch_scale_8(
values: [f64; 8],
min: f64,
max: f64,
use_log: bool,
log_cache: Option<(f64, f64)>,
mask: [bool; 8],
) -> ([f64; 8], [bool; 8]) {
if use_log {
if let Some((log_min, log_range)) = log_cache {
simd_log_scale_8(values, log_min, log_range, mask)
} else {
simd_linear_scale_8(values, min, max, mask)
}
} else {
simd_linear_scale_8(values, min, max, mask)
}
}
use crate::PixelValue;
#[inline]
pub fn simd_to_pixel_values(scaled: [f64; 8], mask: [bool; 8]) -> [PixelValue; 8] {
[
if !mask[0] {
PixelValue::Bad
} else if scaled[0] <= 0.0 {
PixelValue::Underflow
} else if scaled[0] >= 1.0 {
PixelValue::Overflow
} else {
PixelValue::Color(scaled[0])
},
if !mask[1] {
PixelValue::Bad
} else if scaled[1] <= 0.0 {
PixelValue::Underflow
} else if scaled[1] >= 1.0 {
PixelValue::Overflow
} else {
PixelValue::Color(scaled[1])
},
if !mask[2] {
PixelValue::Bad
} else if scaled[2] <= 0.0 {
PixelValue::Underflow
} else if scaled[2] >= 1.0 {
PixelValue::Overflow
} else {
PixelValue::Color(scaled[2])
},
if !mask[3] {
PixelValue::Bad
} else if scaled[3] <= 0.0 {
PixelValue::Underflow
} else if scaled[3] >= 1.0 {
PixelValue::Overflow
} else {
PixelValue::Color(scaled[3])
},
if !mask[4] {
PixelValue::Bad
} else if scaled[4] <= 0.0 {
PixelValue::Underflow
} else if scaled[4] >= 1.0 {
PixelValue::Overflow
} else {
PixelValue::Color(scaled[4])
},
if !mask[5] {
PixelValue::Bad
} else if scaled[5] <= 0.0 {
PixelValue::Underflow
} else if scaled[5] >= 1.0 {
PixelValue::Overflow
} else {
PixelValue::Color(scaled[5])
},
if !mask[6] {
PixelValue::Bad
} else if scaled[6] <= 0.0 {
PixelValue::Underflow
} else if scaled[6] >= 1.0 {
PixelValue::Overflow
} else {
PixelValue::Color(scaled[6])
},
if !mask[7] {
PixelValue::Bad
} else if scaled[7] <= 0.0 {
PixelValue::Underflow
} else if scaled[7] >= 1.0 {
PixelValue::Overflow
} else {
PixelValue::Color(scaled[7])
},
]
}
#[cfg(test)]
mod batch_integration_tests {
use super::*;
#[test]
fn test_batch_scale_linear() {
let values = [0.0, 2.5, 5.0, 7.5, 10.0, 1.0, 3.0, 9.0];
let mask = [true; 8];
let (result, _) = simd_batch_scale_8(values, 0.0, 10.0, false, None, mask);
let expected = [0.0, 0.25, 0.5, 0.75, 1.0, 0.1, 0.3, 0.9];
for i in 0..8 {
assert!((result[i] - expected[i]).abs() < 1e-14);
}
}
#[test]
fn test_batch_scale_log() {
let values = [1.0, 10.0, 100.0, 1000.0, 5.0, 50.0, 10.0, 100.0];
let log_min = 1.0_f64.ln();
let log_range = 100.0_f64.ln() - log_min;
let mask = [true; 8];
let (result, _) =
simd_batch_scale_8(values, 1.0, 100.0, true, Some((log_min, log_range)), mask);
assert!((result[0] - 0.0).abs() < 1e-14); assert!((result[2] - 1.0).abs() < 1e-14); }
#[test]
fn test_simd_to_pixel_values() {
let scaled = [0.0, 0.5, 1.0, 0.25, 0.75, -0.1, 1.1, 0.5];
let mask = [true, true, true, true, true, false, false, true];
let pixel_values = simd_to_pixel_values(scaled, mask);
match pixel_values[0] {
PixelValue::Underflow => {} _ => panic!("Expected Underflow for value 0.0"),
}
match pixel_values[1] {
PixelValue::Color(c) => assert_eq!(c, 0.5),
_ => panic!("Expected Color(0.5)"),
}
match pixel_values[2] {
PixelValue::Overflow => {} _ => panic!("Expected Overflow for value 1.0"),
}
match pixel_values[3] {
PixelValue::Color(c) => assert_eq!(c, 0.25),
_ => panic!("Expected Color(0.25)"),
}
match pixel_values[4] {
PixelValue::Color(c) => assert_eq!(c, 0.75),
_ => panic!("Expected Color(0.75)"),
}
match pixel_values[5] {
PixelValue::Bad => {} _ => panic!("Expected Bad for masked value"),
}
match pixel_values[6] {
PixelValue::Bad => {} _ => panic!("Expected Bad for masked value"),
}
match pixel_values[7] {
PixelValue::Color(c) => assert_eq!(c, 0.5),
_ => panic!("Expected Color(0.5)"),
}
}
}
#[inline]
pub fn simd_sin_cos_16(angles: [f64; 16]) -> ([f64; 16], [f64; 16]) {
let (sin_lo, cos_lo) = simd_sin_cos_8([
angles[0], angles[1], angles[2], angles[3], angles[4], angles[5], angles[6], angles[7],
]);
let (sin_hi, cos_hi) = simd_sin_cos_8([
angles[8], angles[9], angles[10], angles[11], angles[12], angles[13], angles[14],
angles[15],
]);
let mut sin_result = [0.0; 16];
let mut cos_result = [0.0; 16];
sin_result[..8].copy_from_slice(&sin_lo);
cos_result[..8].copy_from_slice(&cos_lo);
sin_result[8..16].copy_from_slice(&sin_hi);
cos_result[8..16].copy_from_slice(&cos_hi);
(sin_result, cos_result)
}
#[inline]
pub fn simd_batch_scale_16(
values: [f64; 16],
min: f64,
max: f64,
use_log: bool,
log_cache: Option<(f64, f64)>,
mask: [bool; 16],
) -> ([f64; 16], [bool; 16]) {
let (scaled_lo, mask_lo) = simd_batch_scale_8(
[
values[0], values[1], values[2], values[3], values[4], values[5], values[6], values[7],
],
min,
max,
use_log,
log_cache,
[
mask[0], mask[1], mask[2], mask[3], mask[4], mask[5], mask[6], mask[7],
],
);
let (scaled_hi, mask_hi) = simd_batch_scale_8(
[
values[8], values[9], values[10], values[11], values[12], values[13], values[14],
values[15],
],
min,
max,
use_log,
log_cache,
[
mask[8], mask[9], mask[10], mask[11], mask[12], mask[13], mask[14], mask[15],
],
);
let mut result = [0.0; 16];
let mut out_mask = [false; 16];
result[..8].copy_from_slice(&scaled_lo);
out_mask[..8].copy_from_slice(&mask_lo);
result[8..16].copy_from_slice(&scaled_hi);
out_mask[8..16].copy_from_slice(&mask_hi);
(result, out_mask)
}
#[inline]
pub fn simd_to_pixel_values_16(scaled: [f64; 16], mask: [bool; 16]) -> [PixelValue; 16] {
let pixel_lo = simd_to_pixel_values(
[
scaled[0], scaled[1], scaled[2], scaled[3], scaled[4], scaled[5], scaled[6], scaled[7],
],
[
mask[0], mask[1], mask[2], mask[3], mask[4], mask[5], mask[6], mask[7],
],
);
let pixel_hi = simd_to_pixel_values(
[
scaled[8], scaled[9], scaled[10], scaled[11], scaled[12], scaled[13], scaled[14],
scaled[15],
],
[
mask[8], mask[9], mask[10], mask[11], mask[12], mask[13], mask[14], mask[15],
],
);
let mut result = [PixelValue::Bad; 16];
result[..8].copy_from_slice(&pixel_lo);
result[8..16].copy_from_slice(&pixel_hi);
result
}
#[cfg(test)]
mod batch_16_tests {
use super::*;
#[test]
fn test_simd_sin_cos_16() {
let mut angles = [0.0; 16];
for (i, item) in angles.iter_mut().enumerate() {
*item = (i as f64) * std::f64::consts::PI / 8.0;
}
let (sines, cosines) = simd_sin_cos_16(angles);
for i in 0..16 {
let (expected_sin, expected_cos) = angles[i].sin_cos();
assert!(
(sines[i] - expected_sin).abs() < 1e-14,
"sin mismatch at {}",
i
);
assert!(
(cosines[i] - expected_cos).abs() < 1e-14,
"cos mismatch at {}",
i
);
}
}
#[test]
fn test_simd_batch_scale_16_linear() {
let values = [
0.0, 2.5, 5.0, 7.5, 10.0, 1.0, 3.0, 9.0, 2.0, 4.0, 6.0, 8.0, 1.5, 3.5, 5.5, 7.5,
];
let mask = [true; 16];
let (result, _) = simd_batch_scale_16(values, 0.0, 10.0, false, None, mask);
for i in 0..16 {
let expected = if values[i] <= 0.0 {
0.0
} else if values[i] >= 10.0 {
1.0
} else {
values[i] / 10.0
};
assert!(
(result[i] - expected).abs() < 1e-14,
"scale mismatch at {}: {} vs {}",
i,
result[i],
expected
);
}
}
#[test]
fn test_simd_to_pixel_values_16() {
let scaled = [
0.0, 0.25, 0.5, 0.75, 1.0, 0.1, 0.9, 0.5, 0.33, 0.67, -0.1, 1.1, 0.2, 0.8, 0.4, 0.6,
];
let mask = [
true, true, true, true, true, true, true, true, true, true, false, false, true, true,
true, true,
];
let pixel_values = simd_to_pixel_values_16(scaled, mask);
match pixel_values[0] {
PixelValue::Underflow => {}
_ => panic!("Expected Underflow at 0"),
}
match pixel_values[4] {
PixelValue::Overflow => {}
_ => panic!("Expected Overflow at 4"),
}
match pixel_values[10] {
PixelValue::Bad => {}
_ => panic!("Expected Bad at 10 (unmasked)"),
}
match pixel_values[15] {
PixelValue::Color(c) => assert_eq!(c, 0.6),
_ => panic!("Expected Color(0.6) at 15"),
}
}
}