use super::Matrix;
use super::order::Order;
use crate::index::Index;
use alloc::boxed::Box;
use alloc::collections::VecDeque;
use alloc::format;
use alloc::string::ToString;
use alloc::vec::Vec;
use core::fmt;
mod whitespace {
pub(super) const SPACE: &str = " ";
pub(super) const INDENT: &str = " ";
pub(super) const NEWLINE: &str = "\n";
}
mod matrix {
pub(super) const DELIMITER_LEFT: &str = "[";
pub(super) const DELIMITER_RIGHT: &str = "]";
}
mod row {
pub(super) const INDEX_GAP: &str = " ";
pub(super) const DELIMITER_LEFT: &str = "[ ";
pub(super) const DELIMITER_RIGHT: &str = " ]";
pub(super) const DELIMITER_PADDING: &str = " ";
pub(super) const SEPARATOR: &str = "";
pub(super) const SEPARATOR_PADDING: &str = "";
}
mod element {
pub(super) const INDEX_GAP: &str = " ";
pub(super) const SEPARATOR: &str = " ";
pub(super) const SEPARATOR_PADDING: &str = " ";
}
impl<T, O> fmt::Debug for Matrix<T, O>
where
T: fmt::Debug,
O: Order,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_empty() {
f.write_str(matrix::DELIMITER_LEFT)?;
f.write_str(matrix::DELIMITER_RIGHT)?;
return Ok(());
}
let stride = self.stride();
let shape = self.shape();
let size = self.size();
let index_width = size.to_string().chars().count();
let mut element_width = 0;
let mut element_hight = 0;
let mut cache = Vec::with_capacity(size);
for element in &self.data {
let lines = Lines::from_debug(element);
let width = lines.width();
if width > element_width {
element_width = width;
}
let height = lines.height();
if height > element_hight {
element_hight = height;
}
cache.push(lines);
}
let index_padding = whitespace::SPACE.repeat(index_width);
let element_padding = whitespace::SPACE.repeat(element_width);
f.write_str(matrix::DELIMITER_LEFT)?;
f.write_str(whitespace::NEWLINE)?;
f.write_str(whitespace::INDENT)?;
f.write_str(&index_padding)?;
f.write_str(row::INDEX_GAP)?;
f.write_str(row::DELIMITER_PADDING)?;
for col in 0..shape.ncols {
if col != 0 {
f.write_str(element::SEPARATOR_PADDING)?;
}
f.write_index(col, index_width)?;
f.write_str(element::INDEX_GAP)?;
f.write_str(&element_padding)?;
}
f.write_str(row::DELIMITER_PADDING)?;
f.write_str(row::SEPARATOR_PADDING)?;
f.write_str(whitespace::NEWLINE)?;
for row in 0..shape.nrows {
f.write_str(whitespace::INDENT)?;
f.write_index(row, index_width)?;
f.write_str(row::INDEX_GAP)?;
f.write_str(row::DELIMITER_LEFT)?;
for col in 0..shape.ncols {
if col != 0 {
f.write_str(element::SEPARATOR)?;
}
let index = Index::new(row, col).to_flattened::<O>(stride);
f.write_index(index, index_width)?;
f.write_str(element::INDEX_GAP)?;
match cache[index].next() {
None if element_width > 0 => f.write_str(&element_padding)?,
Some(line) => f.write_element_line(&line, element_width)?,
_ => (),
}
}
f.write_str(row::DELIMITER_RIGHT)?;
f.write_str(row::SEPARATOR)?;
f.write_str(whitespace::NEWLINE)?;
for _ in 1..element_hight {
f.write_str(whitespace::INDENT)?;
f.write_str(&index_padding)?;
f.write_str(row::INDEX_GAP)?;
f.write_str(row::DELIMITER_PADDING)?;
for col in 0..shape.ncols {
if col != 0 {
f.write_str(element::SEPARATOR_PADDING)?;
}
let index = Index::new(row, col).to_flattened::<O>(stride);
f.write_str(&index_padding)?;
f.write_str(element::INDEX_GAP)?;
match cache[index].next() {
None if element_width > 0 => f.write_str(&element_padding)?,
Some(line) => f.write_element_line(&line, element_width)?,
_ => (),
}
}
f.write_str(row::DELIMITER_PADDING)?;
f.write_str(row::SEPARATOR_PADDING)?;
f.write_str(whitespace::NEWLINE)?;
}
}
f.write_str(matrix::DELIMITER_RIGHT)
}
}
impl<T, O> fmt::Display for Matrix<T, O>
where
T: fmt::Display,
O: Order,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_empty() {
f.write_str(matrix::DELIMITER_LEFT)?;
f.write_str(matrix::DELIMITER_RIGHT)?;
return Ok(());
}
let stride = self.stride();
let shape = self.shape();
let size = self.size();
let mut element_width = 0;
let mut element_hight = 0;
let mut cache = Vec::with_capacity(size);
for element in &self.data {
let lines = Lines::from_display(element);
let width = lines.width();
if width > element_width {
element_width = width;
}
let height = lines.height();
if height > element_hight {
element_hight = height;
}
cache.push(lines);
}
let element_padding = whitespace::SPACE.repeat(element_width);
f.write_str(matrix::DELIMITER_LEFT)?;
f.write_str(whitespace::NEWLINE)?;
for row in 0..shape.nrows {
f.write_str(whitespace::INDENT)?;
f.write_str(row::DELIMITER_LEFT)?;
for col in 0..shape.ncols {
if col != 0 {
f.write_str(element::SEPARATOR)?;
}
let index = Index::new(row, col).to_flattened::<O>(stride);
match cache[index].next() {
None if element_width > 0 => f.write_str(&element_padding)?,
Some(line) => f.write_element_line(&line, element_width)?,
_ => (),
}
}
f.write_str(row::DELIMITER_RIGHT)?;
f.write_str(row::SEPARATOR)?;
f.write_str(whitespace::NEWLINE)?;
for _ in 1..element_hight {
f.write_str(whitespace::INDENT)?;
f.write_str(row::DELIMITER_PADDING)?;
for col in 0..shape.ncols {
if col != 0 {
f.write_str(element::SEPARATOR_PADDING)?;
}
let index = Index::new(row, col).to_flattened::<O>(stride);
match cache[index].next() {
None if element_width > 0 => f.write_str(&element_padding)?,
Some(line) => f.write_element_line(&line, element_width)?,
_ => (),
}
}
f.write_str(row::DELIMITER_PADDING)?;
f.write_str(row::SEPARATOR_PADDING)?;
f.write_str(whitespace::NEWLINE)?;
}
}
f.write_str(matrix::DELIMITER_RIGHT)
}
}
#[derive(Debug)]
struct Lines(VecDeque<Box<str>>);
impl Lines {
fn from_debug<T>(element: T) -> Self
where
T: fmt::Debug,
{
Self(format!("{element:?}").lines().map(Box::from).collect())
}
fn from_display<T>(element: T) -> Self
where
T: fmt::Display,
{
Self(format!("{element}").lines().map(Box::from).collect())
}
fn width(&self) -> usize {
self.0
.iter()
.map(|line| line.chars().count())
.max()
.unwrap_or(0)
}
fn height(&self) -> usize {
self.0.len()
}
}
impl Iterator for Lines {
type Item = Box<str>;
fn next(&mut self) -> Option<Self::Item> {
self.0.pop_front()
}
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.0.len();
(len, Some(len))
}
}
impl ExactSizeIterator for Lines {
fn len(&self) -> usize {
self.0.len()
}
}
trait Write {
fn write_index(&mut self, index: usize, width: usize) -> fmt::Result;
fn write_element_line(&mut self, line: &str, width: usize) -> fmt::Result;
}
impl<T> Write for T
where
T: fmt::Write,
{
#[cfg(not(feature = "pretty-debug"))]
fn write_index(&mut self, index: usize, width: usize) -> fmt::Result {
write!(self, "{index:>width$}")
}
#[cfg(feature = "pretty-debug")]
fn write_index(&mut self, index: usize, width: usize) -> fmt::Result {
let plain = format!("{index:>width$}");
let style = owo_colors::Style::new().green().dimmed();
let styled = style.style(plain);
write!(self, "{styled}")
}
fn write_element_line(&mut self, line: &str, width: usize) -> fmt::Result {
write!(self, "{line:<width$}")
}
}