use crate::formula::ast::MathNode;
use memchr::memchr;
#[inline]
#[allow(dead_code)]
pub fn is_valid_number_fast(s: &str) -> bool {
if s.is_empty() {
return false;
}
let bytes = s.as_bytes();
let mut has_digit = false;
let mut has_dot = false;
for &b in bytes {
match b {
b'0'..=b'9' => has_digit = true,
b'.' => {
if has_dot {
return false; }
has_dot = true;
}
b'-' if bytes.len() == 1 => return false, b'-' => {} _ => return false, }
}
has_digit }
#[inline]
pub fn contains_latex_special_simd(text: &str) -> bool {
let bytes = text.as_bytes();
memchr(b' ', bytes).is_some()
|| memchr(b'#', bytes).is_some()
|| memchr(b'$', bytes).is_some()
|| memchr(b'%', bytes).is_some()
|| memchr(b'&', bytes).is_some()
|| memchr(b'_', bytes).is_some()
|| memchr(b'{', bytes).is_some()
|| memchr(b'}', bytes).is_some()
|| memchr(b'~', bytes).is_some()
|| memchr(b'^', bytes).is_some()
|| memchr(b'\\', bytes).is_some()
}
#[inline]
#[allow(dead_code)]
pub fn escape_latex_special_chars(text: &str, buffer: &mut String) -> bool {
if !contains_latex_special_simd(text) {
buffer.push_str(text);
return false;
}
for ch in text.chars() {
match ch {
' ' | '#' | '$' | '%' | '&' | '_' | '{' | '}' | '~' | '^' | '\\' => {
buffer.push('\\');
buffer.push(ch);
}
_ => buffer.push(ch),
}
}
true
}
#[inline]
pub fn extend_buffer_with_capacity(buffer: &mut String, text: &str, additional_capacity: usize) {
if buffer.capacity() < buffer.len() + text.len() + additional_capacity {
buffer.reserve(text.len() + additional_capacity);
}
buffer.push_str(text);
}
#[inline]
pub fn needs_latex_protection(text: &str) -> bool {
if text.is_empty() {
return true;
}
if memchr(b' ', text.as_bytes()).is_some() {
return true;
}
contains_latex_special_simd(text)
}
#[inline]
#[allow(dead_code)]
pub fn needs_grouping_for_scripts(nodes: &[MathNode]) -> bool {
nodes.len() > 1
}
pub fn estimate_formula_size(nodes: &[MathNode]) -> usize {
estimate_nodes_size(nodes) + 10 }
pub fn estimate_nodes_size(nodes: &[MathNode]) -> usize {
nodes.iter().map(estimate_node_size).sum()
}
pub fn estimate_node_size(node: &MathNode) -> usize {
match node {
MathNode::Text(text) => {
if needs_latex_protection(text) {
text.len() + 10 } else {
text.len()
}
}
MathNode::Number(num) => num.len(),
MathNode::Operator(_) => 5, MathNode::Symbol(_) => 8, MathNode::Frac { numerator, denominator, .. } => {
6 + estimate_nodes_size(numerator) + estimate_nodes_size(denominator) }
MathNode::Root { base, index } => {
(if index.is_some() { 8 } else { 7 }) + estimate_nodes_size(base)
}
MathNode::Power { base, exponent } => {
2 + estimate_nodes_size(base) + estimate_nodes_size(exponent) }
MathNode::Sub { base, subscript } => {
2 + estimate_nodes_size(base) + estimate_nodes_size(subscript) }
MathNode::SubSup { base, subscript, superscript } => {
4 + estimate_nodes_size(base) + estimate_nodes_size(subscript) + estimate_nodes_size(superscript) }
MathNode::Under { base, under, position: _ } => {
10 + estimate_nodes_size(base) + estimate_nodes_size(under) }
MathNode::Over { base, over, position: _ } => {
9 + estimate_nodes_size(base) + estimate_nodes_size(over) }
MathNode::UnderOver { base, under, over, position: _ } => {
20 + estimate_nodes_size(base) + estimate_nodes_size(under) + estimate_nodes_size(over) }
MathNode::Fenced { open: _, content, close: _, separator: _ } => {
12 + estimate_nodes_size(content) }
MathNode::LargeOp { operator: _, lower_limit, upper_limit, integrand, hide_lower: _, hide_upper: _ } => {
8 + lower_limit.as_ref().map_or(0, |l| 2 + estimate_nodes_size(l)) +
upper_limit.as_ref().map_or(0, |u| 2 + estimate_nodes_size(u)) +
integrand.as_ref().map_or(0, |i| 1 + estimate_nodes_size(i))
}
MathNode::Function { name, argument } => {
name.len() + 5 + estimate_nodes_size(argument) }
MathNode::Matrix { rows, .. } => {
20 + rows.len() * 4 + rows.iter().flatten().flatten().count() * 3 }
MathNode::Accent { base, .. } => {
8 + estimate_nodes_size(base) }
MathNode::Space(_) => 5, MathNode::LineBreak => 2, MathNode::Style { content, .. } => {
8 + estimate_nodes_size(content) }
MathNode::Row(nodes) => estimate_nodes_size(nodes),
MathNode::Phantom(content) => {
9 + estimate_nodes_size(content) }
MathNode::Error(msg) => {
15 + msg.len() }
MathNode::PredefinedSymbol(_) => 8, MathNode::PreSub { base, pre_subscript } => {
3 + estimate_nodes_size(base) + estimate_nodes_size(pre_subscript) }
MathNode::PreSup { base, pre_superscript } => {
3 + estimate_nodes_size(base) + estimate_nodes_size(pre_superscript) }
MathNode::PreSubSup { base, pre_subscript, pre_superscript } => {
5 + estimate_nodes_size(base) + estimate_nodes_size(pre_subscript) + estimate_nodes_size(pre_superscript) }
MathNode::Bar { base, .. } => {
6 + estimate_nodes_size(base) }
MathNode::BorderBox { content, .. } => {
12 + estimate_nodes_size(content) }
MathNode::GroupChar { base, .. } => {
12 + estimate_nodes_size(base) }
MathNode::PredefinedFunction { argument, .. } => {
8 + estimate_nodes_size(argument) }
MathNode::EqArray { rows, .. } => {
25 + rows.len() * 4 + rows.iter().flatten().count() * 2 }
MathNode::Run { content, .. } => estimate_nodes_size(content),
MathNode::Limit { content, .. } => estimate_nodes_size(content),
MathNode::Degree(content) => estimate_nodes_size(content),
MathNode::Base(content) => estimate_nodes_size(content),
MathNode::Argument(content) => estimate_nodes_size(content),
MathNode::Numerator(content) => estimate_nodes_size(content),
MathNode::Denominator(content) => estimate_nodes_size(content),
MathNode::Integrand(content) => estimate_nodes_size(content),
MathNode::LowerLimit(content) => estimate_nodes_size(content),
MathNode::UpperLimit(content) => estimate_nodes_size(content),
}
}
pub fn estimate_matrix_capacity(rows: &[Vec<Vec<MathNode>>]) -> usize {
if rows.is_empty() {
return 0;
}
let num_rows = rows.len();
let num_cols = rows[0].len();
let env_overhead = 20; let row_separators = (num_rows.saturating_sub(1)) * 4; let col_separators = num_rows * (num_cols.saturating_sub(1)) * 3;
let content_estimate = rows.iter()
.flatten()
.flatten()
.count() * 5;
env_overhead + row_separators + col_separators + content_estimate
}