mirror of
https://github.com/PaddiM8/kalker.git
synced 2025-02-09 13:09:28 +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(
|
let reg = Regex::new(
|
||||||
r"(?x)
|
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<radix>0[box][a-zA-Z0-9]+) |
|
||||||
(?P<identifier>[^!-@\s_|^⌊⌋⌈⌉\[\]\{\}⟦⟧≠≥≤⁰¹²³⁴⁵⁶⁷⁸⁹⁺⁻⁼⁽⁾₀₁₂₃₄₅₆₇₈₉₊₋₌₍₎]+(_\d+)?)",
|
(?P<identifier>[^!-@\s_|^⌊⌋⌈⌉\[\]\{\}⟦⟧≠≥≤⁰¹²³⁴⁵⁶⁷⁸⁹⁺⁻⁼⁽⁾₀₁₂₃₄₅₆₇₈₉₊₋₌₍₎ᵀ]+(_\d+)?)",
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -161,6 +161,7 @@ lazy_static! {
|
|||||||
m.insert(" or", " ∨");
|
m.insert(" or", " ∨");
|
||||||
m.insert("*", "×");
|
m.insert("*", "×");
|
||||||
m.insert("/", "÷");
|
m.insert("/", "÷");
|
||||||
|
m.insert("^T", "ᵀ");
|
||||||
m.insert("asin", "sin⁻¹()");
|
m.insert("asin", "sin⁻¹()");
|
||||||
m.insert("acos", "cos⁻¹()");
|
m.insert("acos", "cos⁻¹()");
|
||||||
m.insert("atan", "tan⁻¹()");
|
m.insert("atan", "tan⁻¹()");
|
||||||
|
@ -312,7 +312,7 @@ fn analyse_binary(
|
|||||||
));
|
));
|
||||||
context.equation_variable = None;
|
context.equation_variable = None;
|
||||||
|
|
||||||
return Ok(inverted);
|
Ok(inverted)
|
||||||
}
|
}
|
||||||
(Expr::Var(_), TokenKind::Star, _) => {
|
(Expr::Var(_), TokenKind::Star, _) => {
|
||||||
if let Expr::Var(identifier) = left {
|
if let Expr::Var(identifier) = left {
|
||||||
@ -321,13 +321,18 @@ fn analyse_binary(
|
|||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(Expr::Var(_), TokenKind::Power, _) => {
|
(_, TokenKind::Power, _) => match (left, right) {
|
||||||
if let Expr::Var(identifier) = left {
|
(left, Expr::Var(identifier)) if &identifier.full_name == "T" => Ok(Expr::FnCall(
|
||||||
analyse_var(context, identifier, None, Some(right))
|
Identifier::from_full_name("transpose"),
|
||||||
} else {
|
vec![analyse_expr(context, left)?],
|
||||||
unreachable!()
|
)),
|
||||||
}
|
(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, _) => {
|
(_, TokenKind::Colon, _) => {
|
||||||
context.in_comprehension = true;
|
context.in_comprehension = true;
|
||||||
context.in_conditional = true;
|
context.in_conditional = true;
|
||||||
|
@ -48,7 +48,8 @@ mod tests {
|
|||||||
#[test_case("functions")]
|
#[test_case("functions")]
|
||||||
#[test_case("groups")]
|
#[test_case("groups")]
|
||||||
#[test_case("integration")]
|
#[test_case("integration")]
|
||||||
#[test_case("matrices")]
|
#[test_case("matrices/operations")]
|
||||||
|
#[test_case("matrices/transpose")]
|
||||||
#[test_case("radix")]
|
#[test_case("radix")]
|
||||||
#[test_case("sum")]
|
#[test_case("sum")]
|
||||||
#[test_case("variables")]
|
#[test_case("variables")]
|
||||||
|
@ -78,9 +78,13 @@ impl<'a> Lexer<'a> {
|
|||||||
let mut tokens = Vec::new();
|
let mut tokens = Vec::new();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let next = self.next();
|
let mut next = self.next();
|
||||||
|
if next.kind == TokenKind::Power && !next.value.is_empty() {
|
||||||
if let TokenKind::Eof = next.kind {
|
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);
|
tokens.push(next);
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
@ -158,6 +162,8 @@ impl<'a> Lexer<'a> {
|
|||||||
'≠' => build(TokenKind::NotEquals, "", span),
|
'≠' => build(TokenKind::NotEquals, "", span),
|
||||||
'≥' => build(TokenKind::GreaterOrEquals, "", span),
|
'≥' => build(TokenKind::GreaterOrEquals, "", span),
|
||||||
'≤' => build(TokenKind::LessOrEquals, "", 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,
|
// Some of the special symbols will be lexed here,
|
||||||
// so that they don't merge with other symbols.
|
// so that they don't merge with other symbols.
|
||||||
'π' => build(TokenKind::Identifier, "pi", span),
|
'π' => build(TokenKind::Identifier, "pi", span),
|
||||||
@ -385,9 +391,8 @@ fn is_valid_identifier(c: Option<&char>) -> bool {
|
|||||||
match c {
|
match c {
|
||||||
'+' | '-' | '/' | '*' | '%' | '^' | '!' | '(' | ')' | '=' | '.' | ',' | ';' | '|'
|
'+' | '-' | '/' | '*' | '%' | '^' | '!' | '(' | ')' | '=' | '.' | ',' | ';' | '|'
|
||||||
| '⌊' | '⌋' | '⌈' | '⌉' | '[' | ']' | '{' | '}' | 'π' | '√' | 'τ' | 'ϕ' | 'Γ' | '<'
|
| '⌊' | '⌋' | '⌈' | '⌉' | '[' | ']' | '{' | '}' | 'π' | '√' | 'τ' | 'ϕ' | 'Γ' | '<'
|
||||||
| '>' | '≠' | '≥' | '≤' | '×' | '÷' | '⋅' | '⟦' | '⟧' | '∧' | '∨' | ':' | '\n' => {
|
| '>' | '≠' | '≥' | '≤' | '×' | '÷' | '⋅' | '⟦' | '⟧' | '∧' | '∨' | ':' | 'ᵀ'
|
||||||
false
|
| '\n' => false,
|
||||||
}
|
|
||||||
_ => !c.is_digit(10) || is_superscript(c) || is_subscript(c),
|
_ => !c.is_digit(10) || is_superscript(c) || is_subscript(c),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -63,23 +63,24 @@ lazy_static! {
|
|||||||
|
|
||||||
m.insert("abs", (UnaryFuncInfo(abs, Other), ""));
|
m.insert("abs", (UnaryFuncInfo(abs, Other), ""));
|
||||||
m.insert("arg", (UnaryFuncInfo(arg, Other), ""));
|
m.insert("arg", (UnaryFuncInfo(arg, Other), ""));
|
||||||
|
m.insert("bitcmp", (UnaryFuncInfo(bitcmp, Other), ""));
|
||||||
m.insert("cbrt", (UnaryFuncInfo(cbrt, Other), ""));
|
m.insert("cbrt", (UnaryFuncInfo(cbrt, Other), ""));
|
||||||
m.insert("ceil", (UnaryFuncInfo(ceil, Other), ""));
|
m.insert("ceil", (UnaryFuncInfo(ceil, Other), ""));
|
||||||
m.insert("iverson", (UnaryFuncInfo(iverson, Other), ""));
|
|
||||||
m.insert("exp", (UnaryFuncInfo(exp, Other), ""));
|
m.insert("exp", (UnaryFuncInfo(exp, Other), ""));
|
||||||
m.insert("floor", (UnaryFuncInfo(floor, Other), ""));
|
m.insert("floor", (UnaryFuncInfo(floor, Other), ""));
|
||||||
m.insert("frac", (UnaryFuncInfo(frac, Other), ""));
|
m.insert("frac", (UnaryFuncInfo(frac, Other), ""));
|
||||||
m.insert("Im", (UnaryFuncInfo(im, Other), ""));
|
|
||||||
m.insert("gamma", (UnaryFuncInfo(gamma, Other), ""));
|
m.insert("gamma", (UnaryFuncInfo(gamma, Other), ""));
|
||||||
m.insert("Γ", (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("ln", (UnaryFuncInfo(ln, Other), ""));
|
||||||
|
m.insert("log", (UnaryFuncInfo(log, Other), ""));
|
||||||
m.insert("Re", (UnaryFuncInfo(re, Other), ""));
|
m.insert("Re", (UnaryFuncInfo(re, Other), ""));
|
||||||
m.insert("round", (UnaryFuncInfo(round, Other), ""));
|
m.insert("round", (UnaryFuncInfo(round, Other), ""));
|
||||||
m.insert("sqrt", (UnaryFuncInfo(sqrt, Other), ""));
|
m.insert("sqrt", (UnaryFuncInfo(sqrt, Other), ""));
|
||||||
m.insert("√", (UnaryFuncInfo(sqrt, Other), ""));
|
m.insert("√", (UnaryFuncInfo(sqrt, Other), ""));
|
||||||
|
m.insert("transpose", (UnaryFuncInfo(transpose, Other), ""));
|
||||||
m.insert("trunc", (UnaryFuncInfo(trunc, Other), ""));
|
m.insert("trunc", (UnaryFuncInfo(trunc, Other), ""));
|
||||||
m.insert("bitcmp", (UnaryFuncInfo(bitcmp, Other), ""));
|
|
||||||
m
|
m
|
||||||
};
|
};
|
||||||
pub static ref BINARY_FUNCS: HashMap<&'static str, (BinaryFuncInfo, &'static str)> = {
|
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 {
|
pub fn trunc(x: KalkValue) -> KalkValue {
|
||||||
let (real, imaginary, unit) = as_number_or_return!(x);
|
let (real, imaginary, unit) = as_number_or_return!(x);
|
||||||
KalkValue::Number(real.trunc(), imaginary.trunc(), unit)
|
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]
|
#[test]
|
||||||
#[allow(clippy::approx_constant)]
|
#[allow(clippy::approx_constant)]
|
||||||
fn test_trig_funcs() {
|
fn test_trig_funcs() {
|
||||||
|
@ -1,10 +1,47 @@
|
|||||||
pub fn is_superscript(c: &char) -> bool {
|
pub fn is_superscript(c: &char) -> bool {
|
||||||
matches!(c, '⁰' | '¹' | '²' | '³' | '⁴' | '⁵' | '⁶' | '⁷' | '⁸' | '⁹' | '⁺' | '⁻' | '⁼' | '⁽' | '⁾')
|
matches!(
|
||||||
|
c,
|
||||||
|
'⁰' | '¹'
|
||||||
|
| '²'
|
||||||
|
| '³'
|
||||||
|
| '⁴'
|
||||||
|
| '⁵'
|
||||||
|
| '⁶'
|
||||||
|
| '⁷'
|
||||||
|
| '⁸'
|
||||||
|
| '⁹'
|
||||||
|
| '⁺'
|
||||||
|
| '⁻'
|
||||||
|
| '⁼'
|
||||||
|
| '⁽'
|
||||||
|
| '⁾'
|
||||||
|
| 'ᵀ'
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_subscript(c: &char) -> bool {
|
pub fn is_subscript(c: &char) -> bool {
|
||||||
matches!(c, '₀' | '₁' | '₂' | '₃' | '₄' | '₅' | '₆' | '₇' | '₈' | '₉' | '₊' | '₋' | '₌' | '₍' | '₎'
|
matches!(
|
||||||
| 'ₖ' | 'ₗ' | 'ₘ' | 'ₙ' | 'ₓ')
|
c,
|
||||||
|
'₀' | '₁'
|
||||||
|
| '₂'
|
||||||
|
| '₃'
|
||||||
|
| '₄'
|
||||||
|
| '₅'
|
||||||
|
| '₆'
|
||||||
|
| '₇'
|
||||||
|
| '₈'
|
||||||
|
| '₉'
|
||||||
|
| '₊'
|
||||||
|
| '₋'
|
||||||
|
| '₌'
|
||||||
|
| '₍'
|
||||||
|
| '₎'
|
||||||
|
| 'ₖ'
|
||||||
|
| 'ₗ'
|
||||||
|
| 'ₘ'
|
||||||
|
| 'ₙ'
|
||||||
|
| 'ₓ'
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_subscript(chars: impl Iterator<Item = char>) -> Option<u8> {
|
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',
|
'ₗ' => 'l',
|
||||||
'ₘ' => 'm',
|
'ₘ' => 'm',
|
||||||
'ₙ' => 'n',
|
'ₙ' => 'n',
|
||||||
'ₓ' => 'x',
|
'ᵀ' => 'T',
|
||||||
_ => c,
|
_ => 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 result = input;
|
||||||
let offset = 0;
|
let offset = 0;
|
||||||
result = result.replace(
|
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,
|
substring,
|
||||||
|
power,
|
||||||
brackets,
|
brackets,
|
||||||
_radix,
|
_radix,
|
||||||
_2,
|
_2,
|
||||||
@ -323,6 +324,12 @@
|
|||||||
op,
|
op,
|
||||||
identifier
|
identifier
|
||||||
) => {
|
) => {
|
||||||
|
if (power) {
|
||||||
|
if (substring == "^T") {
|
||||||
|
substring = "ᵀ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (brackets) {
|
if (brackets) {
|
||||||
if (substring == "[[") {
|
if (substring == "[[") {
|
||||||
offset -= 1;
|
offset -= 1;
|
||||||
|
Loading…
Reference in New Issue
Block a user