This commit is contained in:
PaddiM8 2022-01-07 00:39:29 +01:00
parent b937cef83d
commit 95fd78f3d1
7 changed files with 355 additions and 51 deletions

View File

@ -22,6 +22,7 @@ pub enum Expr {
Literal(f64),
Piecewise(Vec<ConditionalPiece>),
Vector(Vec<Expr>),
Matrix(Vec<Vec<Expr>>),
Indexer(Box<Expr>, Box<Expr>),
}

View File

@ -125,6 +125,7 @@ pub(crate) fn eval_expr(
}
Expr::Piecewise(pieces) => eval_piecewise(context, pieces, unit),
Expr::Vector(values) => eval_vector(context, values),
Expr::Matrix(rows) => eval_matrix(context, rows),
Expr::Indexer(var, index) => eval_indexer(context, var, index, unit),
}
}
@ -536,6 +537,20 @@ fn eval_vector(context: &mut Context, values: &Vec<Expr>) -> Result<KalkValue, C
Ok(KalkValue::Vector(eval_values))
}
fn eval_matrix(context: &mut Context, rows: &Vec<Vec<Expr>>) -> Result<KalkValue, CalcError> {
let mut eval_rows = Vec::new();
for row in rows {
let mut eval_row = Vec::new();
for value in row {
eval_row.push(eval_expr(context, value, "")?)
}
eval_rows.push(eval_row);
}
Ok(KalkValue::Matrix(eval_rows))
}
fn eval_indexer(
context: &mut Context,
var: &Expr,

View File

@ -88,6 +88,7 @@ fn invert(
Expr::Literal(_) => Ok((target_expr, expr.clone())),
Expr::Piecewise(_) => Err(CalcError::UnableToInvert(String::from("Piecewise"))),
Expr::Vector(_) => Err(CalcError::UnableToInvert(String::from("Vector"))),
Expr::Matrix(_) => Err(CalcError::UnableToInvert(String::from("Matrix"))),
Expr::Indexer(_, _) => Err(CalcError::UnableToInvert(String::from("Inverter"))),
}
}
@ -391,6 +392,9 @@ pub fn contains_var(symbol_table: &SymbolTable, expr: &Expr, var_name: &str) ->
Expr::Vector(items) => items
.iter()
.any(|x| contains_var(symbol_table, x, var_name)),
Expr::Matrix(rows) => rows
.iter()
.any(|row| row.iter().any(|x| contains_var(symbol_table, x, var_name))),
Expr::Indexer(_, _) => false,
}
}

View File

@ -173,6 +173,7 @@ pub enum KalkValue {
Number(Float, Float, String),
Boolean(bool),
Vector(Vec<KalkValue>),
Matrix(Vec<Vec<KalkValue>>),
}
impl KalkValue {
@ -192,7 +193,7 @@ impl KalkValue {
if &as_str == "0" {
imaginary_as_str
} else {
format!("{} {} {}i", as_str, sign, imaginary_as_str)
format!("{} {} {}i", as_str, sign, imaginary_as_str)
}
} else {
as_str
@ -206,18 +207,43 @@ impl KalkValue {
}
}
KalkValue::Vector(values) => {
format!(
"({})",
values
.iter()
.map(|x| x.to_string())
.collect::<Vec<String>>()
.join(", ")
)
}
KalkValue::Matrix(rows) => {
let mut value_strings = Vec::new();
let mut longest = 0;
for row in rows {
for value in row {
let value_str = value.to_string();
longest = longest.max(value_str.len());
value_strings.push(format!("{},", value_str));
}
value_strings.last_mut().unwrap().pop(); // Trailing comma
value_strings.push(String::from("\n"));
}
let mut result = String::from("[");
for value in values {
result.push_str(&value.to_string());
result.push_str(", ");
for value_str in value_strings {
if value_str == "\n" {
result.push_str("\n ");
} else {
result.push_str(&format!("{:width$} ", value_str, width = longest + 1));
}
}
if values.len() > 0 {
result.pop();
result.pop();
}
result.push_str("]");
result.pop(); // Trailing new-line
result.pop(); // Trailing space
result.pop(); // Trailing space
result.pop(); // Trailing comma
result.push(']');
result
}
@ -656,6 +682,9 @@ impl KalkValue {
KalkValue::Number(real, imaginary, _),
KalkValue::Number(real_rhs, imaginary_rhs, unit),
) => KalkValue::Number(real + real_rhs, imaginary + imaginary_rhs, unit.to_string()),
(KalkValue::Matrix(_), _) | (_, KalkValue::Matrix(_)) => {
calculate_matrix(self, rhs, &KalkValue::add_without_unit)
}
(KalkValue::Vector(_), _) | (_, KalkValue::Vector(_)) => {
calculate_vector(self, rhs, &KalkValue::add_without_unit)
}
@ -669,6 +698,9 @@ impl KalkValue {
KalkValue::Number(real, imaginary, _),
KalkValue::Number(real_rhs, imaginary_rhs, unit),
) => KalkValue::Number(real - real_rhs, imaginary - imaginary_rhs, unit.to_string()),
(KalkValue::Matrix(_), _) | (_, KalkValue::Matrix(_)) => {
calculate_matrix(self, rhs, &KalkValue::sub_without_unit)
}
(KalkValue::Vector(_), _) | (_, KalkValue::Vector(_)) => {
calculate_vector(self, rhs, &KalkValue::sub_without_unit)
}
@ -677,26 +709,83 @@ impl KalkValue {
}
pub(crate) fn mul_without_unit(self, rhs: &KalkValue) -> KalkValue {
// (a + bi)(c + di) = ac + adi + bci + bdi²
match (self.clone(), rhs) {
// Make sure matrix is always first to avoid having to match
// different orders in the next match expression.
let (lhs, rhs) = match (&self, rhs) {
(KalkValue::Matrix(_), KalkValue::Matrix(_)) => (&self, rhs),
(_, KalkValue::Matrix(_)) => (rhs, &self),
_ => (&self, rhs),
};
match (lhs, rhs) {
(
KalkValue::Number(real, imaginary, _),
KalkValue::Number(real_rhs, imaginary_rhs, unit),
) => KalkValue::Number(
real.clone() * real_rhs.clone() - imaginary.clone() * imaginary_rhs.clone(),
real * imaginary_rhs + imaginary * real_rhs,
// (a + bi)(c + di) = ac + adi + bci + bdi²
real.clone() * real_rhs - imaginary.clone() * imaginary_rhs,
real.clone() * imaginary_rhs + imaginary * real_rhs,
unit.to_string(),
),
(KalkValue::Matrix(rows), KalkValue::Number(_, _, _)) => KalkValue::Matrix(
rows.iter()
.map(|row| {
row.iter()
.map(|x| x.clone().mul_without_unit(rhs))
.collect()
})
.collect(),
),
(KalkValue::Matrix(rows), KalkValue::Vector(values_rhs)) => {
if rows.first().unwrap().len() != values_rhs.len() {
return KalkValue::nan();
}
let mut new_values: Vec<KalkValue> = Vec::new();
for row in rows {
new_values.push(
row.iter()
.zip(values_rhs)
.map(|(x, y)| x.clone().mul_without_unit(y))
.sum(),
)
}
KalkValue::Vector(new_values)
}
(KalkValue::Matrix(rows), KalkValue::Matrix(rows_rhs)) => {
if rows.first().unwrap().len() != rows_rhs.len() {
return KalkValue::nan();
}
let mut result = Vec::new();
// For every row in lhs
for i in 0..rows.len() {
let mut dot_products = Vec::new();
// For every column in rhs
for j in 0..rows.len() {
let mut dot_product = KalkValue::from(0f64);
// For every value in the current lhs row
for (k, value) in rows[i].iter().enumerate() {
let value_rhs = &rows_rhs[k][j];
dot_product = dot_product
.add_without_unit(&value.clone().mul_without_unit(value_rhs));
}
dot_products.push(dot_product);
}
result.push(dot_products);
}
KalkValue::Matrix(result)
}
(KalkValue::Vector(values), KalkValue::Number(_, _, _)) => KalkValue::Vector(
values
.iter()
.map(|x| x.clone().mul_without_unit(&self))
.collect(),
),
(KalkValue::Number(_, _, _), KalkValue::Vector(values_rhs)) => KalkValue::Vector(
values_rhs
.iter()
.map(|x| self.clone().mul_without_unit(x))
.map(|x| x.clone().mul_without_unit(lhs))
.collect(),
),
(KalkValue::Vector(values), KalkValue::Vector(values_rhs)) => {
@ -738,6 +827,9 @@ impl KalkValue {
)
}
}
(KalkValue::Matrix(_), _) | (_, KalkValue::Matrix(_)) => {
calculate_matrix(self, rhs, &KalkValue::div_without_unit)
}
(KalkValue::Vector(_), _) | (_, KalkValue::Vector(_)) => {
calculate_vector(self, rhs, &KalkValue::div_without_unit)
}
@ -772,6 +864,35 @@ impl KalkValue {
KalkValue::Number(pow(real, real_rhs.clone()), float!(0), unit.to_string())
}
}
(KalkValue::Matrix(rows), KalkValue::Number(real, imaginary, _)) => {
if real < &0f64
|| real.clone().fract() > 0.000001f64
|| !(imaginary == &0f64 || imaginary == &-0f64)
|| rows.len() != rows.first().unwrap().len()
{
return KalkValue::nan();
}
if real == &0f64 {
return KalkValue::from(1f64);
}
let mut result = KalkValue::from(1f64);
for _ in 0..primitive!(real) as i32 {
result = result.mul_without_unit(&self);
}
result
}
(KalkValue::Number(_, _, _), KalkValue::Matrix(rows)) => KalkValue::Matrix(
rows.iter()
.map(|row| {
row.iter()
.map(|x| self.clone().pow_without_unit(x))
.collect()
})
.collect(),
),
(KalkValue::Vector(_), _) | (_, KalkValue::Vector(_)) => {
calculate_vector(self, rhs, &KalkValue::pow_without_unit)
}
@ -898,6 +1019,66 @@ fn calculate_vector(
}
}
fn calculate_matrix(
x: KalkValue,
y: &KalkValue,
action: &dyn Fn(KalkValue, &KalkValue) -> KalkValue,
) -> KalkValue {
// Make sure matrix is always first to avoid having to match
// different orders in the next match expression.
let (x, y) = match (&x, y) {
(_, KalkValue::Matrix(_)) => (y, &x),
_ => (&x, y),
};
match (x, y) {
(KalkValue::Matrix(rows), KalkValue::Number(_, _, _)) => KalkValue::Matrix(
rows.iter()
.map(|row| row.iter().map(|x| action(x.clone(), y)).collect())
.collect(),
),
(KalkValue::Matrix(rows), KalkValue::Vector(values_rhs)) => {
if rows.len() != values_rhs.len() {
return KalkValue::nan();
}
let mut new_rows = Vec::new();
for (i, row) in rows.iter().enumerate() {
new_rows.push(Vec::new());
for value in row {
new_rows
.last_mut()
.unwrap()
.push(action(value.clone(), &values_rhs[i]))
}
}
KalkValue::Matrix(new_rows)
}
(KalkValue::Matrix(rows), KalkValue::Matrix(rows_rhs)) => {
if rows.len() != rows_rhs.len()
|| rows.first().unwrap().len() != rows_rhs.first().unwrap().len()
{
return KalkValue::nan();
}
let mut new_rows = Vec::new();
for (i, row) in rows.iter().enumerate() {
new_rows.push(Vec::new());
for (j, value) in row.iter().enumerate() {
new_rows
.last_mut()
.unwrap()
.push(action(value.clone(), &rows_rhs[i][j]))
}
}
KalkValue::Matrix(new_rows)
}
_ => KalkValue::nan(),
}
}
fn calculate_unit(
context: &mut crate::interpreter::Context,
left: &KalkValue,
@ -937,6 +1118,20 @@ impl Into<String> for ScientificNotation {
}
}
impl std::iter::Sum<KalkValue> for KalkValue {
fn sum<I>(iter: I) -> KalkValue
where
I: std::iter::Iterator<Item = KalkValue>,
{
let mut sum = KalkValue::from(0f64);
for x in iter {
sum = sum.add_without_unit(&x);
}
sum
}
}
impl Into<String> for KalkValue {
fn into(self) -> String {
self.to_string()

View File

@ -105,6 +105,7 @@ pub enum CalcError {
ExpectedIf,
IncorrectAmountOfArguments(usize, String, usize),
ItemOfIndexDoesNotExist(usize),
InconsistentColumnWidths,
InvalidNumberLiteral(String),
InvalidOperator,
InvalidUnit,
@ -134,6 +135,7 @@ impl ToString for CalcError {
expected, func, got
),
CalcError::ItemOfIndexDoesNotExist(index) => format!("Item of index {} does not exist.", index),
CalcError::InconsistentColumnWidths => format!("Inconsistent column widths. Matrix columns must be the same size."),
CalcError::InvalidNumberLiteral(x) => format!("Invalid number literal: '{}'.", x),
CalcError::InvalidOperator => format!("Invalid operator."),
CalcError::InvalidUnit => format!("Invalid unit."),
@ -614,12 +616,39 @@ fn parse_group_fn(context: &mut Context) -> Result<Expr, CalcError> {
}
fn parse_vector(context: &mut Context) -> Result<Expr, CalcError> {
advance(context);
let kind = advance(context).kind;
if kind == TokenKind::OpenBracket {
skip_newlines(context);
}
let mut rows = vec![vec![parse_expr(context)?]];
let mut column_count = None;
let mut items_in_row = 1;
while match_token(context, TokenKind::Comma)
|| match_token(context, TokenKind::Semicolon)
|| (match_token(context, TokenKind::Newline)
&& peek_next(context).kind != TokenKind::ClosedBracket)
{
if kind == TokenKind::OpenBracket
&& (match_token(context, TokenKind::Newline)
|| match_token(context, TokenKind::Semicolon))
{
if let Some(columns) = column_count {
if columns != items_in_row {
return Err(CalcError::InconsistentColumnWidths);
}
} else {
column_count = Some(items_in_row);
}
rows.push(Vec::new());
items_in_row = 0;
}
let mut values = vec![parse_expr(context)?];
while match_token(context, TokenKind::Comma) {
advance(context);
values.push(parse_expr(context)?);
rows.last_mut().unwrap().push(parse_expr(context)?);
items_in_row += 1;
}
if peek(context).kind == TokenKind::EOF {
@ -628,12 +657,21 @@ fn parse_vector(context: &mut Context) -> Result<Expr, CalcError> {
)));
}
if kind == TokenKind::OpenBracket {
skip_newlines(context);
}
advance(context);
if values.len() == 1 {
Ok(Expr::Group(Box::new(values.pop().unwrap())))
if rows.len() == 1 {
let mut values = rows.pop().unwrap();
if values.len() == 1 {
Ok(Expr::Group(Box::new(values.pop().unwrap())))
} else {
Ok(Expr::Vector(values))
}
} else {
Ok(Expr::Vector(values))
Ok(Expr::Matrix(rows))
}
}

View File

@ -283,9 +283,8 @@ pub mod funcs {
let (real, imaginary, unit) = as_number_or_return!(x.clone());
if imaginary != 0f64 || real > 1f64 || real < -1f64 {
// -i * ln(i * sqrt(1 - z²) + z)
let root = sqrt(
KalkValue::from(1f64).sub_without_unit(&x.clone().mul_without_unit(&x.clone())),
);
let root =
sqrt(KalkValue::from(1f64).sub_without_unit(&x.clone().mul_without_unit(&x)));
let iroot = multiply_with_i(root.clone());
let (ln_real, ln_imaginary, ln_unit) =
as_number_or_return!(ln(iroot.add_without_unit(&x)));
@ -361,14 +360,14 @@ pub mod funcs {
let (real, imaginary, unit) = as_number_or_return!(x.clone());
if imaginary != 0f64 || real == 0f64 {
let (inv_x2_real, inv_x2_imaginary, inv_x2_unit) = as_number_or_return!(
KalkValue::from(1f64).div_without_unit(&x.clone().mul_without_unit(&x.clone()))
KalkValue::from(1f64).div_without_unit(&x.clone().mul_without_unit(&x))
);
let sqrt = sqrt(KalkValue::Number(
1f64 + inv_x2_real,
inv_x2_imaginary,
inv_x2_unit,
));
let inv_x = KalkValue::from(1f64).div_without_unit(&x.clone());
let inv_x = KalkValue::from(1f64).div_without_unit(&x);
ln(sqrt.add_without_unit(&inv_x))
} else {
@ -390,7 +389,7 @@ pub mod funcs {
let (real, imaginary, unit) = as_number_or_return!(x.clone());
if imaginary != 0f64 || real <= 0f64 || real > 1f64 {
// 1/z
let inv = KalkValue::from(1f64).div_without_unit(&x.clone());
let inv = KalkValue::from(1f64).div_without_unit(&x);
let (inv_real, inv_imaginary, inv_unit) = as_number_or_return!(inv.clone());
// sqrt(1/z - 1)
let sqrt1 = sqrt(KalkValue::Number(
@ -416,9 +415,8 @@ pub mod funcs {
let (real, imaginary, unit) = as_number_or_return!(x.clone());
if imaginary != 0f64 || real > 1f64 || real < -1f64 {
// i * ln(sqrt(1 - z²) - iz)
let root = sqrt(
KalkValue::from(1f64).sub_without_unit(&x.clone().mul_without_unit(&x.clone())),
);
let root =
sqrt(KalkValue::from(1f64).sub_without_unit(&x.clone().mul_without_unit(&x)));
let iz = multiply_with_i(x.clone());
let ln = ln(root.sub_without_unit(&iz));
multiply_with_i(ln)
@ -431,7 +429,7 @@ pub mod funcs {
let (real, imaginary, unit) = as_number_or_return!(x.clone());
if imaginary != 0f64 {
let (x2_real, x2_imaginary, x2_unit) =
as_number_or_return!(x.clone().mul_without_unit(&x.clone()));
as_number_or_return!(x.clone().mul_without_unit(&x));
let sqrt = sqrt(KalkValue::Number(x2_real + 1f64, x2_imaginary, x2_unit));
ln(x.add_without_unit(&sqrt))

View File

@ -47,8 +47,19 @@
let highlightedTextElement: HTMLElement;
let ignoreNextInput = false;
function setText(text: string) {
const [highlighted, offset] = highlight(text);
enum HighlightType {
Output,
InputField,
History,
}
function setText(text: string, isFinalBeforeSubmit = false) {
const [highlighted, offset] = highlight(
text,
isFinalBeforeSubmit
? HighlightType.History
: HighlightType.InputField
);
const prevCursorPos = inputElement.selectionStart;
setHtml(highlighted);
setCaret(prevCursorPos + offset);
@ -90,12 +101,18 @@
}
}
function hasUnevenAmountOfBraces(input: string): boolean {
function hasUnevenAmountOfBrackets(
input: string,
openChar: string,
closedChar: string,
onlyCheckFirstLine = false
): boolean {
let openCount = 0;
let closedCount = 0;
for (const char of input) {
if (char == "{") openCount++;
if (char == "}") closedCount++;
if (onlyCheckFirstLine && char == "\n") break;
if (char == openChar) openCount++;
if (char == closedChar) closedCount++;
}
return openCount > closedCount;
@ -104,8 +121,15 @@
function handleKeyDown(event: KeyboardEvent, kalk: Kalk) {
if (event.key == "Enter") {
if (
hasUnevenAmountOfBraces(
(event.target as HTMLTextAreaElement).value
hasUnevenAmountOfBrackets(
(event.target as HTMLTextAreaElement).value,
"{",
"}"
) ||
hasUnevenAmountOfBrackets(
(event.target as HTMLTextAreaElement).value,
"[",
"]"
)
) {
return;
@ -127,13 +151,13 @@
const [result, success] = calculate(kalk, input);
output = success
? highlight(result, true)[0]
? highlight(result, HighlightType.Output)[0]
: `<span style="color: ${errorcolor}">${result}</span>`;
}
// Highlight
const target = event.target as HTMLInputElement;
setText(target.value);
setText(target.value, true);
outputLines = output
? [...outputLines, [getHtml(), true], [output, false]]
@ -276,7 +300,7 @@
function highlight(
input: string,
isOutput: boolean = false
highlightType: HighlightType
): [string, number] {
if (!input) return ["", 0];
let result = input;
@ -316,9 +340,38 @@
if (substring.startsWith("\n")) {
if (substring.endsWith("}")) {
return "<br />}";
} else if (substring.endsWith("]")) {
return "<br />]";
} else {
if (!substring.match(/\n\s\s/)) offset += 2;
return isOutput ? "<br />" : "<br />&nbsp;&nbsp;";
let spaceCount = 2;
const unclosedBracketInFirstLine =
hasUnevenAmountOfBrackets(
input,
"[",
"]",
true
);
if (unclosedBracketInFirstLine) {
let bracketIndex = input.indexOf("[");
spaceCount =
bracketIndex == -1
? spaceCount
: bracketIndex + 1;
}
if (!substring.match(/\n\s/)) offset += spaceCount;
if (highlightType == HighlightType.Output) {
return "<br />";
} else if (
highlightType == HighlightType.InputField
) {
return "<br />" + "&nbsp;".repeat(spaceCount);
} else if (highlightType == HighlightType.History) {
// Account for ">> "
return (
"<br />" + "&nbsp;".repeat(spaceCount + 3)
);
}
}
}
if (substring.match(/\s+/)) {
@ -326,7 +379,7 @@
}
}
if (op && !isOutput) {
if (op && highlightType != HighlightType.Output) {
if (substring == "*") return "⋅";
if (substring == "/") return "÷";
}