use crate::{Error, Result};
const RESERVED_CHARS: &[char] = &[',', '-', '>', '(', ')', ' '];
pub(crate) fn char_to_label(c: char) -> Result<u32> {
if RESERVED_CHARS.contains(&c) {
return Err(Error::InvalidArgument(format!(
"invalid einsum label character: {c:?} (U+{:04X}); reserved syntax character",
c as u32
)));
}
Ok(c as u32)
}
pub(crate) fn split_and_validate_notation(notation: &str) -> Result<(&str, &str)> {
if notation.contains("...") {
return Err(Error::InvalidArgument(
"einsum ellipsis '...' is not supported yet".into(),
));
}
if notation.contains('.') {
return Err(Error::InvalidArgument(
"einsum label '.' is reserved for ellipsis, which is not supported yet".into(),
));
}
let parts: Vec<&str> = notation.split("->").collect();
if parts.len() != 2 {
return Err(Error::InvalidArgument(format!(
"einsum notation must contain exactly one '->', got: {notation}"
)));
}
let lhs = parts[0];
let rhs = parts[1];
let mut depth: i32 = 0;
for c in lhs.chars() {
match c {
'(' => depth += 1,
')' => {
depth -= 1;
if depth < 0 {
return Err(Error::InvalidArgument(format!(
"unmatched ')' in einsum notation: {notation}"
)));
}
}
_ => {}
}
}
if depth != 0 {
return Err(Error::InvalidArgument(format!(
"unmatched '(' in einsum notation: {notation}"
)));
}
Ok((lhs, rhs))
}