mirror of
https://github.com/PaddiM8/kalker.git
synced 2025-06-23 19:21:26 +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()),
|
Box::new(expr.clone()),
|
||||||
));
|
));
|
||||||
|
|
||||||
Ok(KalkNum::new(
|
let num = eval_expr(context, &unit_def, "")?;
|
||||||
eval_expr(context, &unit_def, "")?.value,
|
Ok(KalkNum::new_with_imaginary(
|
||||||
|
num.value,
|
||||||
to_unit.into(),
|
to_unit.into(),
|
||||||
|
num.imaginary_value,
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
Err(CalcError::InvalidUnit)
|
Err(CalcError::InvalidUnit)
|
||||||
@ -273,6 +275,23 @@ pub(crate) fn eval_fn_call_expr(
|
|||||||
let prelude_func = match expressions.len() {
|
let prelude_func = match expressions.len() {
|
||||||
1 => {
|
1 => {
|
||||||
let x = eval_expr(context, &expressions[0], "")?;
|
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 {
|
if identifier.prime_count > 0 {
|
||||||
return calculus::derive_func(context, &identifier, x);
|
return calculus::derive_func(context, &identifier, x);
|
||||||
} else {
|
} else {
|
||||||
|
@ -54,12 +54,24 @@ pub struct ScientificNotation {
|
|||||||
pub negative: bool,
|
pub negative: bool,
|
||||||
pub(crate) digits: String,
|
pub(crate) digits: String,
|
||||||
pub exponent: i32,
|
pub exponent: i32,
|
||||||
|
pub imaginary: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
pub enum ComplexNumberType {
|
||||||
|
Real,
|
||||||
|
Imaginary,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
impl ScientificNotation {
|
impl ScientificNotation {
|
||||||
#[wasm_bindgen(js_name = toString)]
|
#[wasm_bindgen(js_name = toString)]
|
||||||
pub fn to_string(&self) -> String {
|
pub fn to_string(&self) -> String {
|
||||||
|
if self.digits == "" {
|
||||||
|
return String::from("0");
|
||||||
|
}
|
||||||
|
|
||||||
let sign = if self.negative { "-" } else { "" };
|
let sign = if self.negative { "-" } else { "" };
|
||||||
let mut digits_and_mul = if self.digits == "1" {
|
let mut digits_and_mul = if self.digits == "1" {
|
||||||
String::new()
|
String::new()
|
||||||
@ -71,33 +83,106 @@ impl ScientificNotation {
|
|||||||
digits_and_mul.insert(1usize, '.');
|
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 {
|
impl KalkNum {
|
||||||
pub fn to_scientific_notation(&self) -> ScientificNotation {
|
pub fn to_scientific_notation(
|
||||||
let value_string = self.to_string();
|
&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(".") {
|
let trimmed = if value_string.contains(".") {
|
||||||
value_string.trim_end_matches("0")
|
value_string.trim_end_matches("0")
|
||||||
} else {
|
} else {
|
||||||
&value_string
|
&value_string
|
||||||
};
|
};
|
||||||
|
let value = match complex_number_type {
|
||||||
|
ComplexNumberType::Real => self.value.clone(),
|
||||||
|
ComplexNumberType::Imaginary => self.imaginary_value.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
ScientificNotation {
|
ScientificNotation {
|
||||||
negative: self.value < 0f64,
|
negative: value < 0f64,
|
||||||
digits: trimmed
|
digits: trimmed
|
||||||
.to_string()
|
.to_string()
|
||||||
.replace(".", "")
|
.replace(".", "")
|
||||||
.trim_start_matches("0")
|
.trim_start_matches("0")
|
||||||
.to_string(),
|
.to_string(),
|
||||||
// I... am not sure what else to do...
|
// 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 {
|
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 {
|
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 {
|
pub(crate) fn add(self, context: &mut crate::interpreter::Context, rhs: KalkNum) -> KalkNum {
|
||||||
let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs);
|
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 {
|
pub(crate) fn sub(self, context: &mut crate::interpreter::Context, rhs: KalkNum) -> KalkNum {
|
||||||
let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs);
|
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 {
|
pub(crate) fn mul(self, context: &mut crate::interpreter::Context, rhs: KalkNum) -> KalkNum {
|
||||||
let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs);
|
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 {
|
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() {
|
if left.has_unit() && right.has_unit() {
|
||||||
right.convert_to_unit(context, &left.unit)
|
right.convert_to_unit(context, &left.unit)
|
||||||
} else {
|
} 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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use crate::kalk_num::ComplexNumberType;
|
||||||
use crate::kalk_num::KalkNum;
|
use crate::kalk_num::KalkNum;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -372,12 +474,12 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_to_scientific_notation() {
|
fn test_to_scientific_notation() {
|
||||||
let num = KalkNum::from(0.000001f64);
|
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.negative, false);
|
||||||
assert_eq!(sci_not.exponent, -6);
|
assert_eq!(sci_not.exponent, -6);
|
||||||
|
|
||||||
let num = KalkNum::from(123.456789f64);
|
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.negative, false);
|
||||||
assert_eq!(sci_not.exponent, 2);
|
assert_eq!(sci_not.exponent, 2);
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ use wasm_bindgen::prelude::*;
|
|||||||
pub struct KalkNum {
|
pub struct KalkNum {
|
||||||
pub(crate) value: f64,
|
pub(crate) value: f64,
|
||||||
pub(crate) unit: String,
|
pub(crate) unit: String,
|
||||||
|
pub(crate) imaginary_value: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
@ -14,6 +15,15 @@ impl KalkNum {
|
|||||||
Self {
|
Self {
|
||||||
value,
|
value,
|
||||||
unit: unit.to_string(),
|
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 struct KalkNum {
|
||||||
pub(crate) value: Float,
|
pub(crate) value: Float,
|
||||||
pub(crate) unit: String,
|
pub(crate) unit: String,
|
||||||
|
pub(crate) imaginary_value: Float,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KalkNum {
|
impl KalkNum {
|
||||||
@ -17,6 +18,15 @@ impl KalkNum {
|
|||||||
Self {
|
Self {
|
||||||
value,
|
value,
|
||||||
unit: unit.to_string(),
|
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)
|
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 {
|
pub fn to_i32(&self) -> i32 {
|
||||||
self.value.to_i32_saturating().unwrap()
|
self.value.to_i32_saturating().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_string(&self) -> String {
|
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(".") {
|
if self.imaginary_value != 0 {
|
||||||
as_str
|
let imaginary_as_str = trim_number_string(&self.imaginary_to_f64().to_string());
|
||||||
.trim_end_matches('0')
|
let sign = if self.imaginary_value < 0 { "-" } else { "+" };
|
||||||
.trim_end_matches('.')
|
|
||||||
.to_string()
|
format!("{} {} {}i", as_str, sign, imaginary_as_str)
|
||||||
} else {
|
} else {
|
||||||
as_str
|
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 {
|
pub fn get_unit(&self) -> &str {
|
||||||
&self.unit
|
&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 {
|
impl From<f64> for KalkNum {
|
||||||
fn from(x: f64) -> Self {
|
fn from(x: f64) -> Self {
|
||||||
KalkNum::new(Float::with_val(63, x), "")
|
KalkNum::new(Float::with_val(63, x), "")
|
||||||
|
@ -2,6 +2,7 @@ use lazy_static::lazy_static;
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use FuncType::*;
|
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";
|
pub const INIT: &'static str = "unit deg = (rad*180)/pi";
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::{ast::Stmt, prelude};
|
use crate::{ast::Expr, ast::Identifier, ast::Stmt, prelude};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -9,10 +9,21 @@ pub struct SymbolTable {
|
|||||||
|
|
||||||
impl SymbolTable {
|
impl SymbolTable {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
SymbolTable {
|
let mut symbol_table = SymbolTable {
|
||||||
hashmap: HashMap::new(),
|
hashmap: HashMap::new(),
|
||||||
unit_types: 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 {
|
pub fn insert(&mut self, value: Stmt) -> &mut Self {
|
||||||
@ -72,6 +83,7 @@ impl SymbolTable {
|
|||||||
|
|
||||||
pub fn contains_var(&self, identifier: &str) -> bool {
|
pub fn contains_var(&self, identifier: &str) -> bool {
|
||||||
prelude::CONSTANTS.contains_key(identifier)
|
prelude::CONSTANTS.contains_key(identifier)
|
||||||
|
|| identifier == "i"
|
||||||
|| self.hashmap.contains_key(&format!("var.{}", identifier))
|
|| 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) {
|
pub fn eval(parser: &mut parser::Context, input: &str, precision: u32) {
|
||||||
match parser::eval(parser, input, precision) {
|
match parser::eval(parser, input, precision) {
|
||||||
Ok(Some(result)) => {
|
Ok(Some(result)) => println!("{}", result.to_string_pretty()),
|
||||||
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(None) => print!(""),
|
Ok(None) => print!(""),
|
||||||
Err(err) => print_err(&err.to_string()),
|
Err(err) => print_err(&err.to_string()),
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user