Matrix tranpose function

This commit is contained in:
bakk 2022-01-19 23:46:36 +01:00 committed by PaddiM8
parent 777d54cd40
commit 917eea3ffe
9 changed files with 161 additions and 26 deletions

View File

@ -105,9 +105,9 @@ impl Highlighter for LineHighlighter {
let reg = Regex::new(
r"(?x)
(?P<op>([+\-/*%^!×÷⋅∧∨]|if|otherwise|\sand|\sor|\smod|load|exit|clear|help)) |
(?P<op>([+\-/*%^!×÷⋅∧∨]|if|otherwise|\sand|\sor|\smod|load|exit|clear|help)) |
(?P<radix>0[box][a-zA-Z0-9]+) |
(?P<identifier>[^!-@\s_|^\[\]\{\}¹²³]+(_\d+)?)",
(?P<identifier>[^!-@\s_|^\[\]\{\}¹²³]+(_\d+)?)",
)
.unwrap();
@ -161,6 +161,7 @@ lazy_static! {
m.insert(" or", " ");
m.insert("*", "×");
m.insert("/", "÷");
m.insert("^T", "");
m.insert("asin", "sin⁻¹()");
m.insert("acos", "cos⁻¹()");
m.insert("atan", "tan⁻¹()");

View File

@ -312,7 +312,7 @@ fn analyse_binary(
));
context.equation_variable = None;
return Ok(inverted);
Ok(inverted)
}
(Expr::Var(_), TokenKind::Star, _) => {
if let Expr::Var(identifier) = left {
@ -321,13 +321,18 @@ fn analyse_binary(
unreachable!()
}
}
(Expr::Var(_), TokenKind::Power, _) => {
if let Expr::Var(identifier) = left {
analyse_var(context, identifier, None, Some(right))
} else {
unreachable!()
}
}
(_, TokenKind::Power, _) => match (left, right) {
(left, Expr::Var(identifier)) if &identifier.full_name == "T" => Ok(Expr::FnCall(
Identifier::from_full_name("transpose"),
vec![analyse_expr(context, left)?],
)),
(Expr::Var(identifier), right) => analyse_var(context, identifier, None, Some(right)),
(left, right) => Ok(Expr::Binary(
Box::new(analyse_expr(context, left)?),
TokenKind::Power,
Box::new(analyse_expr(context, right)?),
)),
},
(_, TokenKind::Colon, _) => {
context.in_comprehension = true;
context.in_conditional = true;

View File

@ -48,7 +48,8 @@ mod tests {
#[test_case("functions")]
#[test_case("groups")]
#[test_case("integration")]
#[test_case("matrices")]
#[test_case("matrices/operations")]
#[test_case("matrices/transpose")]
#[test_case("radix")]
#[test_case("sum")]
#[test_case("variables")]

View File

@ -78,9 +78,13 @@ impl<'a> Lexer<'a> {
let mut tokens = Vec::new();
loop {
let next = self.next();
if let TokenKind::Eof = next.kind {
let mut next = self.next();
if next.kind == TokenKind::Power && !next.value.is_empty() {
let value: String = next.value.drain(..).collect();
let span = next.span;
tokens.push(next);
tokens.push(build(TokenKind::Identifier, &value, span));
} else if TokenKind::Eof == next.kind {
tokens.push(next);
break;
} else {
@ -158,6 +162,8 @@ impl<'a> Lexer<'a> {
'≠' => build(TokenKind::NotEquals, "", span),
'≥' => build(TokenKind::GreaterOrEquals, "", span),
'≤' => build(TokenKind::LessOrEquals, "", span),
// A bit hacky. When the result is handled, this token is turned into two tokens
'ᵀ' => build(TokenKind::Power, "T", span),
// Some of the special symbols will be lexed here,
// so that they don't merge with other symbols.
'π' => build(TokenKind::Identifier, "pi", span),
@ -385,9 +391,8 @@ fn is_valid_identifier(c: Option<&char>) -> bool {
match c {
'+' | '-' | '/' | '*' | '%' | '^' | '!' | '(' | ')' | '=' | '.' | ',' | ';' | '|'
| '⌊' | '⌋' | '⌈' | '⌉' | '[' | ']' | '{' | '}' | 'π' | '√' | 'τ' | 'ϕ' | 'Γ' | '<'
| '>' | '≠' | '≥' | '≤' | '×' | '÷' | '⋅' | '⟦' | '⟧' | '∧' | '' | ':' | '\n' => {
false
}
| '>' | '≠' | '≥' | '≤' | '×' | '÷' | '⋅' | '⟦' | '⟧' | '∧' | '' | ':' | 'ᵀ'
| '\n' => false,
_ => !c.is_digit(10) || is_superscript(c) || is_subscript(c),
}
} else {

View File

@ -63,23 +63,24 @@ lazy_static! {
m.insert("abs", (UnaryFuncInfo(abs, Other), ""));
m.insert("arg", (UnaryFuncInfo(arg, Other), ""));
m.insert("bitcmp", (UnaryFuncInfo(bitcmp, Other), ""));
m.insert("cbrt", (UnaryFuncInfo(cbrt, Other), ""));
m.insert("ceil", (UnaryFuncInfo(ceil, Other), ""));
m.insert("iverson", (UnaryFuncInfo(iverson, Other), ""));
m.insert("exp", (UnaryFuncInfo(exp, Other), ""));
m.insert("floor", (UnaryFuncInfo(floor, Other), ""));
m.insert("frac", (UnaryFuncInfo(frac, Other), ""));
m.insert("Im", (UnaryFuncInfo(im, Other), ""));
m.insert("gamma", (UnaryFuncInfo(gamma, Other), ""));
m.insert("Γ", (UnaryFuncInfo(gamma, Other), ""));
m.insert("log", (UnaryFuncInfo(log, Other), ""));
m.insert("iverson", (UnaryFuncInfo(iverson, Other), ""));
m.insert("Im", (UnaryFuncInfo(im, Other), ""));
m.insert("ln", (UnaryFuncInfo(ln, Other), ""));
m.insert("log", (UnaryFuncInfo(log, Other), ""));
m.insert("Re", (UnaryFuncInfo(re, Other), ""));
m.insert("round", (UnaryFuncInfo(round, Other), ""));
m.insert("sqrt", (UnaryFuncInfo(sqrt, Other), ""));
m.insert("", (UnaryFuncInfo(sqrt, Other), ""));
m.insert("transpose", (UnaryFuncInfo(transpose, Other), ""));
m.insert("trunc", (UnaryFuncInfo(trunc, Other), ""));
m.insert("bitcmp", (UnaryFuncInfo(bitcmp, Other), ""));
m
};
pub static ref BINARY_FUNCS: HashMap<&'static str, (BinaryFuncInfo, &'static str)> = {
@ -821,6 +822,25 @@ pub mod funcs {
}
}
#[allow(clippy::needless_range_loop)]
pub fn transpose(x: KalkValue) -> KalkValue {
if let KalkValue::Matrix(rows) = x {
let original_row_count = rows.len();
let original_column_count = rows.first().unwrap().len();
let mut result =
vec![vec![KalkValue::from(0f64); original_row_count]; original_column_count];
for i in 0..original_row_count {
for j in 0..original_column_count {
result[j][i] = rows[i][j].clone();
}
}
KalkValue::Matrix(result)
} else {
KalkValue::nan()
}
}
pub fn trunc(x: KalkValue) -> KalkValue {
let (real, imaginary, unit) = as_number_or_return!(x);
KalkValue::Number(real.trunc(), imaginary.trunc(), unit)
@ -924,6 +944,38 @@ mod tests {
}
}
#[test]
fn test_transpose() {
fn to_matrix(rows: Vec<Vec<i32>>) -> KalkValue {
let mut new_rows = Vec::new();
for row in rows {
let mut new_row = Vec::new();
for value in row {
new_row.push(KalkValue::from(value as f64));
}
new_rows.push(new_row);
}
KalkValue::Matrix(new_rows)
}
assert_eq!(
transpose(to_matrix(vec![vec![1, 2], vec![3, 4]])),
to_matrix(vec![vec![1, 3], vec![2, 4]])
);
assert_eq!(
transpose(to_matrix(vec![vec![1, 2], vec![3, 4], vec![5, 6]])),
to_matrix(vec![vec![1, 3, 5], vec![2, 4, 6]])
);
assert_eq!(
transpose(to_matrix(vec![vec![1, 2, 3], vec![4, 5, 6], vec![7, 8, 9]])),
to_matrix(vec![vec![1, 4, 7], vec![2, 5, 8], vec![3, 6, 9]])
);
}
#[test]
#[allow(clippy::approx_constant)]
fn test_trig_funcs() {

View File

@ -1,10 +1,47 @@
pub fn is_superscript(c: &char) -> bool {
matches!(c, '⁰' | '¹' | '²' | '³' | '⁴' | '⁵' | '⁶' | '⁷' | '⁸' | '⁹' | '⁺' | '⁻' | '⁼' | '⁽' | '⁾')
matches!(
c,
'⁰' | '¹'
| '²'
| '³'
| '⁴'
| '⁵'
| '⁶'
| '⁷'
| '⁸'
| '⁹'
| '⁺'
| '⁻'
| '⁼'
| '⁽'
| '⁾'
| 'ᵀ'
)
}
pub fn is_subscript(c: &char) -> bool {
matches!(c, '₀' | '₁' | '₂' | '₃' | '₄' | '₅' | '₆' | '₇' | '₈' | '₉' | '₊' | '₋' | '₌' | '₍' | '₎'
| 'ₖ' | 'ₗ' | 'ₘ' | 'ₙ' | 'ₓ')
matches!(
c,
'₀' | '₁'
| '₂'
| '₃'
| '₄'
| '₅'
| '₆'
| '₇'
| '₈'
| '₉'
| '₊'
| '₋'
| '₌'
| '₍'
| '₎'
| 'ₖ'
| 'ₗ'
| 'ₘ'
| 'ₙ'
| 'ₓ'
)
}
pub fn parse_subscript(chars: impl Iterator<Item = char>) -> Option<u8> {
@ -38,7 +75,7 @@ pub fn subscript_to_normal(chars: impl Iterator<Item = char>) -> String {
'ₗ' => 'l',
'ₘ' => 'm',
'ₙ' => 'n',
'ₓ' => 'x',
'ᵀ' => 'T',
_ => c,
});
}

View File

@ -0,0 +1,27 @@
m_1 = [1, 2
3, 4]
m_2 = [1, 2
3, 4
5, 6]
m_3 = [1, 2, 3
4, 5, 6]
m_4 = [1, 2, 3
4, 5, 6
7, 8, 9]
m_1^T = [1, 3
2, 4] and
m_2^T = [1, 3, 5
2, 4, 6] and
m_3^T = [1, 4
2, 5
3, 6] and
m_4^T = [1, 4, 7
2, 5, 8
3, 6, 9]

View File

@ -310,9 +310,10 @@
let result = input;
let offset = 0;
result = result.replace(
/(?<brackets>\[\[)|(?<radix>0[box][a-zA-Z0-9]+)|(?<comparison>(!=|[<>]=?))|(?<html>[<>&]|(\n\s*\}?|\s+))|(?<op>([+\-/*%^!≈×÷⋅∧∨]|if|otherwise|and|or|mod)|(?<identifier>[^!-@\s_|^⌊⌋⌈⌉≈\[\]\{\}⟦⟧≠≥≤⁰¹²³⁴⁵⁶⁷⁸⁹⁺⁻⁼⁽⁾₀₁₂₃₄₅₆₇₈₉₊₋₌₍₎]+(_\d+)?)\(?)/g,
/(?<power>\^[0-9T])|(?<brackets>\[\[)|(?<radix>0[box][a-zA-Z0-9]+)|(?<comparison>(!=|[<>]=?))|(?<html>[<>&]|(\n\s*\}?|\s+))|(?<op>([+\-/*%^!≈×÷⋅∧∨]|if|otherwise|and|or|mod)|(?<identifier>[^!-@\s_|^⌊⌋⌈⌉≈\[\]\{\}⟦⟧≠≥≤⁰¹²³⁴⁵⁶⁷⁸⁹⁺⁻⁼⁽⁾₀₁₂₃₄₅₆₇₈₉₊₋₌₍₎×÷⋅∧∨ᵀ]+(_\d+)?)\(?)/g,
(
substring,
power,
brackets,
_radix,
_2,
@ -323,6 +324,12 @@
op,
identifier
) => {
if (power) {
if (substring == "^T") {
substring = "ᵀ";
}
}
if (brackets) {
if (substring == "[[") {
offset -= 1;