mirror of
https://github.com/PaddiM8/kalker.git
synced 2024-11-07 16:34:21 +01:00
Matrix tranpose function
This commit is contained in:
parent
777d54cd40
commit
917eea3ffe
@ -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⁻¹()");
|
||||
|
@ -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;
|
||||
|
@ -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")]
|
||||
|
@ -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 {
|
||||
|
@ -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() {
|
||||
|
@ -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,
|
||||
});
|
||||
}
|
||||
|
27
tests/matrices/transpose.kalker
Normal file
27
tests/matrices/transpose.kalker
Normal 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]
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user