Squashed 'vendor/ruvector/' content from commit b64c2172

git-subtree-dir: vendor/ruvector
git-subtree-split: b64c21726f2bb37286d9ee36a7869fef60cc6900
This commit is contained in:
ruv
2026-02-28 14:39:40 -05:00
commit d803bfe2b1
7854 changed files with 3522914 additions and 0 deletions

View File

@@ -0,0 +1,645 @@
//! Comprehensive integration tests for mathematical expression parsing and conversion
//!
//! These tests cover complex mathematical expressions including:
//! - Fractions and nested fractions
//! - Radicals (square roots, nth roots)
//! - Powers and exponents
//! - Matrices and vectors
//! - Integrals and summations
//! - Trigonometric functions
//! - Greek letters and special symbols
//! - Complex nested expressions
//!
//! NOTE: These tests require the `math` feature to be enabled.
//! Run with: cargo test --features math
#![cfg(feature = "math")]
use ruvector_scipix::math::{
parse_expression, to_asciimath, to_latex, to_mathml, AsciiMathGenerator, BinaryOp, BracketType,
LaTeXConfig, LaTeXGenerator, LargeOpType, MathExpr, MathNode,
};
#[test]
fn test_quadratic_formula() {
// The famous quadratic formula: x = (-b ± √(b² - 4ac)) / 2a
let latex_input = r"\frac{-b + \sqrt{b^2 - 4*a*c}}{2*a}";
let expr = parse_expression(latex_input).unwrap();
let latex = to_latex(&expr);
assert!(latex.contains(r"\frac"));
assert!(latex.contains(r"\sqrt"));
assert!(latex.contains("b"));
assert!(latex.contains("a"));
assert!(latex.contains("c"));
let mathml = to_mathml(&expr);
assert!(mathml.contains("<mfrac>"));
assert!(mathml.contains("<msqrt>"));
let asciimath = to_asciimath(&expr);
assert!(asciimath.contains("sqrt"));
assert!(asciimath.contains("/"));
}
#[test]
fn test_pythagorean_theorem() {
// a² + b² = c²
let input = "a^2 + b^2 = c^2";
let expr = parse_expression(input).unwrap();
let latex = to_latex(&expr);
assert!(latex.contains("a^{2}"));
assert!(latex.contains("b^{2}"));
assert!(latex.contains("c^{2}"));
assert!(latex.contains("="));
let mathml = to_mathml(&expr);
assert!(mathml.contains("<msup>"));
assert!(mathml.contains("<mo>=</mo>"));
}
#[test]
fn test_euler_identity() {
// e^(iπ) + 1 = 0
let input = "e^{i*\\pi} + 1 = 0";
let expr = parse_expression(input).unwrap();
let latex = to_latex(&expr);
assert!(latex.contains("e^"));
assert!(latex.contains("\\pi"));
}
#[test]
fn test_nested_fractions() {
// Complex continued fraction
let input = r"\frac{1}{\frac{2}{\frac{3}{4}}}";
let expr = parse_expression(input).unwrap();
let latex = to_latex(&expr);
// Should contain multiple \frac commands
assert!(latex.matches(r"\frac").count() >= 3);
let mathml = to_mathml(&expr);
assert!(mathml.matches("<mfrac>").count() >= 3);
}
#[test]
fn test_matrix_2x2() {
// 2x2 matrix
let expr = MathExpr::new(
MathNode::Matrix {
rows: vec![
vec![
MathNode::Number {
value: "1".to_string(),
is_decimal: false,
},
MathNode::Number {
value: "2".to_string(),
is_decimal: false,
},
],
vec![
MathNode::Number {
value: "3".to_string(),
is_decimal: false,
},
MathNode::Number {
value: "4".to_string(),
is_decimal: false,
},
],
],
bracket_type: BracketType::Brackets,
},
1.0,
);
let latex = to_latex(&expr);
assert!(latex.contains(r"\begin{bmatrix}"));
assert!(latex.contains(r"\end{bmatrix}"));
assert!(latex.contains("&"));
assert!(latex.contains(r"\\"));
let mathml = to_mathml(&expr);
assert!(mathml.contains("<mtable>"));
assert!(mathml.contains("<mtr>"));
assert!(mathml.contains("<mtd>"));
let asciimath = to_asciimath(&expr);
assert!(asciimath.contains("["));
assert!(asciimath.contains(";"));
}
#[test]
fn test_matrix_3x3() {
// 3x3 identity matrix
let expr = MathExpr::new(
MathNode::Matrix {
rows: vec![
vec![
MathNode::Number {
value: "1".to_string(),
is_decimal: false,
},
MathNode::Number {
value: "0".to_string(),
is_decimal: false,
},
MathNode::Number {
value: "0".to_string(),
is_decimal: false,
},
],
vec![
MathNode::Number {
value: "0".to_string(),
is_decimal: false,
},
MathNode::Number {
value: "1".to_string(),
is_decimal: false,
},
MathNode::Number {
value: "0".to_string(),
is_decimal: false,
},
],
vec![
MathNode::Number {
value: "0".to_string(),
is_decimal: false,
},
MathNode::Number {
value: "0".to_string(),
is_decimal: false,
},
MathNode::Number {
value: "1".to_string(),
is_decimal: false,
},
],
],
bracket_type: BracketType::Parentheses,
},
1.0,
);
let latex = to_latex(&expr);
assert!(latex.contains(r"\begin{pmatrix}"));
assert!(latex.matches(r"\\").count() >= 2);
}
#[test]
fn test_definite_integral() {
// ∫₀¹ x² dx
let expr = MathExpr::new(
MathNode::LargeOp {
op_type: LargeOpType::Integral,
lower: Some(Box::new(MathNode::Number {
value: "0".to_string(),
is_decimal: false,
})),
upper: Some(Box::new(MathNode::Number {
value: "1".to_string(),
is_decimal: false,
})),
content: Box::new(MathNode::Binary {
op: BinaryOp::Power,
left: Box::new(MathNode::Symbol {
value: "x".to_string(),
unicode: Some('x'),
}),
right: Box::new(MathNode::Number {
value: "2".to_string(),
is_decimal: false,
}),
}),
},
1.0,
);
let latex = to_latex(&expr);
assert!(latex.contains(r"\int"));
assert!(latex.contains("_{0}"));
assert!(latex.contains("^{1}"));
let mathml = to_mathml(&expr);
assert!(mathml.contains("<munderover>"));
assert!(mathml.contains(""));
}
#[test]
fn test_summation() {
// ∑_{i=1}^{n} i²
let expr = MathExpr::new(
MathNode::LargeOp {
op_type: LargeOpType::Sum,
lower: Some(Box::new(MathNode::Binary {
op: BinaryOp::Equal,
left: Box::new(MathNode::Symbol {
value: "i".to_string(),
unicode: Some('i'),
}),
right: Box::new(MathNode::Number {
value: "1".to_string(),
is_decimal: false,
}),
})),
upper: Some(Box::new(MathNode::Symbol {
value: "n".to_string(),
unicode: Some('n'),
})),
content: Box::new(MathNode::Binary {
op: BinaryOp::Power,
left: Box::new(MathNode::Symbol {
value: "i".to_string(),
unicode: Some('i'),
}),
right: Box::new(MathNode::Number {
value: "2".to_string(),
is_decimal: false,
}),
}),
},
1.0,
);
let latex = to_latex(&expr);
assert!(latex.contains(r"\sum"));
assert!(latex.contains("_{i"));
assert!(latex.contains("^{n}"));
let mathml = to_mathml(&expr);
assert!(mathml.contains(""));
assert!(mathml.contains("<munderover>"));
}
#[test]
fn test_product_notation() {
// ∏_{k=1}^{n} k
let expr = MathExpr::new(
MathNode::LargeOp {
op_type: LargeOpType::Product,
lower: Some(Box::new(MathNode::Binary {
op: BinaryOp::Equal,
left: Box::new(MathNode::Symbol {
value: "k".to_string(),
unicode: Some('k'),
}),
right: Box::new(MathNode::Number {
value: "1".to_string(),
is_decimal: false,
}),
})),
upper: Some(Box::new(MathNode::Symbol {
value: "n".to_string(),
unicode: Some('n'),
})),
content: Box::new(MathNode::Symbol {
value: "k".to_string(),
unicode: Some('k'),
}),
},
1.0,
);
let latex = to_latex(&expr);
assert!(latex.contains(r"\prod"));
let mathml = to_mathml(&expr);
assert!(mathml.contains(""));
}
#[test]
fn test_nth_root() {
// ∛8 (cube root)
let input = r"\sqrt[3]{8}";
let expr = parse_expression(input).unwrap();
let latex = to_latex(&expr);
assert!(latex.contains(r"\sqrt[3]"));
let mathml = to_mathml(&expr);
assert!(mathml.contains("<mroot>"));
}
#[test]
fn test_complex_radical() {
// √(a² + b²)
let input = r"\sqrt{a^2 + b^2}";
let expr = parse_expression(input).unwrap();
let latex = to_latex(&expr);
assert!(latex.contains(r"\sqrt"));
assert!(latex.contains("a^{2}"));
assert!(latex.contains("b^{2}"));
}
#[test]
fn test_binomial_coefficient() {
// (n choose k) represented as fraction
let input = r"\frac{n}{k}";
let expr = parse_expression(input).unwrap();
let latex = to_latex(&expr);
assert!(latex.contains(r"\frac{n}{k}"));
let mathml = to_mathml(&expr);
assert!(mathml.contains("<mfrac>"));
assert!(mathml.contains("<mi>n</mi>"));
assert!(mathml.contains("<mi>k</mi>"));
}
#[test]
fn test_trigonometric_functions() {
// sin²(x) + cos²(x) = 1
let input = r"\sin{x}^2 + \cos{x}^2 = 1";
let expr = parse_expression(input).unwrap();
let latex = to_latex(&expr);
assert!(latex.contains(r"\sin"));
assert!(latex.contains(r"\cos"));
}
#[test]
fn test_limits() {
// lim_{x→∞} (1 + 1/x)^x = e
// Simplified: testing basic limit structure
let input = r"\sum_{x=1}^{10} x";
let expr = parse_expression(input).unwrap();
let latex = to_latex(&expr);
assert!(latex.contains(r"\sum"));
}
#[test]
fn test_greek_letters() {
// α + β + γ = δ
let input = r"\alpha + \beta + \gamma = \delta";
let expr = parse_expression(input).unwrap();
let latex = to_latex(&expr);
assert!(latex.contains(r"\alpha"));
assert!(latex.contains(r"\beta"));
assert!(latex.contains(r"\gamma"));
assert!(latex.contains(r"\delta"));
}
#[test]
fn test_subscript_and_superscript() {
// a₁² + a₂² = a₃²
let input = "a_1^2 + a_2^2 = a_3^2";
let expr = parse_expression(input).unwrap();
let latex = to_latex(&expr);
assert!(latex.contains("a_{1}^{2}"));
assert!(latex.contains("a_{2}^{2}"));
assert!(latex.contains("a_{3}^{2}"));
let mathml = to_mathml(&expr);
assert!(mathml.contains("<msubsup>"));
}
#[test]
fn test_operator_precedence() {
// 1 + 2 * 3 should parse as 1 + (2 * 3)
let input = "1 + 2 * 3";
let expr = parse_expression(input).unwrap();
match expr.root {
MathNode::Binary {
op: BinaryOp::Add,
right,
..
} => {
assert!(matches!(
*right,
MathNode::Binary {
op: BinaryOp::Multiply,
..
}
));
}
_ => panic!("Expected addition with multiplication on right"),
}
let latex = to_latex(&expr);
// Should not have unnecessary parentheses around 2 * 3
assert!(!latex.contains("(2"));
}
#[test]
fn test_parentheses_grouping() {
// (1 + 2) * 3 should parse as (1 + 2) * 3
let input = "(1 + 2) * 3";
let expr = parse_expression(input).unwrap();
let latex = to_latex(&expr);
// The addition should be grouped
assert!(latex.contains("1 + 2"));
}
#[test]
fn test_complex_nested_expression() {
// Complex expression with multiple levels of nesting
let input = r"\frac{\sqrt{a + b}}{c^2 - d^2}";
let expr = parse_expression(input).unwrap();
let latex = to_latex(&expr);
assert!(latex.contains(r"\frac"));
assert!(latex.contains(r"\sqrt"));
assert!(latex.contains("c^{2}"));
assert!(latex.contains("d^{2}"));
let mathml = to_mathml(&expr);
assert!(mathml.contains("<mfrac>"));
assert!(mathml.contains("<msqrt>"));
assert!(mathml.contains("<msup>"));
}
#[test]
fn test_latex_config_display_style() {
let expr = parse_expression(r"\frac{1}{2}").unwrap();
let config_inline = LaTeXConfig {
display_style: false,
auto_size_delimiters: true,
spacing: true,
};
let config_display = LaTeXConfig {
display_style: true,
auto_size_delimiters: true,
spacing: true,
};
let gen_inline = LaTeXGenerator::with_config(config_inline);
let gen_display = LaTeXGenerator::with_config(config_display);
let latex_inline = gen_inline.generate(&expr);
let latex_display = gen_display.generate(&expr);
// Both should contain \frac
assert!(latex_inline.contains(r"\frac"));
assert!(latex_display.contains(r"\frac"));
}
#[test]
fn test_latex_config_no_auto_size() {
let expr = MathExpr::new(
MathNode::Group {
content: Box::new(MathNode::Number {
value: "42".to_string(),
is_decimal: false,
}),
bracket_type: BracketType::Parentheses,
},
1.0,
);
let config_auto = LaTeXConfig {
display_style: false,
auto_size_delimiters: true,
spacing: true,
};
let config_no_auto = LaTeXConfig {
display_style: false,
auto_size_delimiters: false,
spacing: true,
};
let gen_auto = LaTeXGenerator::with_config(config_auto);
let gen_no_auto = LaTeXGenerator::with_config(config_no_auto);
let latex_auto = gen_auto.generate(&expr);
let latex_no_auto = gen_no_auto.generate(&expr);
assert!(latex_auto.contains(r"\left("));
assert!(!latex_no_auto.contains(r"\left("));
}
#[test]
fn test_asciimath_unicode_vs_ascii() {
let expr = parse_expression("2 * 3").unwrap();
let gen_unicode = AsciiMathGenerator::new();
let gen_ascii = AsciiMathGenerator::ascii_only();
let unicode_output = gen_unicode.generate(&expr);
let ascii_output = gen_ascii.generate(&expr);
assert!(unicode_output.contains("×") || unicode_output.contains("*"));
assert!(ascii_output.contains("*"));
}
#[test]
fn test_double_integral() {
let expr = MathExpr::new(
MathNode::LargeOp {
op_type: LargeOpType::DoubleIntegral,
lower: None,
upper: None,
content: Box::new(MathNode::Symbol {
value: "f".to_string(),
unicode: Some('f'),
}),
},
1.0,
);
let latex = to_latex(&expr);
assert!(latex.contains(r"\iint"));
let mathml = to_mathml(&expr);
assert!(mathml.contains(""));
}
#[test]
fn test_triple_integral() {
let expr = MathExpr::new(
MathNode::LargeOp {
op_type: LargeOpType::TripleIntegral,
lower: None,
upper: None,
content: Box::new(MathNode::Symbol {
value: "f".to_string(),
unicode: Some('f'),
}),
},
1.0,
);
let latex = to_latex(&expr);
assert!(latex.contains(r"\iiint"));
let mathml = to_mathml(&expr);
assert!(mathml.contains(""));
}
#[test]
fn test_decimal_numbers() {
let input = "3.14159";
let expr = parse_expression(input).unwrap();
match expr.root {
MathNode::Number { value, is_decimal } => {
assert_eq!(value, "3.14159");
assert!(is_decimal);
}
_ => panic!("Expected decimal number"),
}
let latex = to_latex(&expr);
assert!(latex.contains("3.14159"));
}
#[test]
fn test_large_expression() {
// Test a realistically complex expression
let input = r"\frac{-b \pm \sqrt{b^2 - 4ac}}{2a}";
let expr = parse_expression(input).unwrap();
let latex = to_latex(&expr);
assert!(latex.contains(r"\frac"));
assert!(latex.contains(r"\sqrt"));
assert!(latex.contains("b^{2}"));
let mathml = to_mathml(&expr);
assert!(mathml.contains("<mfrac>"));
assert!(mathml.contains("<msqrt>"));
let asciimath = to_asciimath(&expr);
assert!(asciimath.contains("sqrt"));
assert!(asciimath.contains("/"));
}
#[test]
fn test_all_bracket_types() {
for bracket_type in &[
BracketType::Parentheses,
BracketType::Brackets,
BracketType::Braces,
BracketType::Vertical,
] {
let expr = MathExpr::new(
MathNode::Group {
content: Box::new(MathNode::Symbol {
value: "x".to_string(),
unicode: Some('x'),
}),
bracket_type: *bracket_type,
},
1.0,
);
let latex = to_latex(&expr);
let mathml = to_mathml(&expr);
// All should produce valid output
assert!(!latex.is_empty());
assert!(!mathml.is_empty());
}
}