use std::{fmt::Display, fs::File, path::Path, sync::Arc};
use enum_map::EnumMap;
use crate::{
format::{DOLLAR40_2, F40_2, Format, PCT40_1},
output::{
Text,
drivers::{
Driver,
cairo::{CairoConfig, CairoDriver},
html::HtmlDriver,
spv::SpvDriver,
},
pivot::{
Axis2, Class, Dimension, Footnote, FootnoteMarkerPosition, FootnoteMarkerType,
Footnotes, Group, PivotTable,
look::{
Area, Border, BorderStyle, Color, HeadingRegion, HorzAlign, LabelPosition, Look,
RowColBorder, Stroke,
},
value::TemplateValue,
},
},
};
use super::{
Axis3,
value::{Value, ValueInner},
};
#[test]
fn color() {
assert_eq!("#112233".parse(), Ok(Color::new(0x11, 0x22, 0x33)));
assert_eq!("112233".parse(), Ok(Color::new(0x11, 0x22, 0x33)));
assert_eq!("rgb(11,22,33)".parse(), Ok(Color::new(11, 22, 33)));
assert_eq!(
"rgba(11,22,33, 0.25)".parse(),
Ok(Color::new(11, 22, 33).with_alpha(64))
);
assert_eq!("lavender".parse(), Ok(Color::new(230, 230, 250)));
assert_eq!("transparent".parse(), Ok(Color::new(0, 0, 0).with_alpha(0)));
}
fn d1(title: &str, axis: Axis3) -> PivotTable {
let dimension = Dimension::new(
Group::new("a")
.with_label_shown()
.with("a1")
.with("a2")
.with("a3"),
);
let mut pt = PivotTable::new([(axis, dimension)])
.with_title(title)
.with_look(Arc::new(test_look()));
for i in 0..3 {
pt.insert(&[i], Value::new_integer(Some(i as f64)));
}
pt
}
#[test]
fn d1_c() {
assert_rendering("d1_c", &d1("Columns", Axis3::X));
}
#[test]
fn d1_r() {
assert_rendering("d1_r", &d1("Rows", Axis3::Y));
}
fn test_look() -> Look {
let mut look = Look::default();
look.areas[Area::Title].cell_style.horz_align = Some(HorzAlign::Left);
look.areas[Area::Title].font_style.bold = false;
look
}
fn d2(title: &str, axes: [Axis3; 2], dimension_labels: Option<LabelPosition>) -> PivotTable {
let d1 = Dimension::new(
Group::new("a")
.with_show_label(dimension_labels.is_some())
.with("a1")
.with("a2")
.with("a3"),
);
let d2 = Dimension::new(
Group::new("b")
.with_show_label(dimension_labels.is_some())
.with("b1")
.with("b2")
.with("b3"),
);
let mut pt = PivotTable::new([(axes[0], d1), (axes[1], d2)]).with_title(title);
let mut i = 0;
for b in 0..3 {
for a in 0..3 {
pt.insert(&[a, b], Value::new_integer(Some(i as f64)));
i += 1;
}
}
let look = match dimension_labels {
Some(position) => test_look().with_row_label_position(position),
None => test_look(),
};
pt.with_look(Arc::new(look))
}
#[track_caller]
pub fn assert_lines_eq<E, A>(expected: &str, expected_name: E, actual: &str, actual_name: A)
where
E: Display,
A: Display,
{
if expected != actual {
eprintln!("Unexpected output:\n--- {expected_name}\n+++ {actual_name}");
for result in diff::lines(expected, &actual) {
let (prefix, line) = match result {
diff::Result::Left(line) => ('-', line),
diff::Result::Both(line, _) => (' ', line),
diff::Result::Right(line) => ('+', line),
};
let suffix = if line.trim_end().len() != line.len() {
"$"
} else {
""
};
eprintln!("{prefix}{line}{suffix}");
}
panic!();
}
}
#[track_caller]
pub fn assert_rendering(name: &str, pivot_table: &PivotTable) {
let item = Arc::new(pivot_table.clone().into_item());
if let Some(dir) = std::env::var_os("PSPP_TEST_HTML_DIR") {
let writer = File::create(Path::new(&dir).join(name).with_extension("html")).unwrap();
HtmlDriver::for_writer(writer).write(&item);
}
let item = Arc::new(pivot_table.clone().into_item());
if let Some(dir) = std::env::var_os("PSPP_TEST_PDF_DIR") {
let config = CairoConfig::new(Path::new(&dir).join(name).with_extension("pdf"));
let mut pdf_driver = CairoDriver::new(&config).unwrap();
pdf_driver.write(&Arc::new(
Text::new(crate::output::TextType::PageTitle, "page title").into_item(),
));
pdf_driver.write(&item);
}
if let Some(dir) = std::env::var_os("PSPP_TEST_SPV_DIR") {
let writer = File::create(Path::new(&dir).join(name).with_extension("spv")).unwrap();
let mut spv_driver = SpvDriver::for_writer(writer);
spv_driver.write(&Arc::new(
Text::new(crate::output::TextType::PageTitle, "page title").into_item(),
));
spv_driver.write(&item);
}
let expected_filename = Path::new("src/output/pivot/testdata")
.join(name)
.with_extension("expected");
let actual = pivot_table.to_string();
let expected = std::fs::read_to_string(&expected_filename).unwrap();
if expected != actual {
if std::env::var("PSPP_REFRESH_EXPECTED").is_ok() {
std::fs::write(&expected_filename, actual).unwrap();
panic!("{}: refreshed output", expected_filename.display());
} else {
eprintln!("note: rerun with PSPP_REFRESH_EXPECTED=1 to refresh expected output");
}
}
assert_lines_eq(
&expected,
expected_filename.display(),
&actual,
format!("actual"),
);
}
#[test]
fn d2_cc() {
assert_rendering("d2_cc", &d2("Columns", [Axis3::X, Axis3::X], None));
}
#[test]
fn d2_cc_with_dim_labels() {
assert_rendering(
"d2_cc_with_dim_labels",
&d2("Columns", [Axis3::X, Axis3::X], Some(LabelPosition::Corner)),
);
}
#[test]
fn d2_rr() {
assert_rendering("d2_rr", &d2("Rows", [Axis3::Y, Axis3::Y], None));
}
#[test]
fn d2_rr_with_corner_dim_labels() {
assert_rendering(
"d2_rr_with_corner_dim_labels",
&d2(
"Rows - Corner",
[Axis3::Y, Axis3::Y],
Some(LabelPosition::Corner),
),
);
}
#[test]
fn d2_rr_with_nested_dim_labels() {
assert_rendering(
"d2_rr_with_nested_dim_labels",
&d2(
"Rows - Nested",
[Axis3::Y, Axis3::Y],
Some(LabelPosition::Nested),
),
);
}
#[test]
fn d2_cr() {
assert_rendering("d2_cr", &d2("Column x Row", [Axis3::X, Axis3::Y], None));
}
#[test]
fn d2_cr_with_corner_dim_labels() {
assert_rendering(
"d2_cr_with_corner_dim_labels",
&d2(
"Column x Row - Corner",
[Axis3::X, Axis3::Y],
Some(LabelPosition::Corner),
),
);
}
#[test]
fn d2_cr_with_nested_dim_labels() {
assert_rendering(
"d2_cr_with_nested_dim_labels",
&d2(
"Column x Row - Nested",
[Axis3::X, Axis3::Y],
Some(LabelPosition::Nested),
),
);
}
#[test]
fn d2_rc() {
assert_rendering("d2_rc", &d2("Row x Column", [Axis3::Y, Axis3::X], None));
}
#[test]
fn d2_rc_with_corner_dim_labels() {
assert_rendering(
"d2_rc_with_corner_dim_labels",
&d2(
"Row x Column - Corner",
[Axis3::Y, Axis3::X],
Some(LabelPosition::Corner),
),
);
}
#[test]
fn d2_rc_with_nested_dim_labels() {
assert_rendering(
"d2_rc_with_nested_dim_labels",
&d2(
"Row x Column - Nested",
[Axis3::Y, Axis3::X],
Some(LabelPosition::Nested),
),
);
}
#[test]
fn d2_cl() {
let pivot_table = d2("Column x b1", [Axis3::X, Axis3::Z], None);
assert_rendering("d2_cl-layer0", &pivot_table);
let pivot_table = pivot_table
.with_layer(&[1])
.with_title(Value::new_text("Column x b2"));
assert_rendering("d2_cl-layer1", &pivot_table);
let pivot_table = pivot_table
.with_all_layers()
.with_title(Value::new_text("Column (All Layers)"));
assert_rendering("d2_cl-all_layers", &pivot_table);
}
#[test]
fn d2_rl() {
let pivot_table = d2("Row x b1", [Axis3::Y, Axis3::Z], None);
assert_rendering("d2_rl-layer0", &pivot_table);
let pivot_table = pivot_table
.with_layer(&[1])
.with_title(Value::new_text("Row x b2"));
assert_rendering("d2_rl-layer1", &pivot_table);
let pivot_table = pivot_table
.with_all_layers()
.with_title(Value::new_text("Row (All Layers)"));
assert_rendering("d2_rl-all_layers", &pivot_table);
}
fn d2m(title: &str, axes: [Axis3; 2], dimension_labels: Option<LabelPosition>) -> PivotTable {
let d1 = Dimension::new(
Group::new("a")
.with_show_label(dimension_labels.is_some())
.with("a1")
.with("a2")
.with("a3"),
);
let d2 = Dimension::new(
Group::new("outer")
.with_show_label(dimension_labels.is_some())
.with(Group::new("b").with(Group::new("b1").with("b2")).with("b3"))
.with(Group::new("c").with("c1"))
.with("d")
.with("e"),
);
let mut pt = PivotTable::new([(axes[0], d1), (axes[1], d2)]).with_title(title);
let mut i = 0;
for b in 0..5 {
for a in 0..3 {
pt.insert(&[a, b], Value::new_integer(Some(i as f64)));
i += 1;
}
}
let look = match dimension_labels {
Some(position) => test_look().with_row_label_position(position),
None => test_look(),
};
pt.with_look(Arc::new(look))
}
#[test]
fn d2m_cc() {
assert_rendering("d2m_cc", &d2m("Columns", [Axis3::X, Axis3::X], None));
}
#[test]
fn d2m_rr() {
assert_rendering("d2m_rr", &d2m("Rows", [Axis3::Y, Axis3::Y], None));
}
#[test]
fn d2m_rc() {
assert_rendering("d2m_rc", &d2m("Row x Column", [Axis3::Y, Axis3::X], None));
}
#[test]
fn d2m_cr() {
assert_rendering("d2m_cr", &d2m("Column x Row", [Axis3::X, Axis3::Y], None));
}
#[test]
fn d3() {
let a = (
Axis3::Z,
Dimension::new(Group::new("a").with("a1").with("a2").with("a3")),
);
let b = (
Axis3::Z,
Dimension::new(
Group::new("b")
.with("b1")
.with("b2")
.with("b3")
.with("b4")
.with_label_shown(),
),
);
let c = (
Axis3::X,
Dimension::new(
Group::new("c")
.with("c1")
.with("c2")
.with("c3")
.with("c4")
.with("c5"),
),
);
let mut pt = PivotTable::new([a, b, c])
.with_title("Column x b1 x a1")
.with_look(Arc::new(test_look()));
let mut i = 0;
for c in 0..5 {
for b in 0..4 {
for a in 0..3 {
pt.insert(&[a, b, c], Value::new_integer(Some(i as f64)));
i += 1;
}
}
}
assert_rendering("d3-layer0_0", &pt);
let pt = pt.with_layer(&[0, 1]).with_title("Column x b2 x a1");
assert_rendering("d3-layer0_1", &pt);
let pt = pt.with_layer(&[1, 2]).with_title("Column x b3 x a2");
assert_rendering("d3-layer1_2", &pt);
}
#[test]
fn title_and_caption() {
let pivot_table =
d2("Title", [Axis3::X, Axis3::Y], None).with_caption(Value::new_text("Caption"));
assert_rendering("title_and_caption", &pivot_table);
let pivot_table = pivot_table.with_show_title(false);
assert_rendering("caption", &pivot_table);
let pivot_table = pivot_table.with_show_caption(false);
assert_rendering("no_title_or_caption", &pivot_table);
}
fn template_formats(format: Format) -> PivotTable {
let value = Value::new_number(Some(0.5)).with_format(format);
let value_in_template = Value::new(ValueInner::Template(TemplateValue {
localized: "No leading zero inside template: ^1".into(),
args: vec![vec![value.clone()]],
id: Some("id".into()),
}));
d2("Title", [Axis3::X, Axis3::Y], None)
.with_title(value)
.with_caption(value_in_template)
}
#[test]
fn template_formats_dollar() {
assert_rendering("template_formats_dollar", &template_formats(DOLLAR40_2));
}
#[test]
fn template_formats_pct() {
assert_rendering("template_formats_pct", &template_formats(PCT40_1));
}
#[test]
fn template_formats_f() {
assert_rendering("template_formats_f", &template_formats(F40_2));
}
fn footnote_table(show_f0: bool) -> PivotTable {
let mut footnotes = Footnotes::new();
let f0 = footnotes.push(
Footnote::new("First footnote")
.with_some_marker("*")
.with_show(show_f0),
);
let f1 = footnotes.push(Footnote::new("Second footnote"));
let a = (
Axis3::X,
Dimension::new(
Group::new(Value::new_text("A").with_footnote(&f0))
.with_label_shown()
.with(Value::new_text("B").with_footnote(&f1))
.with(Value::new_text("C").with_footnote(&f0).with_footnote(&f1)),
),
);
let d = (
Axis3::Y,
Dimension::new(
Group::new(Value::new_text("D").with_footnote(&f1))
.with_label_shown()
.with(Value::new_text("E").with_footnote(&f0))
.with(Value::new_text("F").with_footnote(&f1).with_footnote(&f0)),
),
);
let look = test_look().with_row_label_position(LabelPosition::Nested);
let mut pt = PivotTable::new([a, d]).with_title(
Value::new_text("Pivot Table with Alphabetic Subscript Footnotes").with_footnote(&f0),
);
pt.insert(&[0, 0], Value::new_number(Some(0.0)));
pt.insert(&[1, 0], Value::new_number(Some(1.0)).with_footnote(&f0));
pt.insert(&[0, 1], Value::new_number(Some(2.0)).with_footnote(&f1));
pt.insert(
&[1, 1],
Value::new_number(Some(3.0))
.with_footnote(&f0)
.with_footnote(&f1),
);
pt.with_look(Arc::new(look))
.with_footnotes(footnotes)
.with_caption(Value::new_text("Caption").with_footnote(&f0))
.with_corner_text(
Value::new_text("Corner")
.with_footnote(&f0)
.with_footnote(&f1),
)
}
#[test]
fn footnote_alphabetic_subscript() {
assert_rendering("footnote_alphabetic_subscript", &footnote_table(true));
}
#[test]
fn footnote_alphabetic_superscript() {
let mut pt = footnote_table(true);
let f0 = pt.footnotes.0[0].clone();
pt = pt.with_title(
Value::new_text("Pivot Table with Alphabetic Superscript Footnotes").with_footnote(&f0),
);
pt.look_mut().footnote_marker_position = FootnoteMarkerPosition::Superscript;
assert_rendering("footnote_alphabetic_superscript", &pt);
}
#[test]
fn footnote_numeric_subscript() {
let mut pt = footnote_table(true);
let f0 = pt.footnotes.0[0].clone();
pt = pt.with_title(
Value::new_text("Pivot Table with Numeric Subscript Footnotes").with_footnote(&f0),
);
pt.look_mut().footnote_marker_type = FootnoteMarkerType::Numeric;
assert_rendering("footnote_numeric_subscript", &pt);
}
#[test]
fn footnote_numeric_superscript() {
let mut pt = footnote_table(true);
let f0 = pt.footnotes.0[0].clone();
pt = pt.with_title(
Value::new_text("Pivot Table with Numeric Superscript Footnotes").with_footnote(&f0),
);
pt.look_mut().footnote_marker_type = FootnoteMarkerType::Numeric;
pt.look_mut().footnote_marker_position = FootnoteMarkerPosition::Superscript;
assert_rendering("footnote_numeric_superscript", &pt);
}
#[test]
fn footnote_hidden() {
assert_rendering("footnote_hidden", &footnote_table(false));
}
#[test]
fn no_dimension() {
let pivot_table = PivotTable::new([])
.with_title("No Dimensions")
.with_look(Arc::new(test_look()));
assert_rendering("no_dimension", &pivot_table);
}
#[test]
fn empty_dimensions() {
let look = Arc::new(test_look().with_omit_empty(false));
let d1 = (Axis3::X, Dimension::new(Group::new("a")));
let pivot_table = PivotTable::new([d1])
.with_title("One Empty Dimension")
.with_look(look.clone());
assert_rendering("one_empty_dimension", &pivot_table);
let d1 = (Axis3::X, Dimension::new(Group::new("a")));
let d2 = (Axis3::X, Dimension::new(Group::new("b").with_label_shown()));
let pivot_table = PivotTable::new([d1, d2])
.with_title("Two Empty Dimensions")
.with_look(look.clone());
assert_rendering("two_empty_dimensions", &pivot_table);
let d1 = (Axis3::X, Dimension::new(Group::new("a")));
let d2 = (Axis3::X, Dimension::new(Group::new("b").with_label_shown()));
let d3 = (
Axis3::X,
Dimension::new(Group::new("c").with("c1").with("c2")),
);
let pivot_table = PivotTable::new([d1, d2, d3])
.with_title("Three Dimensions, Two Empty")
.with_look(look.clone());
assert_rendering("three_dimensions_two_empty", &pivot_table);
}
#[test]
fn empty_groups() {
let d1 = (
Axis3::X,
Dimension::new(Group::new("a").with("a1").with(Group::new("a2")).with("a3")),
);
let d2 = (
Axis3::Y,
Dimension::new(Group::new("b").with(Group::new("b1")).with("b2").with("b3")),
);
let mut pt = PivotTable::new([d1, d2]).with_title("Empty Groups");
let mut i = 0;
for b in 0..2 {
for a in 0..2 {
pt.insert(&[a, b], Value::new_integer(Some(i as f64)));
i += 1;
}
}
let pivot_table = pt.with_look(Arc::new(test_look().with_omit_empty(false)));
assert_rendering("empty_groups", &pivot_table);
}
fn d4(
title: &str,
borders: EnumMap<Border, BorderStyle>,
show_dimension_labels: bool,
) -> PivotTable {
let a = (
Axis3::X,
Dimension::new(
Group::new("a")
.with_show_label(show_dimension_labels)
.with("a1")
.with(Group::new("ag1").with("a2").with("a3")),
),
);
let b = (
Axis3::X,
Dimension::new(
Group::new("b")
.with_show_label(show_dimension_labels)
.with(Group::new("bg1").with("b1").with("b2"))
.with("b3"),
),
);
let c = (
Axis3::Y,
Dimension::new(
Group::new("c")
.with_show_label(show_dimension_labels)
.with("c1")
.with(Group::new("cg1").with("c2").with("c3")),
),
);
let d = (
Axis3::Y,
Dimension::new(
Group::new("d")
.with_show_label(show_dimension_labels)
.with(Group::new("dg1").with("d1").with("d2"))
.with("d3"),
),
);
let mut pivot_table = PivotTable::new([a, b, c, d])
.with_title(title)
.with_look(Arc::new(test_look().with_borders(borders)));
let mut i = 0;
for d in 0..3 {
for c in 0..3 {
for b in 0..3 {
for a in 0..3 {
pivot_table.insert(&[a, b, c, d], Value::new_integer(Some(i as f64)));
i += 1;
}
}
}
}
pivot_table
}
#[test]
fn dimension_borders_1() {
let pivot_table = d4(
"Dimension Borders 1",
EnumMap::from_fn(|border| match border {
Border::Dimension(RowColBorder(HeadingRegion::Rows, Axis2::X))
| Border::Dimension(RowColBorder(HeadingRegion::Columns, Axis2::Y)) => SOLID_BLUE,
_ => BorderStyle::none(),
}),
true,
);
assert_rendering("dimension_borders_1", &pivot_table);
}
#[test]
fn dimension_borders_2() {
let pivot_table = d4(
"Dimension Borders 2",
EnumMap::from_fn(|border| match border {
Border::Dimension(RowColBorder(HeadingRegion::Rows, Axis2::Y))
| Border::Dimension(RowColBorder(HeadingRegion::Columns, Axis2::X)) => SOLID_BLUE,
_ => BorderStyle::none(),
}),
true,
);
assert_rendering("dimension_borders_2", &pivot_table);
}
#[test]
fn category_borders_1() {
let pivot_table = d4(
"Category Borders 1",
EnumMap::from_fn(|border| match border {
Border::Category(RowColBorder(HeadingRegion::Rows, Axis2::X))
| Border::Category(RowColBorder(HeadingRegion::Columns, Axis2::Y)) => DASHED_RED,
_ => BorderStyle::none(),
}),
true,
);
assert_rendering("category_borders_1", &pivot_table);
}
#[test]
fn category_borders_2() {
let pivot_table = d4(
"Category Borders 2",
EnumMap::from_fn(|border| match border {
Border::Category(RowColBorder(HeadingRegion::Rows, Axis2::Y))
| Border::Category(RowColBorder(HeadingRegion::Columns, Axis2::X)) => DASHED_RED,
_ => BorderStyle::none(),
}),
true,
);
assert_rendering("category_borders_2", &pivot_table);
}
#[test]
fn category_and_dimension_borders_1() {
let pivot_table = d4(
"Category and Dimension Borders 1",
EnumMap::from_fn(|border| match border {
Border::Dimension(RowColBorder(HeadingRegion::Rows, Axis2::X))
| Border::Dimension(RowColBorder(HeadingRegion::Columns, Axis2::Y)) => SOLID_BLUE,
Border::Category(RowColBorder(HeadingRegion::Rows, Axis2::X))
| Border::Category(RowColBorder(HeadingRegion::Columns, Axis2::Y)) => DASHED_RED,
_ => BorderStyle::none(),
}),
true,
);
assert_rendering("category_and_dimension_borders_1", &pivot_table);
}
#[test]
fn category_and_dimension_borders_2() {
let pivot_table = d4(
"Category and Dimension Borders 2",
EnumMap::from_fn(|border| match border {
Border::Dimension(RowColBorder(HeadingRegion::Rows, Axis2::Y))
| Border::Dimension(RowColBorder(HeadingRegion::Columns, Axis2::X)) => SOLID_BLUE,
Border::Category(RowColBorder(HeadingRegion::Rows, Axis2::Y))
| Border::Category(RowColBorder(HeadingRegion::Columns, Axis2::X)) => DASHED_RED,
_ => BorderStyle::none(),
}),
true,
);
assert_rendering("category_and_dimension_borders_2", &pivot_table);
}
const SOLID_BLUE: BorderStyle = BorderStyle {
stroke: Stroke::Solid,
color: Color::BLUE,
};
const DASHED_RED: BorderStyle = BorderStyle {
stroke: Stroke::Dashed,
color: Color::RED,
};
#[test]
fn category_and_dimension_borders_3() {
let pivot_table = d4(
"Category and Dimension Borders 3",
EnumMap::from_fn(|border| match border {
Border::Dimension(_) => SOLID_BLUE,
Border::Category(_) => DASHED_RED,
_ => BorderStyle::none(),
}),
false,
);
assert_rendering("category_and_dimension_borders_3", &pivot_table);
}
#[test]
fn small_numbers() {
let exponent = (
Axis3::Y,
Dimension::new(
Group::new("exponent")
.with("0")
.with("-1")
.with("-2")
.with("-3")
.with("-4")
.with("-5")
.with("-6")
.with("-7")
.with("-8")
.with("-9")
.with_label_shown(),
),
);
let sign = (
Axis3::X,
Dimension::new(
Group::new("sign")
.with("positive")
.with("negative")
.with_label_shown(),
),
);
let rc = (
Axis3::X,
Dimension::new(
Group::new("result class")
.with("general")
.with("specific")
.with_label_shown(),
),
);
let mut pt = PivotTable::new([exponent, sign, rc]).with_title("small numbers");
pt.insert_number(&[0, 0, 0], Some(1.0), Class::Other);
pt.insert_number(&[1, 0, 0], Some(0.1), Class::Other);
pt.insert_number(&[2, 0, 0], Some(0.01), Class::Other);
pt.insert_number(&[3, 0, 0], Some(0.001), Class::Other);
pt.insert_number(&[4, 0, 0], Some(0.0001), Class::Other);
pt.insert_number(&[5, 0, 0], Some(0.00001), Class::Other);
pt.insert_number(&[6, 0, 0], Some(0.000001), Class::Other);
pt.insert_number(&[7, 0, 0], Some(0.0000001), Class::Other);
pt.insert_number(&[8, 0, 0], Some(0.00000001), Class::Other);
pt.insert_number(&[9, 0, 0], Some(0.000000001), Class::Other);
pt.insert_number(&[0, 0, 1], Some(-1.0), Class::Residual);
pt.insert_number(&[1, 0, 1], Some(-0.1), Class::Residual);
pt.insert_number(&[2, 0, 1], Some(-0.01), Class::Residual);
pt.insert_number(&[3, 0, 1], Some(-0.001), Class::Residual);
pt.insert_number(&[4, 0, 1], Some(-0.0001), Class::Residual);
pt.insert_number(&[5, 0, 1], Some(-0.00001), Class::Residual);
pt.insert_number(&[6, 0, 1], Some(-0.000001), Class::Residual);
pt.insert_number(&[7, 0, 1], Some(-0.0000001), Class::Residual);
pt.insert_number(&[8, 0, 1], Some(-0.00000001), Class::Residual);
pt.insert_number(&[9, 0, 1], Some(-0.000000001), Class::Residual);
pt.insert_number(&[0, 1, 0], Some(1.0), Class::Other);
pt.insert_number(&[1, 1, 0], Some(0.1), Class::Other);
pt.insert_number(&[2, 1, 0], Some(0.01), Class::Other);
pt.insert_number(&[3, 1, 0], Some(0.001), Class::Other);
pt.insert_number(&[4, 1, 0], Some(0.0001), Class::Other);
pt.insert_number(&[5, 1, 0], Some(0.00001), Class::Other);
pt.insert_number(&[6, 1, 0], Some(0.000001), Class::Other);
pt.insert_number(&[7, 1, 0], Some(0.0000001), Class::Other);
pt.insert_number(&[8, 1, 0], Some(0.00000001), Class::Other);
pt.insert_number(&[9, 1, 0], Some(0.000000001), Class::Other);
pt.insert_number(&[0, 1, 1], Some(-1.0), Class::Residual);
pt.insert_number(&[1, 1, 1], Some(-0.1), Class::Residual);
pt.insert_number(&[2, 1, 1], Some(-0.01), Class::Residual);
pt.insert_number(&[3, 1, 1], Some(-0.001), Class::Residual);
pt.insert_number(&[4, 1, 1], Some(-0.0001), Class::Residual);
pt.insert_number(&[5, 1, 1], Some(-0.00001), Class::Residual);
pt.insert_number(&[6, 1, 1], Some(-0.000001), Class::Residual);
pt.insert_number(&[7, 1, 1], Some(-0.0000001), Class::Residual);
pt.insert_number(&[8, 1, 1], Some(-0.00000001), Class::Residual);
pt.insert_number(&[9, 1, 1], Some(-0.000000001), Class::Residual);
let pivot_table = pt.with_look(Arc::new(test_look()));
assert_rendering("small_numbers", &pivot_table);
}