use crate::error::MathError;
use crate::traits::MatrixDimensions;
use std::fmt::Display;
pub fn evaluate_index(index: impl TryInto<i64> + Display) -> Result<i64, MathError> {
let index: i64 = if cfg!(debug_assertions) {
let index_str = index.to_string();
match index.try_into() {
Ok(index) => index,
_ => {
return Err(MathError::OutOfBounds(
"fit into a i64".to_owned(),
index_str,
));
}
}
} else {
match index.try_into() {
Ok(index) => index,
_ => {
return Err(MathError::OutOfBounds(
"fit into a i64".to_owned(),
"rerun in debug mode to obtain the incorrect index".to_owned(),
));
}
}
};
if index < 0 {
return Err(MathError::OutOfBounds(
"be at least zero".to_owned(),
index.to_string(),
));
}
Ok(index)
}
pub fn evaluate_indices(
index1: impl TryInto<i64> + Display,
index2: impl TryInto<i64> + Display,
) -> Result<(i64, i64), MathError> {
let index1_i64 = evaluate_index(index1)?;
let index2_i64 = evaluate_index(index2)?;
Ok((index1_i64, index2_i64))
}
pub fn evaluate_index_for_vector(
index: impl TryInto<i64> + Display,
vector_length: i64,
) -> Result<i64, MathError> {
let mut index_i64: i64 = match index.try_into() {
Ok(index) => index,
_ => {
return Err(MathError::OutOfBounds(
"fit into a i64".to_owned(),
"unknown for performance reasons".to_owned(),
));
}
};
if index_i64 < 0 {
index_i64 += vector_length;
if index_i64 < 0 {
return Err(MathError::OutOfBounds(
format!("be larger or equal to {}", -vector_length),
format!("{}", index_i64 - vector_length),
));
}
}
if vector_length <= index_i64 {
return Err(MathError::OutOfBounds(
format!("be smaller than {vector_length}"),
format!("{index_i64}"),
));
}
Ok(index_i64)
}
pub fn evaluate_indices_for_matrix<S: MatrixDimensions + MatrixDimensions>(
matrix: &S,
row: impl TryInto<i64> + Display,
column: impl TryInto<i64> + Display,
) -> Result<(i64, i64), MathError> {
let mut row_i64: i64 = match row.try_into() {
Ok(index) => index,
_ => {
return Err(MathError::OutOfBounds(
"fit into a i64".to_owned(),
"unknown for performance reasons".to_owned(),
));
}
};
let mut column_i64: i64 = match column.try_into() {
Ok(index) => index,
_ => {
return Err(MathError::OutOfBounds(
"fit into a i64".to_owned(),
"unknown for performance reasons".to_owned(),
));
}
};
if row_i64 < 0 {
row_i64 += matrix.get_num_rows();
if row_i64 < 0 {
return Err(MathError::OutOfBounds(
format!(
"be larger or equal to ({}, {})",
-matrix.get_num_rows(),
-matrix.get_num_columns()
),
format!("({}, {})", row_i64 - matrix.get_num_rows(), column_i64),
));
}
}
if column_i64 < 0 {
column_i64 += matrix.get_num_columns();
if column_i64 < 0 {
return Err(MathError::OutOfBounds(
format!(
"be larger or equal to ({}, {})",
-matrix.get_num_rows(),
-matrix.get_num_columns()
),
format!("({}, {})", row_i64, column_i64 - matrix.get_num_columns()),
));
}
}
if matrix.get_num_rows() <= row_i64 || matrix.get_num_columns() <= column_i64 {
return Err(MathError::OutOfBounds(
format!(
"be smaller than ({}, {})",
matrix.get_num_rows(),
matrix.get_num_columns()
),
format!("({row_i64}, {column_i64})"),
));
}
Ok((row_i64, column_i64))
}
fn bit_reverse(mut x: usize, log_n: usize) -> usize {
let mut res = 0;
for _ in 0..log_n {
res = (res << 1) | (x & 1);
x >>= 1;
}
res
}
pub fn bit_reverse_permutation<T>(a: &mut [T]) {
let n = a.len();
let log_n = n.trailing_zeros() as usize;
for i in 0..n {
let rev_i = bit_reverse(i, log_n);
if i < rev_i {
a.swap(i, rev_i);
}
}
}
#[cfg(test)]
mod test_eval_index {
use super::evaluate_index;
use crate::integer::Z;
#[test]
fn is_err_negative() {
assert!(evaluate_index(i32::MIN).is_err());
}
#[test]
fn is_ok_several_types() {
assert!(evaluate_index(i8::MAX).is_ok());
assert!(evaluate_index(i16::MAX).is_ok());
assert!(evaluate_index(i32::MAX).is_ok());
assert!(evaluate_index(i64::MAX).is_ok());
assert!(evaluate_index(u8::MAX).is_ok());
assert!(evaluate_index(u16::MAX).is_ok());
assert!(evaluate_index(u32::MAX).is_ok());
assert!(evaluate_index(Z::from(10)).is_ok());
}
#[test]
fn does_not_fit() {
assert!(evaluate_index(u64::MAX).is_err());
}
}
#[cfg(test)]
mod test_eval_index_for_vector {
use super::evaluate_index_for_vector;
#[test]
fn small_negative() {
let a = evaluate_index_for_vector(-1, 10).expect("No error with small negative index.");
assert_eq!(a, 9);
}
#[test]
fn negative_out_of_bounds() {
assert!(evaluate_index_for_vector(-4, 3).is_err());
assert!(evaluate_index_for_vector(3, 3).is_err());
}
#[test]
fn is_ok_several_types() {
assert!(evaluate_index_for_vector(3i8, 4).is_ok());
assert!(evaluate_index_for_vector(3i16, 4).is_ok());
assert!(evaluate_index_for_vector(3i32, 4).is_ok());
assert!(evaluate_index_for_vector(3i64, 4).is_ok());
assert!(evaluate_index_for_vector(3u8, 4).is_ok());
assert!(evaluate_index_for_vector(3u16, 4).is_ok());
assert!(evaluate_index_for_vector(3u32, 4).is_ok());
assert!(evaluate_index_for_vector(3u64, 4).is_ok());
}
#[test]
fn does_not_fit() {
assert!(evaluate_index_for_vector(u64::MAX - 1, i64::MAX).is_err());
}
}
#[cfg(test)]
mod test_eval_indices {
use super::evaluate_indices_for_matrix;
use crate::integer::MatZ;
#[test]
fn small_negative() {
let matrix = MatZ::new(10, 10);
let (a, b) = evaluate_indices_for_matrix(&matrix, -1, -10)
.expect("No error with small negative index.");
assert_eq!(a, 9);
assert_eq!(b, 0);
}
#[test]
fn negative_out_of_bounds() {
let matrix = MatZ::new(1, 1);
assert!(evaluate_indices_for_matrix(&matrix, 0, -2).is_err());
assert!(evaluate_indices_for_matrix(&matrix, -2, 0).is_err());
assert!(evaluate_indices_for_matrix(&matrix, -2, -2).is_err());
}
#[test]
fn is_ok_several_types() {
let matrix = MatZ::new(i16::MAX, u8::MAX);
assert!(evaluate_indices_for_matrix(&matrix, 3i8, 0).is_ok());
assert!(evaluate_indices_for_matrix(&matrix, 3i16, 0).is_ok());
assert!(evaluate_indices_for_matrix(&matrix, 3i32, 0).is_ok());
assert!(evaluate_indices_for_matrix(&matrix, 3i64, 0).is_ok());
assert!(evaluate_indices_for_matrix(&matrix, 3u8, 0).is_ok());
assert!(evaluate_indices_for_matrix(&matrix, 3u16, 0).is_ok());
assert!(evaluate_indices_for_matrix(&matrix, 3u32, 0).is_ok());
assert!(evaluate_indices_for_matrix(&matrix, 3u64, 0).is_ok());
assert!(evaluate_indices_for_matrix(&matrix, 0, 3i8).is_ok());
assert!(evaluate_indices_for_matrix(&matrix, 0, 3i16).is_ok());
assert!(evaluate_indices_for_matrix(&matrix, 0, 3i32).is_ok());
assert!(evaluate_indices_for_matrix(&matrix, 0, 3i64).is_ok());
assert!(evaluate_indices_for_matrix(&matrix, 0, 3u8).is_ok());
assert!(evaluate_indices_for_matrix(&matrix, 0, 3u16).is_ok());
assert!(evaluate_indices_for_matrix(&matrix, 0, 3u32).is_ok());
assert!(evaluate_indices_for_matrix(&matrix, 0, 3u64).is_ok());
}
#[test]
fn does_not_fit() {
let matrix = MatZ::new(3, 3);
assert!(evaluate_indices_for_matrix(&matrix, u64::MAX, 0).is_err());
assert!(evaluate_indices_for_matrix(&matrix, 0, u64::MAX).is_err());
}
}
#[cfg(test)]
mod test_bit_reversed_order {
use super::bit_reverse_permutation;
#[test]
fn correct_new_order() {
let mut vec: Vec<i64> = (0..16).collect();
bit_reverse_permutation(&mut vec);
let cmp_vec = vec![0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15];
assert_eq!(cmp_vec, vec);
}
#[test]
fn self_inverse() {
let vec: Vec<usize> = (0..12332).collect();
let mut vec_perm = vec.clone();
bit_reverse_permutation(&mut vec_perm);
bit_reverse_permutation(&mut vec_perm);
assert_eq!(vec, vec_perm);
}
}