use crate::array::Array;
use lazy_static::lazy_static;
use std::sync::RwLock;
pub struct PrintOptions {
pub threshold: usize,
pub edgeitems: usize,
pub linewidth: usize,
pub suppress: bool,
pub precision: usize,
pub sign: char,
pub separator: String,
pub prefix: String,
pub suffix: String,
}
impl Clone for PrintOptions {
fn clone(&self) -> Self {
Self {
threshold: self.threshold,
edgeitems: self.edgeitems,
linewidth: self.linewidth,
suppress: self.suppress,
precision: self.precision,
sign: self.sign,
separator: self.separator.clone(),
prefix: self.prefix.clone(),
suffix: self.suffix.clone(),
}
}
}
impl Default for PrintOptions {
fn default() -> Self {
Self {
threshold: 1000,
edgeitems: 3,
linewidth: 75,
suppress: false,
precision: 8,
sign: '-',
separator: " ".to_string(),
prefix: "".to_string(),
suffix: "".to_string(),
}
}
}
lazy_static! {
static ref PRINT_OPTIONS: RwLock<PrintOptions> = RwLock::new(PrintOptions::default());
}
pub fn set_printoptions(
precision: Option<usize>,
threshold: Option<usize>,
edgeitems: Option<usize>,
linewidth: Option<usize>,
suppress: Option<bool>,
) {
let mut options = PRINT_OPTIONS
.write()
.expect("PRINT_OPTIONS RwLock poisoned: failed to acquire write lock");
if let Some(p) = precision {
options.precision = p;
}
if let Some(t) = threshold {
options.threshold = t;
}
if let Some(e) = edgeitems {
options.edgeitems = e;
}
if let Some(l) = linewidth {
options.linewidth = l;
}
if let Some(s) = suppress {
options.suppress = s;
}
}
pub fn get_printoptions() -> PrintOptions {
PRINT_OPTIONS
.read()
.expect("PRINT_OPTIONS RwLock poisoned: failed to acquire read lock")
.clone()
}
pub fn reset_printoptions() {
let mut options = PRINT_OPTIONS
.write()
.expect("PRINT_OPTIONS RwLock poisoned: failed to acquire write lock");
*options = PrintOptions::default();
}
pub fn format_float(value: f64) -> String {
let options = PRINT_OPTIONS
.read()
.expect("PRINT_OPTIONS RwLock poisoned: failed to acquire read lock");
if options.suppress && value.abs() < 10_f64.powi(-(options.precision as i32)) {
return "0".to_string();
}
match options.sign {
'+' => format!("{:+.*}", options.precision, value),
' ' => {
if value >= 0.0 {
format!(" {:.*}", options.precision, value)
} else {
format!("{:.*}", options.precision, value)
}
}
_ => format!("{:.*}", options.precision, value),
}
}
pub fn format_int<T: std::fmt::Display>(value: T) -> String {
let options = PRINT_OPTIONS
.read()
.expect("PRINT_OPTIONS RwLock poisoned: failed to acquire read lock");
match options.sign {
'+' => format!("{:+}", value),
' ' => format!(" {}", value),
_ => format!("{}", value),
}
}
pub fn array_str<T>(
array: &Array<T>,
precision: Option<usize>,
threshold: Option<usize>,
edgeitems: Option<usize>,
) -> String
where
T: Clone + std::fmt::Display + std::fmt::Debug,
{
let original_options = get_printoptions();
set_printoptions(precision, threshold, edgeitems, None, None);
let result = format!("{}", array);
let mut options = PRINT_OPTIONS
.write()
.expect("PRINT_OPTIONS RwLock poisoned: failed to acquire write lock");
*options = original_options;
result
}
#[cfg(test)]
mod tests {
use super::*;
use crate::array::Array;
#[test]
fn test_set_and_get_printoptions() {
let original = get_printoptions();
set_printoptions(Some(2), Some(100), Some(2), Some(50), Some(true));
let options = get_printoptions();
assert_eq!(options.precision, 2);
assert_eq!(options.threshold, 100);
assert_eq!(options.edgeitems, 2);
assert_eq!(options.linewidth, 50);
assert!(options.suppress);
let mut global_options = PRINT_OPTIONS
.write()
.expect("PRINT_OPTIONS lock should not be poisoned in test");
*global_options = original;
}
#[test]
fn test_reset_printoptions() {
set_printoptions(Some(2), Some(100), None, None, None);
reset_printoptions();
let options = get_printoptions();
assert_eq!(options.precision, 8);
assert_eq!(options.threshold, 1000);
assert_eq!(options.edgeitems, 3);
}
#[test]
fn test_array_display_1d() {
let arr = Array::from_vec(vec![1.0, 2.0, 3.0]);
let display = format!("{}", arr);
assert!(display.contains("["));
assert!(display.contains("]"));
assert!(display.contains("1"));
assert!(display.contains("2"));
assert!(display.contains("3"));
}
#[test]
fn test_array_display_2d() {
let arr = Array::from_vec(vec![1.0, 2.0, 3.0, 4.0]).reshape(&[2, 2]);
let display = format!("{}", arr);
assert!(display.contains("["));
assert!(display.contains("]"));
assert!(display.contains("1"));
assert!(display.contains("2"));
assert!(display.contains("3"));
assert!(display.contains("4"));
}
#[test]
fn test_array_str_function() {
let arr = Array::from_vec(vec![1.0 / 3.0, 2.0 / 3.0, 1.0]);
let formatted = array_str(&arr, Some(2), None, None);
assert!(!formatted.is_empty());
}
#[test]
fn test_format_float() {
let original = get_printoptions();
set_printoptions(Some(2), None, None, None, None);
let formatted = format_float(1.23456);
assert!(formatted.contains("1.23"));
let mut global_options = PRINT_OPTIONS
.write()
.expect("PRINT_OPTIONS lock should not be poisoned in test");
*global_options = original;
}
#[test]
fn test_suppress_small_values() {
let original = get_printoptions();
set_printoptions(Some(8), None, None, None, Some(true));
let formatted = format_float(1e-10);
assert_eq!(formatted, "0");
let mut global_options = PRINT_OPTIONS
.write()
.expect("PRINT_OPTIONS lock should not be poisoned in test");
*global_options = original;
}
#[test]
fn test_sign_formatting() {
let original = get_printoptions();
{
let mut options = PRINT_OPTIONS
.write()
.expect("PRINT_OPTIONS lock should not be poisoned in test");
options.sign = '+';
}
let formatted = format_float(1.23);
assert!(formatted.starts_with('+'));
let mut global_options = PRINT_OPTIONS
.write()
.expect("PRINT_OPTIONS lock should not be poisoned in test");
*global_options = original;
}
}