use rand::prelude::*;
use rand_distr::Distribution;
use crate::base_types::{c32, c64};
use crate::dense::array::Array;
use crate::traits::accessors::RandomAccessByValue;
use crate::traits::base_operations::Shape;
use crate::traits::rlst_num::{RandScalar, RlstScalar};
impl RandScalar for f32 {
fn random_scalar<R: Rng, D: Distribution<<Self as RlstScalar>::Real>>(
rng: &mut R,
dist: &D,
) -> Self {
dist.sample(rng)
}
}
impl RandScalar for f64 {
fn random_scalar<R: Rng, D: Distribution<<Self as RlstScalar>::Real>>(
rng: &mut R,
dist: &D,
) -> Self {
dist.sample(rng)
}
}
impl RandScalar for c32 {
fn random_scalar<R: Rng, D: Distribution<<Self as RlstScalar>::Real>>(
rng: &mut R,
dist: &D,
) -> Self {
Self::new(dist.sample(rng), dist.sample(rng))
}
}
impl RandScalar for c64 {
fn random_scalar<R: Rng, D: Distribution<<Self as RlstScalar>::Real>>(
rng: &mut R,
dist: &D,
) -> Self {
Self::new(dist.sample(rng), dist.sample(rng))
}
}
#[macro_export]
macro_rules! assert_matrix_abs_diff_eq {
($expected_matrix:expr, $actual_matrix:expr, $epsilon:expr) => {{
use approx::assert_abs_diff_eq;
assert_eq!($expected_matrix.shape(), $actual_matrix.shape());
for row in 0..$expected_matrix.shape().0 {
for col in 0..$expected_matrix.shape().1 {
assert_abs_diff_eq!(
$actual_matrix[[row, col]],
$expected_matrix[[row, col]],
epsilon = $epsilon
);
}
}
}};
}
#[macro_export]
macro_rules! assert_matrix_relative_eq {
($expected_matrix:expr, $actual_matrix:expr, $epsilon:expr) => {{
use approx::assert_relative_eq;
assert_eq!($expected_matrix.shape(), $actual_matrix.shape());
for row in 0..$expected_matrix.shape().0 {
for col in 0..$expected_matrix.shape().1 {
assert_relative_eq!(
$actual_matrix[[row, col]],
$expected_matrix[[row, col]],
max_relative = $epsilon
);
}
}
}};
}
#[macro_export]
macro_rules! assert_array_abs_diff_eq {
($expected_array:expr, $actual_array:expr, $epsilon:expr) => {{
use approx::assert_abs_diff_eq;
assert_eq!($expected_array.shape(), $actual_array.shape());
for (actual, expected) in $actual_array.iter_value().zip($expected_array.iter_value()) {
assert_abs_diff_eq!(actual, expected, epsilon = $epsilon);
}
}};
}
#[macro_export]
macro_rules! assert_array_relative_eq {
($expected_array:expr, $actual_array:expr, $epsilon:expr) => {{
use approx::assert_relative_eq;
assert_eq!($expected_array.shape(), $actual_array.shape());
for (actual, expected) in $actual_array.iter_value().zip($expected_array.iter_value()) {
assert_relative_eq!(expected, actual, max_relative = $epsilon);
}
}};
}
#[macro_export]
macro_rules! assert_matrix_ulps_eq {
($expected_matrix:expr, $actual_matrix:expr, $ulps:expr) => {{
use approx::assert_abs_ulps_eq;
assert_eq!($expected_matrix.shape(), $actual_matrix.shape());
for row in 0..$expected_matrix.shape().0 {
for col in 0..$expected_matrix.shape().1 {
assert_abs_ulps_eq!(
$actual_matrix[[row, col]],
$expected_matrix[[row, col]],
max_ulps = $ulps
);
}
}
}};
}
pub trait PrettyPrint<T: RlstScalar> {
fn pretty_print(&self);
fn pretty_print_with_dimension(&self, rows: usize, cols: usize);
fn pretty_print_advanced(
&self,
rows: usize,
cols: usize,
print_width: usize,
mantissa: usize,
exponent: usize,
);
}
macro_rules! pretty_print_impl {
($scalar:ty, $fmtfun:ident) => {
impl<ArrayImpl: RandomAccessByValue<2, Item = $scalar> + Shape<2>> PrettyPrint<$scalar>
for Array<ArrayImpl, 2>
{
fn pretty_print(&self) {
self.pretty_print_advanced(10, 10, 11, 3, 2)
}
fn pretty_print_with_dimension(&self, rows: usize, cols: usize) {
self.pretty_print_advanced(rows, cols, 11, 3, 2)
}
fn pretty_print_advanced(
&self,
rows: usize,
cols: usize,
print_width: usize,
mantissa: usize,
exponent: usize,
) {
let shape = (
std::cmp::min(self.shape()[0], rows),
std::cmp::min(self.shape()[1], cols),
);
let mut content_str = String::new();
let print_width = std::cmp::max(print_width, 5 + mantissa + exponent);
let num_outer_spaces = print_width - mantissa - exponent - 4;
let mut x_ij = self.get_value([0, 0]).unwrap();
for row in 0..shape.0 {
content_str += "│";
for col in 0..shape.1 {
x_ij = self.get_value([row, col]).unwrap();
content_str += &format!(
" {}",
$fmtfun::<$scalar>(x_ij, print_width, mantissa, exponent)
);
}
content_str += &" ".repeat(num_outer_spaces);
content_str += "│\n";
}
let colwidth = $fmtfun::<$scalar>(x_ij, print_width, mantissa, exponent)
.chars()
.count();
let top_str = format!(
"\n┌{}┐\n",
" ".repeat((shape.1 * (colwidth + 1)) + num_outer_spaces)
);
let btm_str = format!(
"└{}┘\n",
" ".repeat((shape.1 * (colwidth + 1)) + num_outer_spaces)
);
println!(
"Printing the upper left {} x {} block of a matrix with dimensions {} x {}.",
shape.0,
shape.1,
self.shape()[0],
self.shape()[1]
);
println!("{top_str}{content_str}{btm_str}");
}
}
};
}
pretty_print_impl!(f64, fmt_real);
pretty_print_impl!(f32, fmt_real);
pretty_print_impl!(c32, fmt_complex);
pretty_print_impl!(c64, fmt_complex);
fn fmt_real<T: RlstScalar>(num: T::Real, width: usize, precision: usize, exp_pad: usize) -> String {
let mut num = format!("{num:.precision$e}");
let exp = num.split_off(num.find('e').unwrap());
let (sign, exp) = exp
.strip_prefix("e-")
.map_or_else(|| ('+', &exp[1..]), |stripped| ('-', stripped));
num.push_str(&format!("e{sign}{exp:0>exp_pad$}"));
format!("{num:>width$}")
}
fn fmt_complex<T: RlstScalar>(num: T, width: usize, precision: usize, exp_pad: usize) -> String {
let sign = if num.im() < <T::Real as num::Zero>::zero() {
"-"
} else {
"+"
};
let mut printstr = format!(
"{}{}",
fmt_real::<T::Real>(num.re(), width, precision, exp_pad),
fmt_real::<T::Real>(num.im().abs(), width, precision, exp_pad),
);
let num_blanks = width - precision - exp_pad - 4;
if num_blanks.is_multiple_of(2) {
printstr = format!(
"{} {}𝑖",
fmt_real::<T::Real>(num.re(), width, precision, exp_pad),
fmt_real::<T::Real>(num.im().abs(), width, precision, exp_pad),
);
}
let sign_position = width + (num_blanks / 2);
printstr.replace_range(sign_position..=sign_position, sign);
printstr
}