mirror of
https://github.com/PaddiM8/kalker.git
synced 2025-06-23 03:01:22 +02:00
Basics of complex numbers
This commit is contained in:
parent
869708b454
commit
2936a58620
@ -212,9 +212,11 @@ pub fn convert_unit(
|
||||
Box::new(expr.clone()),
|
||||
));
|
||||
|
||||
Ok(KalkNum::new(
|
||||
eval_expr(context, &unit_def, "")?.value,
|
||||
let num = eval_expr(context, &unit_def, "")?;
|
||||
Ok(KalkNum::new_with_imaginary(
|
||||
num.value,
|
||||
to_unit.into(),
|
||||
num.imaginary_value,
|
||||
))
|
||||
} else {
|
||||
Err(CalcError::InvalidUnit)
|
||||
@ -273,6 +275,23 @@ pub(crate) fn eval_fn_call_expr(
|
||||
let prelude_func = match expressions.len() {
|
||||
1 => {
|
||||
let x = eval_expr(context, &expressions[0], "")?;
|
||||
|
||||
if x.value < 0f64 && (identifier.full_name == "sqrt" || identifier.full_name == "√") {
|
||||
let (sqrt, unit) = prelude::call_unary_func(
|
||||
context,
|
||||
&identifier.full_name,
|
||||
x.value * (-1f64),
|
||||
&context.angle_unit.clone(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
return Ok(KalkNum::new_with_imaginary(
|
||||
KalkNum::default().value,
|
||||
&unit,
|
||||
sqrt,
|
||||
));
|
||||
}
|
||||
|
||||
if identifier.prime_count > 0 {
|
||||
return calculus::derive_func(context, &identifier, x);
|
||||
} else {
|
||||
|
@ -54,12 +54,24 @@ pub struct ScientificNotation {
|
||||
pub negative: bool,
|
||||
pub(crate) digits: String,
|
||||
pub exponent: i32,
|
||||
pub imaginary: bool,
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
#[derive(PartialEq)]
|
||||
pub enum ComplexNumberType {
|
||||
Real,
|
||||
Imaginary,
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
impl ScientificNotation {
|
||||
#[wasm_bindgen(js_name = toString)]
|
||||
pub fn to_string(&self) -> String {
|
||||
if self.digits == "" {
|
||||
return String::from("0");
|
||||
}
|
||||
|
||||
let sign = if self.negative { "-" } else { "" };
|
||||
let mut digits_and_mul = if self.digits == "1" {
|
||||
String::new()
|
||||
@ -71,33 +83,106 @@ impl ScientificNotation {
|
||||
digits_and_mul.insert(1usize, '.');
|
||||
}
|
||||
|
||||
format!("{}{}10^{}", sign, digits_and_mul, self.exponent - 1)
|
||||
format!(
|
||||
"{}{}10^{} {}",
|
||||
sign,
|
||||
digits_and_mul,
|
||||
self.exponent - 1,
|
||||
if self.imaginary { "i" } else { "" }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl KalkNum {
|
||||
pub fn to_scientific_notation(&self) -> ScientificNotation {
|
||||
let value_string = self.to_string();
|
||||
pub fn to_scientific_notation(
|
||||
&self,
|
||||
complex_number_type: ComplexNumberType,
|
||||
) -> ScientificNotation {
|
||||
let value_string = match complex_number_type {
|
||||
ComplexNumberType::Real => self.to_string_real(),
|
||||
ComplexNumberType::Imaginary => self.to_string_imaginary(false),
|
||||
};
|
||||
let trimmed = if value_string.contains(".") {
|
||||
value_string.trim_end_matches("0")
|
||||
} else {
|
||||
&value_string
|
||||
};
|
||||
let value = match complex_number_type {
|
||||
ComplexNumberType::Real => self.value.clone(),
|
||||
ComplexNumberType::Imaginary => self.imaginary_value.clone(),
|
||||
};
|
||||
|
||||
ScientificNotation {
|
||||
negative: self.value < 0f64,
|
||||
negative: value < 0f64,
|
||||
digits: trimmed
|
||||
.to_string()
|
||||
.replace(".", "")
|
||||
.trim_start_matches("0")
|
||||
.to_string(),
|
||||
// I... am not sure what else to do...
|
||||
exponent: KalkNum::new(self.value.clone().log10(), "").to_i32(),
|
||||
exponent: KalkNum::new(value.clone().abs().log10(), "").to_i32(),
|
||||
imaginary: complex_number_type == ComplexNumberType::Imaginary,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_string_big(&self) -> String {
|
||||
self.value.to_string()
|
||||
if self.imaginary_value == 0 {
|
||||
self.value.to_string()
|
||||
} else {
|
||||
let sign = if self.imaginary_value < 0 { "-" } else { "+" };
|
||||
format!(
|
||||
"{} {} {}",
|
||||
self.value.to_string(),
|
||||
sign,
|
||||
self.imaginary_value.to_string()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_string_pretty(&self) -> String {
|
||||
let sci_notation_real = self.to_scientific_notation(ComplexNumberType::Real);
|
||||
let result_str = if (-6..8).contains(&sci_notation_real.exponent) || self.value == 0f64 {
|
||||
self.to_string_real()
|
||||
} else {
|
||||
sci_notation_real.to_string()
|
||||
};
|
||||
|
||||
let sci_notation_imaginary = self.to_scientific_notation(ComplexNumberType::Imaginary);
|
||||
let result_str_imaginary = if (-6..8).contains(&sci_notation_imaginary.exponent)
|
||||
|| self.imaginary_value == 0f64
|
||||
|| self.imaginary_value == 1f64
|
||||
{
|
||||
self.to_string_imaginary(true)
|
||||
} else {
|
||||
format!("{} i", sci_notation_imaginary.to_string())
|
||||
};
|
||||
|
||||
let mut output = result_str;
|
||||
if self.imaginary_value != 0f64 {
|
||||
// If the real value is 0, and there is an imaginary one,
|
||||
// clear the output so that the real value is not shown.
|
||||
if output == "0" {
|
||||
output = String::new();
|
||||
}
|
||||
if output.len() > 0 {
|
||||
output.push_str(&format!(
|
||||
" {} ",
|
||||
if self.imaginary_value < 0 { "-" } else { "+" }
|
||||
));
|
||||
}
|
||||
output.push_str(&format!("{}", result_str_imaginary));
|
||||
}
|
||||
|
||||
let unit = self.get_unit();
|
||||
if unit != "" {
|
||||
output.push_str(&format!(" {}", unit));
|
||||
}
|
||||
|
||||
if let Some(estimate) = self.estimate() {
|
||||
output.push_str(&format!(" ≈ {}", estimate));
|
||||
}
|
||||
|
||||
output
|
||||
}
|
||||
|
||||
pub fn is_too_big(&self) -> bool {
|
||||
@ -133,17 +218,29 @@ impl KalkNum {
|
||||
|
||||
pub(crate) fn add(self, context: &mut crate::interpreter::Context, rhs: KalkNum) -> KalkNum {
|
||||
let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs);
|
||||
KalkNum::new(self.value + right.value, &right.unit)
|
||||
KalkNum::new_with_imaginary(
|
||||
self.value + right.value,
|
||||
&right.unit,
|
||||
self.imaginary_value + right.imaginary_value,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn sub(self, context: &mut crate::interpreter::Context, rhs: KalkNum) -> KalkNum {
|
||||
let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs);
|
||||
KalkNum::new(self.value - right.value, &right.unit)
|
||||
KalkNum::new_with_imaginary(
|
||||
self.value - right.value,
|
||||
&right.unit,
|
||||
self.imaginary_value - right.imaginary_value,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn mul(self, context: &mut crate::interpreter::Context, rhs: KalkNum) -> KalkNum {
|
||||
let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs);
|
||||
KalkNum::new(self.value * right.value, &right.unit)
|
||||
KalkNum::new_with_imaginary(
|
||||
KalkNum::default().value,
|
||||
&right.unit,
|
||||
self.value * right.imaginary_value + self.imaginary_value * right.value,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn div(self, context: &mut crate::interpreter::Context, rhs: KalkNum) -> KalkNum {
|
||||
@ -258,7 +355,11 @@ fn calculate_unit(
|
||||
if left.has_unit() && right.has_unit() {
|
||||
right.convert_to_unit(context, &left.unit)
|
||||
} else {
|
||||
Some(KalkNum::new(right.value, &left.unit))
|
||||
Some(KalkNum::new_with_imaginary(
|
||||
right.value,
|
||||
&left.unit,
|
||||
right.imaginary_value,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@ -293,6 +394,7 @@ impl Into<f64> for KalkNum {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::kalk_num::ComplexNumberType;
|
||||
use crate::kalk_num::KalkNum;
|
||||
|
||||
#[test]
|
||||
@ -372,12 +474,12 @@ mod tests {
|
||||
#[test]
|
||||
fn test_to_scientific_notation() {
|
||||
let num = KalkNum::from(0.000001f64);
|
||||
let sci_not = num.to_scientific_notation();
|
||||
let sci_not = num.to_scientific_notation(ComplexNumberType::Real);
|
||||
assert_eq!(sci_not.negative, false);
|
||||
assert_eq!(sci_not.exponent, -6);
|
||||
|
||||
let num = KalkNum::from(123.456789f64);
|
||||
let sci_not = num.to_scientific_notation();
|
||||
let sci_not = num.to_scientific_notation(ComplexNumberType::Real);
|
||||
assert_eq!(sci_not.negative, false);
|
||||
assert_eq!(sci_not.exponent, 2);
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ use wasm_bindgen::prelude::*;
|
||||
pub struct KalkNum {
|
||||
pub(crate) value: f64,
|
||||
pub(crate) unit: String,
|
||||
pub(crate) imaginary_value: f64,
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
@ -14,6 +15,15 @@ impl KalkNum {
|
||||
Self {
|
||||
value,
|
||||
unit: unit.to_string(),
|
||||
imaginary_value: 0f64,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_with_imaginary(value: f64, unit: &str, imaginary_value: f64) -> Self {
|
||||
Self {
|
||||
value,
|
||||
unit: unit.to_string(),
|
||||
imaginary_value,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@ impl Default for KalkNum {
|
||||
pub struct KalkNum {
|
||||
pub(crate) value: Float,
|
||||
pub(crate) unit: String,
|
||||
pub(crate) imaginary_value: Float,
|
||||
}
|
||||
|
||||
impl KalkNum {
|
||||
@ -17,6 +18,15 @@ impl KalkNum {
|
||||
Self {
|
||||
value,
|
||||
unit: unit.to_string(),
|
||||
imaginary_value: Float::with_val(63, 0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_with_imaginary(value: Float, unit: &str, imaginary_value: Float) -> Self {
|
||||
Self {
|
||||
value,
|
||||
unit: unit.to_string(),
|
||||
imaginary_value,
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,23 +34,43 @@ impl KalkNum {
|
||||
self.value.to_f64_round(rug::float::Round::Nearest)
|
||||
}
|
||||
|
||||
pub fn imaginary_to_f64(&self) -> f64 {
|
||||
self.imaginary_value
|
||||
.to_f64_round(rug::float::Round::Nearest)
|
||||
}
|
||||
|
||||
pub fn to_i32(&self) -> i32 {
|
||||
self.value.to_i32_saturating().unwrap()
|
||||
}
|
||||
|
||||
pub fn to_string(&self) -> String {
|
||||
let as_str = self.to_f64().to_string();
|
||||
let as_str = trim_number_string(&self.to_f64().to_string());
|
||||
|
||||
if as_str.contains(".") {
|
||||
as_str
|
||||
.trim_end_matches('0')
|
||||
.trim_end_matches('.')
|
||||
.to_string()
|
||||
if self.imaginary_value != 0 {
|
||||
let imaginary_as_str = trim_number_string(&self.imaginary_to_f64().to_string());
|
||||
let sign = if self.imaginary_value < 0 { "-" } else { "+" };
|
||||
|
||||
format!("{} {} {}i", as_str, sign, imaginary_as_str)
|
||||
} else {
|
||||
as_str
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_string_real(&self) -> String {
|
||||
trim_number_string(&self.to_f64().to_string())
|
||||
}
|
||||
|
||||
pub fn to_string_imaginary(&self, include_i: bool) -> String {
|
||||
let value = trim_number_string(&self.imaginary_to_f64().to_string());
|
||||
if include_i && value == "1" {
|
||||
String::from("i")
|
||||
} else if include_i {
|
||||
format!("{}i", value)
|
||||
} else {
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_unit(&self) -> &str {
|
||||
&self.unit
|
||||
}
|
||||
@ -51,6 +81,17 @@ impl KalkNum {
|
||||
}
|
||||
}
|
||||
|
||||
fn trim_number_string(input: &str) -> String {
|
||||
if input.contains(".") {
|
||||
input
|
||||
.trim_end_matches('0')
|
||||
.trim_end_matches('.')
|
||||
.to_string()
|
||||
} else {
|
||||
input.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f64> for KalkNum {
|
||||
fn from(x: f64) -> Self {
|
||||
KalkNum::new(Float::with_val(63, x), "")
|
||||
|
@ -2,6 +2,7 @@ use lazy_static::lazy_static;
|
||||
use std::collections::HashMap;
|
||||
use FuncType::*;
|
||||
|
||||
// `i` is added in the symbol_table module, since for some reason it didn't work here.
|
||||
pub const INIT: &'static str = "unit deg = (rad*180)/pi";
|
||||
|
||||
lazy_static! {
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::{ast::Stmt, prelude};
|
||||
use crate::{ast::Expr, ast::Identifier, ast::Stmt, prelude};
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -9,10 +9,21 @@ pub struct SymbolTable {
|
||||
|
||||
impl SymbolTable {
|
||||
pub fn new() -> Self {
|
||||
SymbolTable {
|
||||
let mut symbol_table = SymbolTable {
|
||||
hashmap: HashMap::new(),
|
||||
unit_types: HashMap::new(),
|
||||
}
|
||||
};
|
||||
|
||||
// i = sqrt(-1)
|
||||
symbol_table.insert(Stmt::VarDecl(
|
||||
Identifier::from_full_name("i"),
|
||||
Box::new(Expr::FnCall(
|
||||
Identifier::from_full_name("sqrt"),
|
||||
vec![Expr::Literal(-1f64)],
|
||||
)),
|
||||
));
|
||||
|
||||
symbol_table
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, value: Stmt) -> &mut Self {
|
||||
@ -72,6 +83,7 @@ impl SymbolTable {
|
||||
|
||||
pub fn contains_var(&self, identifier: &str) -> bool {
|
||||
prelude::CONSTANTS.contains_key(identifier)
|
||||
|| identifier == "i"
|
||||
|| self.hashmap.contains_key(&format!("var.{}", identifier))
|
||||
}
|
||||
|
||||
|
@ -4,27 +4,7 @@ use kalk::parser;
|
||||
|
||||
pub fn eval(parser: &mut parser::Context, input: &str, precision: u32) {
|
||||
match parser::eval(parser, input, precision) {
|
||||
Ok(Some(result)) => {
|
||||
let sci_notation = result.to_scientific_notation();
|
||||
let result_str = if sci_notation.exponent > 8 || sci_notation.exponent < -6 {
|
||||
sci_notation.to_string()
|
||||
} else if precision == DEFAULT_PRECISION {
|
||||
result.to_string()
|
||||
} else {
|
||||
result.to_string_big()
|
||||
};
|
||||
|
||||
let unit = result.get_unit();
|
||||
if let Some(estimate) = result.estimate() {
|
||||
if unit == "" {
|
||||
println!("{} ≈ {}", result_str, estimate);
|
||||
} else {
|
||||
println!("{} {} ≈ {}", result_str, unit, estimate);
|
||||
}
|
||||
} else {
|
||||
println!("{} {}", result_str, unit);
|
||||
}
|
||||
}
|
||||
Ok(Some(result)) => println!("{}", result.to_string_pretty()),
|
||||
Ok(None) => print!(""),
|
||||
Err(err) => print_err(&err.to_string()),
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user