From 09996801d818eed2b81565d1861e31529058bcd7 Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 17 Aug 2023 10:32:05 -0700 Subject: [PATCH 01/30] Updated TODO --- TODO.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/TODO.md b/TODO.md index 470cf3d..b5e36e0 100644 --- a/TODO.md +++ b/TODO.md @@ -2,9 +2,9 @@ - update Cargo.toml - run cargo test - commit - - git tag -a v1.0.0 -m "Version 1.0.0" - - git push - - git push origin v1.0.0 + - push + - merge + - git tag -a v1.0.0 -m "Version 1.0.0" on merge commit - cargo publish - Update packages From b846a7c144a475904362c1c924b712dd7234c048 Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 17 Aug 2023 12:49:29 -0700 Subject: [PATCH 02/30] Fixed help art --- src/formattedtext.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/formattedtext.rs b/src/formattedtext.rs index 0c1bf46..68de1ef 100644 --- a/src/formattedtext.rs +++ b/src/formattedtext.rs @@ -61,10 +61,12 @@ fn format_map_ansi(c: char) -> Option { } +// style::reset also resets color. +// Make sure color comes AFTER style reset. fn format_map_full(c: char) -> Option { Some(match c { 'n' => { // Normal text - format!("{}{}", color::Fg(color::Reset), style::Reset) + format!("{}{}", style::Reset, color::Fg(color::Reset)) }, 'i' => { // Normal italic text format!("{}{}", color::Fg(color::Reset), style::Italic) @@ -73,7 +75,7 @@ fn format_map_full(c: char) -> Option { format!("{}{}", color::Fg(color::Magenta), style::Bold) }, 'a' => { // Colored text - format!("{}{}", color::Fg(color::Magenta), style::Reset) + format!("{}{}", style::Reset, color::Fg(color::Magenta)) }, 'e' => { // Error titles format!("{}{}", color::Fg(color::Red), style::Bold) From cc81c3979c66270ad2e47d2d1e587c80ab430f06 Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 17 Aug 2023 16:38:02 -0700 Subject: [PATCH 03/30] Fixed mod bug --- src/evaluate/operator.rs | 5 +++++ src/quantity/scalar/floatbase.rs | 2 +- src/quantity/scalar/scalar.rs | 2 +- src/tests.rs | 1 + 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/evaluate/operator.rs b/src/evaluate/operator.rs index bc77de4..1268554 100644 --- a/src/evaluate/operator.rs +++ b/src/evaluate/operator.rs @@ -162,6 +162,11 @@ pub fn eval_operator(context: &mut Context, g: &Expression) -> Result for FloatBase { (!modulus.fract().unwrap().is_zero()) } { panic!() } - FloatBase{val : self.val.fract() % modulus.val.fract()} + FloatBase{val : self.val.trunc() % modulus.val.trunc()} } } diff --git a/src/quantity/scalar/scalar.rs b/src/quantity/scalar/scalar.rs index 514e575..756e5bb 100644 --- a/src/quantity/scalar/scalar.rs +++ b/src/quantity/scalar/scalar.rs @@ -186,7 +186,7 @@ impl Scalar { pub fn is_nan(&self) -> bool { match self { Scalar::Float {v} => {v.val.is_nan()}, - Scalar::Rational {..} => {panic!()} + Scalar::Rational {..} => {false} } } diff --git a/src/tests.rs b/src/tests.rs index 0f41c8d..cf30e25 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -162,6 +162,7 @@ fn operators() { good_expr("2", "6/3"); good_expr("2", "5%3"); + good_expr("4", "2^5 mod 7"); good_expr("8", "5+3"); good_expr("64", "4^3"); good_expr("64", "4 ^ 3"); From 86b5356d4f1f7c887d7d2fba759efbdf6a47c194 Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 17 Aug 2023 16:38:17 -0700 Subject: [PATCH 04/30] Updated TODO --- TODO.md | 1 + 1 file changed, 1 insertion(+) diff --git a/TODO.md b/TODO.md index b5e36e0..f09cb5b 100644 --- a/TODO.md +++ b/TODO.md @@ -7,6 +7,7 @@ - git tag -a v1.0.0 -m "Version 1.0.0" on merge commit - cargo publish - Update packages + - Faster startup From f9ec4d82fe93fa8be3119e34ff7498d8d26abbcc Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 17 Aug 2023 17:31:43 -0700 Subject: [PATCH 05/30] Added a test --- src/tests.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tests.rs b/src/tests.rs index cf30e25..51342b3 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -185,6 +185,7 @@ fn operators() { bad_expr("1e5!"); bad_expr("0^(-1)"); bad_expr("pi!"); + bad_expr("2.5 mod 8"); } #[test] From 6e3609d85e1a5dd4c330b5800949e3514c8e1b93 Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 17 Aug 2023 17:33:54 -0700 Subject: [PATCH 06/30] Updated TODO --- TODO.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TODO.md b/TODO.md index f09cb5b..0c6c007 100644 --- a/TODO.md +++ b/TODO.md @@ -7,8 +7,6 @@ - git tag -a v1.0.0 -m "Version 1.0.0" on merge commit - cargo publish - Update packages - - Faster startup - ## Pre-release @@ -31,6 +29,7 @@ - evaluate straight from command line - Auto-push to crates.io - Package for debian + - Faster startup @@ -49,6 +48,7 @@ - Complex numbers - acot/acoth functions - Sums and products with functional arguments + - Add functions: gcd, inverse mod ## Prompt - Live syntax/output (like firefox js terminal) From 6969d17cce166e361880c5d8f872006f6140453e Mon Sep 17 00:00:00 2001 From: Mark Date: Sun, 20 Aug 2023 16:19:57 -0700 Subject: [PATCH 07/30] Added substitution config switch --- src/context.rs | 4 +- src/entrypoint/unix/promptbuffer.rs | 56 +++++--------- src/parser/mod.rs | 42 +++++++---- src/parser/stage/find_subs.rs | 109 ++++++++++++++++------------ 4 files changed, 110 insertions(+), 101 deletions(-) diff --git a/src/context.rs b/src/context.rs index b1fef92..484d61b 100644 --- a/src/context.rs +++ b/src/context.rs @@ -19,7 +19,7 @@ pub struct Config { // with prettier unicode alternatives? // // Automatically disabled if enable_unicode is off. - //pub enable_substituion: bool, + pub enable_substituion: bool, // Should we print simple powers // as unicode superscript chars? @@ -38,7 +38,7 @@ impl Config { pub fn new() -> Config { Config{ term_color_type: 2, - //enable_substituion: true, + enable_substituion: true, //enable_unicode: true, enable_super_powers: true, enable_one_over_power: true diff --git a/src/entrypoint/unix/promptbuffer.rs b/src/entrypoint/unix/promptbuffer.rs index 2b711ab..7077bca 100644 --- a/src/entrypoint/unix/promptbuffer.rs +++ b/src/entrypoint/unix/promptbuffer.rs @@ -38,39 +38,23 @@ impl PromptBuffer { // Same as write_primpt, but pretends there is no cursor pub fn write_prompt_nocursor(&mut self, context: &Context, stdout: &mut RawTerminal) -> Result<(), std::io::Error> { - // Draw prettyprinted expression - let (_, s) = substitute_cursor(context, &self.get_contents(), self.buffer.chars().count()); - - write!( - stdout, "\r{}{}==>{}{} {}", - style::Bold, - color::Fg(color::Blue), - color::Fg(color::Reset), - style::Reset, - s - )?; - - // If this string is shorter, clear the remaining old one. - if s.chars().count() < self.last_print_len { - write!( - stdout, "{}{}", - " ".repeat(self.last_print_len - s.chars().count()), - termion::cursor::Left((self.last_print_len - s.chars().count()) as u16) - )?; - } - - self.last_print_len = s.chars().count(); - stdout.flush()?; - return Ok(()); + let tmp = self.cursor; + self.cursor = 0; + let r = self.write_prompt(context, stdout); + self.cursor = tmp; + return r; } pub fn write_prompt(&mut self, context: &Context, stdout: &mut RawTerminal) -> Result<(), std::io::Error> { let l = self.buffer.chars().count(); let i = if l == 0 {0} else {l - self.cursor}; - // Draw prettyprinted expression - let (display_cursor, s) = substitute_cursor(context, &self.get_contents(), i); + //println!("{i} {}", self.cursor); + + // Draw prettyprinted expression + let (display_c, s) = substitute_cursor(context, &self.get_contents(), i); + write!( stdout, "\r{}{}==>{}{} {}", style::Bold, @@ -82,22 +66,18 @@ impl PromptBuffer { // If this string is shorter, clear the remaining old one. if s.chars().count() < self.last_print_len { - write!( - stdout, "{}{}", - " ".repeat(self.last_print_len - s.chars().count()), - termion::cursor::Left((self.last_print_len - s.chars().count()) as u16) - )?; - } - - - // Move cursor to correct position - if display_cursor != 0 { write!( stdout, "{}", - termion::cursor::Left(display_cursor as u16) + " ".repeat(self.last_print_len - s.chars().count()), )?; - stdout.flush()?; } + + + write!( + stdout, "\r{}", + termion::cursor::Right((display_c + 4) as u16) + )?; + stdout.flush()?; self.last_print_len = s.chars().count(); stdout.flush()?; diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 32c608d..56f4eef 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -21,8 +21,10 @@ pub fn parse( context: &Context, s: &String ) -> Result { - let expressions = stage::tokenize(context, s); - let (_, expressions) = stage::find_subs(expressions); + let mut expressions = stage::tokenize(context, s); + if context.config.enable_substituion { + (_, expressions) = stage::find_subs(expressions); + } let g = stage::groupify(context, expressions)?; let g = stage::treeify(context, g)?; @@ -34,6 +36,7 @@ pub fn parse_no_context(s: &String) -> Result String { + if !context.config.enable_substituion { return s.clone(); } let (_, s) = substitute_cursor(context, s, s.chars().count()); return s; } @@ -43,34 +46,45 @@ pub fn substitute_cursor( s: &String, // The string to substitute c: usize // Location of the cursor right now ) -> ( - usize, // Location of cursor in substituted string + usize, // New cursor String // String with substitutions ) { + + if !context.config.enable_substituion { return (c, s.clone()); } if s == "" { return (c, s.clone()) } + + let mut new_s = s.clone(); - let l = s.chars().count(); let expressions = stage::tokenize(context, s); let (mut subs, _) = stage::find_subs(expressions); - let mut new_c = l - c; + let mut new_c = c.clone(); while subs.len() > 0 { - let r = subs.pop_back().unwrap(); // Apply substitutions in reverse order + // r is the current substitution: (linelocation, string) + let r = subs.pop_back().unwrap(); if { // Don't substitute if our cursor is inside the substitution c >= r.0.pos && c < r.0.pos+r.0.len } { continue; } - if c < r.0.pos { - let ct = r.1.chars().count(); - if ct >= r.0.len { - if new_c >= ct - r.0.len { - new_c += ct - r.0.len - } - } else { - new_c -= r.0.len - ct + // If this substitution is before our cursor, + // we need to adjust our cursor's position. + if c > r.0.pos { + let c_o = r.0.len; // Old length + let c_n = r.1.chars().count(); // New length + + if c_n > c_o { + // Move cursor right by difference + new_c += c_n - c_o; + + } else if c_n < c_o { + // Move cursor left by difference + if new_c >= c_o - c_n { + new_c -= c_o - c_n; + } else { new_c = 0; } } } diff --git a/src/parser/stage/find_subs.rs b/src/parser/stage/find_subs.rs index 058cf54..865f407 100644 --- a/src/parser/stage/find_subs.rs +++ b/src/parser/stage/find_subs.rs @@ -6,6 +6,64 @@ use super::super::{ }; +fn sub_string(s: &str) -> Option<&'static str> { + let r = match s { + + /* Only found in operator tokens */ + + "*" => "×", + "/" => "÷", + "sqrt" => "√", + "rt" => "√", + + + + /* Only found in word tokens */ + + // Greek letters + "alpha" => "α", + "beta" => "β", + "gamma" => "γ", + "delta" => "δ", + "epsilon" => "ε", + "zeta" => "ζ", + "eta" => "η", + "theta" => "θ", + //"iota" => {Some("ι")}, // looks just like i + //"kappa" => {Some("κ")}, // looks just like k + "lambda" => "λ", + "mu" => "μ", + //"nu" => {Some("ν")}, // looks just like v + "xi" => "ξ", + //"omicron" => {Some("ο")}, // looks exactly like o + "pi" => "π", + "rho" => "ρ", + "sigma" => "σ", + "tau" => "τ", + //"upsilon" => {Some("υ")}, // looks just like u + "phi" => "φ", + "chi" => "χ", + //"psi" => {Some("ψ")}, Conflict with pound / square inch + "omega" => "ω", + + // Constants + "epsilon_zero" => "ε₀", + "eps_zero" => "ε₀", + "g_zero" => "g₀", + "mu_zero" => "μ₀", + "h_bar" => "ℏ", + + // Misc + "deg" => "°", + + _ => { return None; } + }; + return Some(r); +} + + + + pub fn find_subs( mut g: VecDeque, ) -> ( @@ -24,62 +82,19 @@ pub fn find_subs( while g.len() > 0 { let mut t = g.pop_front().unwrap(); + let target: Option<&str> = match &mut t { Token::Operator(_, s) => { - let target = match &s[..] { - "*" => {Some("×")}, - "/" => {Some("÷")}, - "sqrt" => {Some("√")}, - "rt" => {Some("√")}, - _ => {None} - }; + let target = sub_string(s); // Update token contents too. - // This makes sure that errors also contain the updated text. + // This makes errors and printouts use the updated string. if target.is_some() { *s = String::from(target.unwrap()); } target }, Token::Word(_, s) => { - let target = match &s[..] { - // Greek letters - "alpha" => {Some("α")}, - "beta" => {Some("β")}, - "gamma" => {Some("γ")}, - "delta" => {Some("δ")}, - "epsilon" => {Some("ε")}, - "zeta" => {Some("ζ")}, - "eta" => {Some("η")}, - "theta" => {Some("θ")}, - //"iota" => {Some("ι")}, - //"kappa" => {Some("κ")}, - "lambda" => {Some("λ")}, - "mu" => {Some("μ")}, - //"nu" => {Some("ν")}, - "xi" => {Some("ξ")}, - //"omicron" => {Some("ο")}, - "pi" => {Some("π")}, - "rho" => {Some("ρ")}, - "sigma" => {Some("σ")}, - "tau" => {Some("τ")}, - //"upsilon" => {Some("υ")}, - "phi" => {Some("φ")}, - "chi" => {Some("χ")}, - //"psi" => {Some("ψ")}, Conflict with pound / square inch - "omega" => {Some("ω")}, - - // Constants - "epsilon_zero" => {Some("ε₀")}, - "eps_zero" => {Some("ε₀")}, - "g_zero" => {Some("g₀")}, - "mu_zero" => {Some("μ₀")}, - "h_bar" => {Some("ℏ")}, - - // Misc - "deg" => {Some("°")} - _ => {None} - }; - + let target = sub_string(s); if target.is_some() { *s = String::from(target.unwrap()); } target }, From 4055c082176aa252970db6567657ec94045f7c49 Mon Sep 17 00:00:00 2001 From: Mark Date: Sun, 20 Aug 2023 16:33:34 -0700 Subject: [PATCH 08/30] Fixed incompatible messages when one unit is a scalar --- src/evaluate/operator.rs | 64 ++++++++++++++++++++++++++++++++-------- 1 file changed, 52 insertions(+), 12 deletions(-) diff --git a/src/evaluate/operator.rs b/src/evaluate/operator.rs index 1268554..f5fbf8a 100644 --- a/src/evaluate/operator.rs +++ b/src/evaluate/operator.rs @@ -75,12 +75,26 @@ pub fn eval_operator(context: &mut Context, g: &Expression) -> Result Result Result Date: Sun, 20 Aug 2023 16:49:05 -0700 Subject: [PATCH 09/30] Minor cleanup --- src/entrypoint/unix/unix.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/entrypoint/unix/unix.rs b/src/entrypoint/unix/unix.rs index b0a6347..104a60a 100644 --- a/src/entrypoint/unix/unix.rs +++ b/src/entrypoint/unix/unix.rs @@ -23,11 +23,11 @@ pub fn main() -> Result<(), std::io::Error> { // Set color compatibilty let term_colors = stdout.available_colors().unwrap_or(0); if term_colors >= 256 { - context.config.term_color_type = 2 + context.config.term_color_type = 2; } else if term_colors >= 8 { - context.config.term_color_type = 1 + context.config.term_color_type = 1; } else { - context.config.term_color_type = 0 + context.config.term_color_type = 0; } context.config.check(); From 77c357c2f316045a36f01f3ae27812d74add56ab Mon Sep 17 00:00:00 2001 From: Mark Date: Sun, 20 Aug 2023 16:49:35 -0700 Subject: [PATCH 10/30] All style definitions in one place --- src/entrypoint/unix/promptbuffer.rs | 22 +++++-------- src/formattedtext.rs | 49 +++++++++++++++++------------ 2 files changed, 37 insertions(+), 34 deletions(-) diff --git a/src/entrypoint/unix/promptbuffer.rs b/src/entrypoint/unix/promptbuffer.rs index 7077bca..619f759 100644 --- a/src/entrypoint/unix/promptbuffer.rs +++ b/src/entrypoint/unix/promptbuffer.rs @@ -1,11 +1,12 @@ use std::collections::VecDeque; use std::io::Write; use termion::raw::RawTerminal; -use termion::color; -use termion::style; +use crate::formattedtext; use crate::parser::substitute_cursor; use crate::context::Context; +const PROMPT_STR: &str = "==> "; + #[derive(Debug)] pub struct PromptBuffer { // History @@ -49,18 +50,13 @@ impl PromptBuffer { let l = self.buffer.chars().count(); let i = if l == 0 {0} else {l - self.cursor}; - - //println!("{i} {}", self.cursor); - // Draw prettyprinted expression let (display_c, s) = substitute_cursor(context, &self.get_contents(), i); write!( - stdout, "\r{}{}==>{}{} {}", - style::Bold, - color::Fg(color::Blue), - color::Fg(color::Reset), - style::Reset, + stdout, "\r{}{PROMPT_STR}{}{}", + formattedtext::format_map('p', context).unwrap(), + formattedtext::format_map('n', context).unwrap(), s )?; @@ -72,15 +68,13 @@ impl PromptBuffer { )?; } - write!( stdout, "\r{}", - termion::cursor::Right((display_c + 4) as u16) + termion::cursor::Right((display_c + PROMPT_STR.chars().count()) as u16) )?; - stdout.flush()?; - self.last_print_len = s.chars().count(); stdout.flush()?; + self.last_print_len = s.chars().count(); return Ok(()); } diff --git a/src/formattedtext.rs b/src/formattedtext.rs index 68de1ef..dfcfc55 100644 --- a/src/formattedtext.rs +++ b/src/formattedtext.rs @@ -22,7 +22,8 @@ impl ToString for FormattedText { fn format_map_none(c: char) -> Option { Some(match c { 'n'|'i'|'t'|'a'| - 'e'|'c'|'s'|'r' + 'e'|'c'|'s'|'r'| + 'p' => { "".to_string() }, _ => { return None } }) @@ -37,24 +38,27 @@ fn format_map_ansi(c: char) -> Option { 'i' => { // Normal italic text format!("{}{}", color::Fg(color::Reset), color::Bg(color::Reset)) }, - 't' => { // Title text + 't' => { // Title text (should be cyan) format!("{}{}", color::Fg(color::AnsiValue(6)), color::Bg(color::Reset)) }, - 'a' => { // Colored text + 'a' => { // Colored text (should be pink) format!("{}{}", color::Fg(color::AnsiValue(5)), color::Bg(color::Reset)) }, - 'e' => { // Error titles + 'e' => { // Error titles (should be red) format!("{}{}", color::Fg(color::AnsiValue(1)), color::Bg(color::Reset)) }, - 'c' => { // Console text + 'c' => { // Console text (inverted black on white) format!("{}{}", color::Fg(color::AnsiValue(0)), color::Bg(color::AnsiValue(7))) }, - 's' => { // Repeat prompt (how => is styled) - format!("{}{}", color::Fg(color::AnsiValue(2)), color::Bg(color::Reset)) - }, - 'r' => { // Result prompt (how = is styled) + 'p' => { // Input prompt (how ==> is styled) (should be blue) format!("{}{}", color::Fg(color::AnsiValue(4)), color::Bg(color::Reset)) }, + 's' => { // Repeat prompt (how => is styled) (should be pink) + format!("{}{}", color::Fg(color::AnsiValue(5)), color::Bg(color::Reset)) + }, + 'r' => { // Result prompt (how = is styled) (should be green) + format!("{}{}", color::Fg(color::AnsiValue(2)), color::Bg(color::Reset)) + }, _ => { return None } }) @@ -83,6 +87,9 @@ fn format_map_full(c: char) -> Option { 'c' => { // Console text format!("{}{}", color::Fg(color::LightBlack), style::Italic) }, + 'p' => { // Input prompt (how ==> is styled) + format!("{}{}", color::Fg(color::Blue), style::Bold) + }, 's' => { // Repeat prompt (how => is styled) format!("{}{}", color::Fg(color::Magenta), style::Bold) }, @@ -90,17 +97,24 @@ fn format_map_full(c: char) -> Option { format!("{}{}", color::Fg(color::Green), style::Bold) }, + _ => { return None } }) } +pub fn format_map(c: char, context: &Context) -> Option { + match context.config.term_color_type { + 0 => format_map_none(c), + 1 => format_map_ansi(c), + 2 => format_map_full(c), + _ => unreachable!("Invalid term_color_type") + } +} + impl FormattedText { pub fn newline(stdout: &mut RawTerminal) -> Result<(), std::io::Error> { - write!( - stdout, - "\r\n", - )?; + write!(stdout, "\n")?; return Ok(()); } } @@ -147,12 +161,7 @@ impl FormattedText { match (a, b) { (c, ']') => { // Normal text - let q = match context.config.term_color_type { - 0 => format_map_none(c), - 1 => format_map_ansi(c), - 2 => format_map_full(c), - _ => unreachable!("Invalid term_color_type") - }; + let q = format_map(c, context); if q.is_some() { s.push_str(&q.unwrap()); @@ -175,7 +184,7 @@ impl FormattedText { } } - write!(stdout, "{}", s)?; + write!(stdout, "\r{}", s)?; return Ok(()); } } From edc859dc01f03bf579190b659fb1ded4454da9ba Mon Sep 17 00:00:00 2001 From: Mark Date: Sun, 20 Aug 2023 17:24:39 -0700 Subject: [PATCH 11/30] Updated TODO --- TODO.md | 1 + 1 file changed, 1 insertion(+) diff --git a/TODO.md b/TODO.md index 0c6c007..b0e7f36 100644 --- a/TODO.md +++ b/TODO.md @@ -14,6 +14,7 @@ - Tuple operations - we don't need vectors as arguments to operators - Assignment tests + - Color check is too slow ## Parser - Better error when `sin = 2` From c477302c8867863641c114a89adf5623cc5fc744 Mon Sep 17 00:00:00 2001 From: Mark Date: Mon, 21 Aug 2023 13:17:58 -0700 Subject: [PATCH 12/30] Fixed `ans` evaluation --- src/context.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/context.rs b/src/context.rs index 484d61b..c61444c 100644 --- a/src/context.rs +++ b/src/context.rs @@ -126,6 +126,7 @@ impl Context { } else { panic!() } } + // Can we define a new variable with this name? pub fn valid_varible(&self, s: &str) -> bool { if { Function::from_string(s).is_some() || @@ -145,10 +146,17 @@ impl Context { } } + // Can we get a value fro mthis variable name? pub fn is_varible(&self, s: &str) -> bool { return { - self.valid_varible(s) && - (self.variables.contains_key(s) || self.shadow.contains_key(s)) + ( + s == "ans" && + self.history.len() != 0 + ) || + ( + self.valid_varible(s) && + (self.variables.contains_key(s) || self.shadow.contains_key(s)) + ) }; } From a125e867c43f1d689bc6b6a3b4d6c6a673ff8283 Mon Sep 17 00:00:00 2001 From: Mark Date: Mon, 21 Aug 2023 13:24:28 -0700 Subject: [PATCH 13/30] Help text typo --- src/command/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/command/mod.rs b/src/command/mod.rs index 68f16bc..ebc774c 100644 --- a/src/command/mod.rs +++ b/src/command/mod.rs @@ -224,7 +224,7 @@ pub fn do_command( if args.len() != 2 { return FormattedText::new( format!( - "[c]{first}[n] [t]takes exactly two arguments.[n]\n\n", + "[c]{first}[n] [t]takes exactly one argument.[n]\n\n", ) ); } From b136353d36d77fc288c982f5aed56ec552b7303e Mon Sep 17 00:00:00 2001 From: Mark Date: Mon, 21 Aug 2023 13:24:49 -0700 Subject: [PATCH 14/30] Comments --- src/parser/expression/expression.rs | 17 +++++++++++++++++ src/parser/mod.rs | 6 ++++++ src/parser/stage/find_subs.rs | 10 +++++----- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/parser/expression/expression.rs b/src/parser/expression/expression.rs index 564a162..f366e63 100644 --- a/src/parser/expression/expression.rs +++ b/src/parser/expression/expression.rs @@ -11,6 +11,23 @@ use super::super::LineLocation; #[derive(Debug)] #[derive(Clone)] pub enum Expression { + // Meaning of `LineLocation`: + // + // For Variables, Constants, Quantities, Tuples: + // If this expression was parsed, LineLocation is what part of the prompt was parsed to get this expression + // If this expression is the result of a calculation, LineLocaion is the sum of the LineLocations of + // all expressions used to make it. In other words, it points to the part of the prompt that was evaluated + // to get this new expression. + // + // For Operators: + // Linelocation points to the operator's position in the prompt. + // If this is a function, it points to the function name. + // If this is `+`, `!`, or etc, it points to that character. + // Operator arguments are NOT included in this linelocation. + // + // + // All the above rules are implemented when parsing and evaluating expressions. + Variable(LineLocation, String), Quantity(LineLocation, Quantity), Constant(LineLocation, Constant), diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 56f4eef..fe3a0d5 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -35,6 +35,12 @@ pub fn parse_no_context(s: &String) -> Result String { if !context.config.enable_substituion { return s.clone(); } let (_, s) = substitute_cursor(context, s, s.chars().count()); diff --git a/src/parser/stage/find_subs.rs b/src/parser/stage/find_subs.rs index 865f407..a7b09d4 100644 --- a/src/parser/stage/find_subs.rs +++ b/src/parser/stage/find_subs.rs @@ -62,13 +62,13 @@ fn sub_string(s: &str) -> Option<&'static str> { } - - +// Finds substitutions in an array of tokens. +// Returns new token array and substitution list. pub fn find_subs( mut g: VecDeque, ) -> ( - VecDeque<(LineLocation, String)>, - VecDeque + VecDeque<(LineLocation, String)>, // List of substrings to replace (in order) + VecDeque // New token array, with updated strings and linelocations ) { // Array of replacements @@ -103,7 +103,7 @@ pub fn find_subs( }; if target.is_none() { - // Even if nothing changed, we need to update token location + // Even if nothing changed, we need to update the new token's linelocation let l = t.get_mut_linelocation(); *l = LineLocation{pos: l.pos - offset, len: l.len}; } else { From 3a08cfb2d353b7c12b7c53b835dd0e76d235c19a Mon Sep 17 00:00:00 2001 From: Mark Date: Mon, 21 Aug 2023 13:26:53 -0700 Subject: [PATCH 15/30] Updated TODO --- TODO.md | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/TODO.md b/TODO.md index b0e7f36..e449a2b 100644 --- a/TODO.md +++ b/TODO.md @@ -1,4 +1,5 @@ ## Version Bump checklist + - TODO: build and publish script - update Cargo.toml - run cargo test - commit @@ -10,28 +11,22 @@ ## Pre-release - - Fix linelocation (consistent, what does an operator's linelocation mean?) - Tuple operations - we don't need vectors as arguments to operators - - Assignment tests - - Color check is too slow + - Fix linelocation when evaluating functions ## Parser - - Better error when `sin = 2` - Should functions be operators? - Binary, hex, octal numbers ## General + - Better tests (assignment, many expressions in one context) - Optional config file - Optional history file - - daisyrc file - Compile to WASM, publish a webapp - evaluate straight from command line - - Auto-push to crates.io - Package for debian - - Faster startup - ## Internals @@ -41,7 +36,6 @@ - Remove rug dependency (too big, incompatible) ## Math Features - - Dice - Mean, Median, Min - Arbitrary base logarithm - Derivatives @@ -49,21 +43,16 @@ - Complex numbers - acot/acoth functions - Sums and products with functional arguments - - Add functions: gcd, inverse mod + - Add functions: gcd, inverse mod, dice ## Prompt + - Fix terminal color detection - Live syntax/output (like firefox js terminal) - - Syntax highlight input and output + - Syntax highlighting - fish-style tab completion - Numbered expressions, history recall - - Color configuration - Enable/disable unit sets (defaults?) - Consistent unit ordering - - Better linelocation - - we shouldn't need to re-print user input on evaluation errors, red arrows should adjust themselves to the prettyprinted string - - Backend-independent colorful printing - - Better colors in error texts - - Better substitution. Consistent: when ascii, when unicode? - Command to list substitutions ## Units From 11b5cd877a4f0e7527d4ef55574c5ef6ee17ed2f Mon Sep 17 00:00:00 2001 From: mark Date: Sun, 27 Aug 2023 22:28:17 -0700 Subject: [PATCH 16/30] Replaced RUG rational with num rational --- src/quantity/scalar/rationalbase.rs | 65 ++++++++++------------------- 1 file changed, 23 insertions(+), 42 deletions(-) diff --git a/src/quantity/scalar/rationalbase.rs b/src/quantity/scalar/rationalbase.rs index 4270b1b..eca8c97 100644 --- a/src/quantity/scalar/rationalbase.rs +++ b/src/quantity/scalar/rationalbase.rs @@ -1,5 +1,7 @@ -use rug::Rational; -use rug::Integer; +use num::rational::BigRational; +use num::BigInt; +use num::Num; +use num::Signed; use std::ops::{ Add, Sub, Mul, Div, @@ -22,7 +24,7 @@ macro_rules! cant_do { #[derive(Debug)] #[derive(Clone)] pub struct RationalBase where { - pub val: Rational + pub val: BigRational } impl ToString for RationalBase{ @@ -33,18 +35,12 @@ impl ToString for RationalBase{ impl RationalBase { pub fn from_frac(t: i64, b: i64) -> Option { - let v = Rational::from((t, b)); + let v = BigRational::new_raw(BigInt::from(t), BigInt::from(b)); return Some(RationalBase{ val: v }); } } impl ScalarBase for RationalBase { - fn from_f64(f: f64) -> Option { - let v = Rational::from_f64(f); - if v.is_none() { return None } - return Some(RationalBase{ val: v.unwrap() }); - } - fn from_string(s: &str) -> Option { // Scientific notation let mut sci = s.split("e"); @@ -89,7 +85,7 @@ impl ScalarBase for RationalBase { // From fraction string - let r = Rational::from_str_radix(&s, 10); + let r = BigRational::from_str_radix(&s, 10); let r = match r { Ok(x) => x, Err(_) => return None @@ -100,18 +96,13 @@ impl ScalarBase for RationalBase { } - fn fract(&self) -> Option { - Some(RationalBase{val: self.val.clone().fract_floor(Integer::new()).0}) - } + fn fract(&self) -> Option { Some(RationalBase{val: self.val.fract()}) } + fn is_int(&self) -> bool { self.val.is_integer() } - fn is_int(&self) -> bool { - self.fract() == RationalBase::from_f64(0f64) - } - - fn is_zero(&self) -> bool {self.val == Rational::from((0,1))} - fn is_one(&self) -> bool {self.val == Rational::from((1,1))} - fn is_negative(&self) -> bool { self.val.clone().signum() == -1 } - fn is_positive(&self) -> bool { self.val.clone().signum() == 1 } + fn is_zero(&self) -> bool {self.val == BigRational::from_integer(BigInt::from(0))} + fn is_one(&self) -> bool {self.val == BigRational::from_integer(BigInt::from(1))} + fn is_negative(&self) -> bool { self.val.is_negative() } + fn is_positive(&self) -> bool { self.val.is_positive() } fn abs(&self) -> Option {Some(RationalBase{val: self.val.clone().abs()})} fn floor(&self) -> Option {Some(RationalBase{val: self.val.clone().floor()})} @@ -153,9 +144,7 @@ impl Add for RationalBase where { type Output = Self; fn add(self, other: Self) -> Self::Output { - Self { - val: self.val + other.val - } + Self { val: self.val + other.val } } } @@ -169,9 +158,7 @@ impl Sub for RationalBase { type Output = Self; fn sub(self, other: Self) -> Self::Output { - Self { - val: self.val - other.val - } + Self { val: self.val - other.val } } } @@ -185,9 +172,7 @@ impl Mul for RationalBase { type Output = Self; fn mul(self, other: Self) -> Self::Output { - Self { - val: self.val * other.val - } + Self { val: self.val * other.val } } } @@ -201,9 +186,7 @@ impl Div for RationalBase { type Output = Self; fn div(self, other: Self) -> Self::Output { - Self { - val: self.val / other.val - } + Self { val: self.val / other.val } } } @@ -217,9 +200,7 @@ impl Neg for RationalBase where { type Output = Self; fn neg(self) -> Self::Output { - Self { - val: -self.val - } + Self { val: -self.val } } } @@ -228,15 +209,15 @@ impl Rem for RationalBase { fn rem(self, modulus: RationalBase) -> Self::Output { if { - *self.val.denom() != 1 || - *modulus.val.denom() != 1 + *self.val.denom() != BigInt::from(1) || + *modulus.val.denom() != BigInt::from(1) } { panic!() } RationalBase{ - val : Rational::from(( + val : BigRational::new_raw( self.val.numer() % modulus.val.numer(), - 1 - )) + BigInt::from(1) + ) } } } From 80b1c76c59ee52f153d8aebe2ba2b10d28a3db02 Mon Sep 17 00:00:00 2001 From: mark Date: Sun, 3 Sep 2023 13:12:22 -0700 Subject: [PATCH 17/30] Added shell.nix --- shell.nix | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 shell.nix diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..dff6d69 --- /dev/null +++ b/shell.nix @@ -0,0 +1,11 @@ +let + pkgs = import (fetchTarball "https://github.com/NixOS/nixpkgs/archive/f155f0cf4ea43c4e3c8918d2d327d44777b6cad4.tar.gz") {}; +in pkgs.mkShell { + buildInputs = with pkgs; [ + cargo + rustc + rustfmt + m4 + ]; +} + From 38c982bb00e9ac9ef614de8484b1b4dc96ccf2aa Mon Sep 17 00:00:00 2001 From: mark Date: Sun, 3 Sep 2023 13:12:37 -0700 Subject: [PATCH 18/30] Updated TODO --- TODO.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/TODO.md b/TODO.md index e449a2b..21854af 100644 --- a/TODO.md +++ b/TODO.md @@ -26,7 +26,7 @@ - Optional history file - Compile to WASM, publish a webapp - evaluate straight from command line - - Package for debian + - Package for debian, nix ## Internals @@ -53,7 +53,6 @@ - Numbered expressions, history recall - Enable/disable unit sets (defaults?) - Consistent unit ordering - - Command to list substitutions ## Units - long prefixes (megatonne, etc) From 2391606ae148d75f79c789b8d5a8d4b07a030a7b Mon Sep 17 00:00:00 2001 From: mark Date: Sun, 3 Sep 2023 14:36:00 -0700 Subject: [PATCH 19/30] Fully removed RUG dependency --- Cargo.lock | 176 ++++++++++++++++++--------------- Cargo.toml | 3 +- src/quantity/scalar/f64base.rs | 114 ++++++++++++++++++--- src/quantity/scalar/mod.rs | 16 ++- src/quantity/scalar/scalar.rs | 13 ++- 5 files changed, 216 insertions(+), 106 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2cbc20d..c133079 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9,10 +9,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] -name = "az" -version = "1.2.1" +name = "bigdecimal" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" +checksum = "454bca3db10617b88b566f205ed190aedb0e0e6dd4cad61d3988a72e8c5594cb" +dependencies = [ + "autocfg", + "libm", + "num-bigint", + "num-integer", + "num-traits", +] [[package]] name = "bitflags" @@ -30,22 +37,13 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" name = "daisycalc" version = "1.0.1" dependencies = [ + "bigdecimal", "cfg-if", - "rug", + "num", "termion", "toml", ] -[[package]] -name = "gmp-mpfr-sys" -version = "1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b560063e2ffa8ce9c2ef9bf487f2944a97deca5b8de0b5bcd0ae6437ef8b75f" -dependencies = [ - "libc", - "windows-sys", -] - [[package]] name = "hashbrown" version = "0.12.3" @@ -68,12 +66,94 @@ version = "0.2.140" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" +[[package]] +name = "libm" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" + [[package]] name = "memchr" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +[[package]] +name = "num" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +dependencies = [ + "autocfg", +] + [[package]] name = "numtoa" version = "0.1.0" @@ -98,17 +178,6 @@ dependencies = [ "redox_syscall", ] -[[package]] -name = "rug" -version = "1.19.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "555e8b44763d034526db899c88cd56ccc4486cd38b444c8aa0e79d4e70ae5a34" -dependencies = [ - "az", - "gmp-mpfr-sys", - "libc", -] - [[package]] name = "serde" version = "1.0.164" @@ -170,63 +239,6 @@ dependencies = [ "winnow", ] -[[package]] -name = "windows-sys" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - [[package]] name = "winnow" version = "0.4.6" diff --git a/Cargo.toml b/Cargo.toml index d611062..b08301d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,8 @@ cfg-if = "1.0.0" [target.'cfg(target_family = "unix")'.dependencies] termion = "2.0.1" -rug = "1.19.2" +bigdecimal = "0.4.1" +num = "0.4.1" [build-dependencies] toml = "0.7.4" \ No newline at end of file diff --git a/src/quantity/scalar/f64base.rs b/src/quantity/scalar/f64base.rs index 3901920..0de90bd 100644 --- a/src/quantity/scalar/f64base.rs +++ b/src/quantity/scalar/f64base.rs @@ -8,7 +8,7 @@ use std::ops::{ use std::cmp::Ordering; use super::ScalarBase; - +use super::PRINT_LEN; macro_rules! foward { ( $x:ident ) => { @@ -25,16 +25,99 @@ pub struct F64Base where { } impl ToString for F64Base { - fn to_string(&self) -> String { self.val.to_string() } + fn to_string(&self) -> String { + // Decimal, looks like xxx.xxx. + // May start with a zero. + let mut s = self.val.to_string(); + + // Remove negative sign from string + let neg = s.starts_with("-"); + if neg { s = String::from(&s[1..]); } + + // Power of ten + let mut p: i32 = { + if let Some(x) = s.find(".") { + x as i32 + } else { + s.len() as i32 + } + }; + p -= 1; + + // We no longer need a decimal point in our string. + // also, trim off leading zeros and adjust power. + let mut s: &str = &s.replace(".", ""); + s = &s[0..]; + s = s.trim_end_matches('0'); + while s.starts_with('0') { + s = &s[1..]; + p -= 1; + } + + + let neg = if neg {"-"} else {""}; + + if (p.abs() as usize) < PRINT_LEN { + + if p >= 0 { + let q = p as usize; + + // Add zero padding + let t; + if s.len() < (q + 1) { + t = format!("{s}{}", "0".repeat(q + 1 - s.len())); + } else { t = s.to_string() } + + // Section before decimal point + let first = &t[0..q+1]; + + // The rest of the number, including a decimal point + let mut rest: String; + if first.len() == t.len() { + rest = String::from(""); + } else { + rest = format!(".{}", &t[q+1..]); + } + + // Limit length of decimal portion + if rest.len() > PRINT_LEN { + rest = String::from(&rest[0..PRINT_LEN]); + } + + return format!("{neg}{first}{rest}"); + } else { + let q = p.abs() as usize; + + let t = format!("{neg}0.{}{s}", "0".repeat(q-1)); + + return if t.len() > PRINT_LEN { String::from(&t[0..PRINT_LEN]) } else {t}; + } + + // Print full scientific notation + } else { + // First (non-zero) digit of our number + let first = &s[0..1]; + + // The rest of the number, including a decimal point + let mut rest: String; + if first.len() == s.len() { + rest = String::from(""); + } else { + rest = format!(".{}", &s[1..]); + } + + // Limit length of decimal portion + if rest.len() > 5 { + rest = String::from(&rest[0..PRINT_LEN]); + } + + return format!("{neg}{first}{rest}e{p}"); + } + } } impl ScalarBase for F64Base { - - fn from_f64(f: f64) -> Option { - return Some(F64Base{ val: f }); - } - fn from_string(s: &str) -> Option { let v = s.parse::(); let v = match v { @@ -51,6 +134,7 @@ impl ScalarBase for F64Base { fn is_one(&self) -> bool {self.val == 1f64} fn is_negative(&self) -> bool { self.val.is_sign_negative() } fn is_positive(&self) -> bool { self.val.is_sign_positive() } + fn is_int(&self) -> bool { self.val.floor() == self.val } foward!(abs); foward!(floor); @@ -60,9 +144,11 @@ impl ScalarBase for F64Base { foward!(sin); foward!(cos); foward!(tan); - foward!(csc); - foward!(sec); - foward!(cot); + + fn csc(&self) -> Option { Some(F64Base{ val: 1f64/self.val.sin() }) } + fn sec(&self) -> Option { Some(F64Base{ val: 1f64/self.val.cos() }) } + fn cot(&self) -> Option { Some(F64Base{ val: 1f64/self.val.tan() }) } + foward!(asin); foward!(acos); foward!(atan); @@ -70,9 +156,11 @@ impl ScalarBase for F64Base { foward!(sinh); foward!(cosh); foward!(tanh); - foward!(csch); - foward!(sech); - foward!(coth); + + fn csch(&self) -> Option { Some(F64Base{ val: 1f64/self.val.sinh() }) } + fn sech(&self) -> Option { Some(F64Base{ val: 1f64/self.val.cosh() }) } + fn coth(&self) -> Option { Some(F64Base{ val: 1f64/self.val.tanh() }) } + foward!(asinh); foward!(acosh); foward!(atanh); diff --git a/src/quantity/scalar/mod.rs b/src/quantity/scalar/mod.rs index 65a4722..b3f4f32 100644 --- a/src/quantity/scalar/mod.rs +++ b/src/quantity/scalar/mod.rs @@ -1,9 +1,19 @@ -const FLOAT_PRECISION: u32 = 1024; +//const FLOAT_PRECISION: u32 = 1024; const PRINT_LEN: usize = 5; // How many significant digits we will show in output pub(in self) mod rationalbase; -pub(in self) mod floatbase; -//mod f64base; + + +// Pick a float implementation. +// floatbase is high-precision, f64base is for testing. + +//pub(in self) mod floatbase; +//pub use floatbase::FloatBase; + +pub(in self) mod f64base; +pub use f64base::F64Base as FloatBase; + + mod scalar; pub use self::scalar::Scalar; diff --git a/src/quantity/scalar/scalar.rs b/src/quantity/scalar/scalar.rs index 756e5bb..e6a0d5a 100644 --- a/src/quantity/scalar/scalar.rs +++ b/src/quantity/scalar/scalar.rs @@ -7,7 +7,7 @@ use std::ops::{ }; use std::cmp::Ordering; -use super::floatbase::FloatBase as FloatBase; +use super::FloatBase as FloatBase; use super::rationalbase::RationalBase; @@ -21,7 +21,6 @@ pub trait ScalarBase: PartialEq + PartialOrd { // Creation - fn from_f64(f: f64) -> Option; fn from_string(s: &str) -> Option; // Utility @@ -87,8 +86,8 @@ fn to_float(r: Scalar) -> Scalar { match &r { Scalar::Float {..} => r, Scalar::Rational {v} => wrap_float!( - FloatBase::from(v.val.numer()).unwrap() / - FloatBase::from(v.val.denom()).unwrap() + FloatBase::from_string(&v.val.numer().to_string()).unwrap() / + FloatBase::from_string(&v.val.denom().to_string()).unwrap() ) } } @@ -105,13 +104,13 @@ impl ToString for Scalar { // Creation methods impl Scalar { pub fn new_float(f: f64) -> Option { - let v = FloatBase::from_f64(f); + let v = FloatBase::from_string(&f.to_string()); if v.is_none() { return None; } return Some(wrap_float!(v.unwrap())); } pub fn new_rational(f: f64) -> Option { - let r = RationalBase::from_f64(f); + let r = RationalBase::from_string(&f.to_string()); if r.is_none() { return None; } return Some(wrap_rational!(r.unwrap())); } @@ -185,7 +184,7 @@ impl Scalar { pub fn is_nan(&self) -> bool { match self { - Scalar::Float {v} => {v.val.is_nan()}, + Scalar::Float {..} => {false}, Scalar::Rational {..} => {false} } } From 84ebf89d6fb8c220940c6b77396c1ce09c686997 Mon Sep 17 00:00:00 2001 From: mark Date: Sun, 3 Sep 2023 14:36:20 -0700 Subject: [PATCH 20/30] Updated TODO --- TODO.md | 1 - 1 file changed, 1 deletion(-) diff --git a/TODO.md b/TODO.md index 21854af..8a7ebba 100644 --- a/TODO.md +++ b/TODO.md @@ -33,7 +33,6 @@ - Non-recursive treeify - Faster factorial function. Maybe use gamma instead? - Arbitrary precision float (rug doesn't offer arbitrary exponents) - - Remove rug dependency (too big, incompatible) ## Math Features - Mean, Median, Min From 4353548900eff2c56954e09f4f69030ab762f969 Mon Sep 17 00:00:00 2001 From: mark Date: Sun, 3 Sep 2023 15:56:33 -0700 Subject: [PATCH 21/30] Fixed float printing --- src/quantity/scalar/f64base.rs | 85 ++++++++++++++++------------------ src/quantity/scalar/mod.rs | 3 +- src/tests.rs | 2 +- 3 files changed, 43 insertions(+), 47 deletions(-) diff --git a/src/quantity/scalar/f64base.rs b/src/quantity/scalar/f64base.rs index 0de90bd..4695e67 100644 --- a/src/quantity/scalar/f64base.rs +++ b/src/quantity/scalar/f64base.rs @@ -8,7 +8,8 @@ use std::ops::{ use std::cmp::Ordering; use super::ScalarBase; -use super::PRINT_LEN; +use super::SHOW_SIG; +use super::MAX_LEN; macro_rules! foward { ( $x:ident ) => { @@ -55,63 +56,57 @@ impl ToString for F64Base { } + // Pick significant digits and round + let mut s = String::from(s); + if s.len() > SHOW_SIG { + let round; + if s.len() != SHOW_SIG + 1 { + round = s[SHOW_SIG..SHOW_SIG+1].parse().unwrap(); + } else { round = 0; } + + s = String::from(&s[0..SHOW_SIG]); + + if round >= 5 { + let new = s[s.len()-1..s.len()].parse::().unwrap() + 1u8; + if new != 10 { + s = format!("{}{new}", &s[0..s.len()-1]); + } + } + } + + s = format!("{s}{}", "0".repeat(SHOW_SIG - s.len())); + // at this point, s is guaranteed to have exactly SHOW_SIG digits. + let neg = if neg {"-"} else {""}; - if (p.abs() as usize) < PRINT_LEN { - + if (p.abs() as usize) < MAX_LEN { if p >= 0 { let q = p as usize; - // Add zero padding - let t; - if s.len() < (q + 1) { - t = format!("{s}{}", "0".repeat(q + 1 - s.len())); - } else { t = s.to_string() } - - // Section before decimal point - let first = &t[0..q+1]; - - // The rest of the number, including a decimal point - let mut rest: String; - if first.len() == t.len() { - rest = String::from(""); + let first = &s[0..q+1]; + let mut rest = &s[q+1..]; + rest = rest.trim_end_matches('0'); + if rest == "" { + return format!("{neg}{first}"); } else { - rest = format!(".{}", &t[q+1..]); + return format!("{neg}{first}.{rest}"); } - - // Limit length of decimal portion - if rest.len() > PRINT_LEN { - rest = String::from(&rest[0..PRINT_LEN]); - } - - return format!("{neg}{first}{rest}"); } else { let q = p.abs() as usize; - - let t = format!("{neg}0.{}{s}", "0".repeat(q-1)); - - return if t.len() > PRINT_LEN { String::from(&t[0..PRINT_LEN]) } else {t}; + let t = format!("0.{}{s}", "0".repeat(q-1)); + return format!("{neg}{}", t.trim_end_matches('0')); } // Print full scientific notation } else { - // First (non-zero) digit of our number let first = &s[0..1]; - - // The rest of the number, including a decimal point - let mut rest: String; - if first.len() == s.len() { - rest = String::from(""); + let mut rest = &s[1..]; + rest = rest.trim_end_matches('0'); + if rest == "" { + return format!("{neg}{first}e{p}"); } else { - rest = format!(".{}", &s[1..]); + return format!("{neg}{first}.{rest}e{p}"); } - - // Limit length of decimal portion - if rest.len() > 5 { - rest = String::from(&rest[0..PRINT_LEN]); - } - - return format!("{neg}{first}{rest}e{p}"); } } } @@ -249,11 +244,11 @@ impl Rem for F64Base { fn rem(self, modulus: F64Base) -> Self::Output { if { - (!self.fract().unwrap().is_zero()) || - (!modulus.fract().unwrap().is_zero()) + (!self.is_int()) || + (!modulus.is_int()) } { panic!() } - F64Base{val : self.val.fract() % modulus.val.fract()} + F64Base{val : self.val.round() % modulus.val.round()} } } diff --git a/src/quantity/scalar/mod.rs b/src/quantity/scalar/mod.rs index b3f4f32..8f8b8ed 100644 --- a/src/quantity/scalar/mod.rs +++ b/src/quantity/scalar/mod.rs @@ -1,5 +1,6 @@ //const FLOAT_PRECISION: u32 = 1024; -const PRINT_LEN: usize = 5; // How many significant digits we will show in output +const SHOW_SIG: usize = 5; // How many significant digits we will show in output +const MAX_LEN: usize = 5; // If a scientific exponent is >= this value, do not use scientific notation. pub(in self) mod rationalbase; diff --git a/src/tests.rs b/src/tests.rs index 51342b3..d54f10f 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -131,7 +131,7 @@ fn operators() { good_expr("125", "5^(+3)"); good_expr("125", "+5^3"); - good_expr("0.2148", "3 ^ (-1.4)"); + good_expr("0.21479", "3 ^ (-1.4)"); // Should parse as ((2^3)^4)^5 good_expr("1.1529e18", "2^3^4^5"); From 47abd9d18e58415bbc363fc0a01471a48955e591 Mon Sep 17 00:00:00 2001 From: mark Date: Sun, 3 Sep 2023 15:57:46 -0700 Subject: [PATCH 22/30] Migrated floatbase to bigdecimal --- src/quantity/scalar/floatbase.rs | 275 ++++++++++++++++++------------- 1 file changed, 160 insertions(+), 115 deletions(-) diff --git a/src/quantity/scalar/floatbase.rs b/src/quantity/scalar/floatbase.rs index 2a4792f..84a37c9 100644 --- a/src/quantity/scalar/floatbase.rs +++ b/src/quantity/scalar/floatbase.rs @@ -1,7 +1,7 @@ -use rug::Float; -use rug::Assign; -use rug::ops::AssignRound; -use rug::ops::Pow; +use bigdecimal::BigDecimal; +use bigdecimal::Zero; +use bigdecimal::RoundingMode; +use std::str::FromStr; use std::ops::{ Add, Sub, Mul, Div, @@ -14,158 +14,203 @@ use std::ops::{ use std::cmp::Ordering; use super::ScalarBase; -use super::PRINT_LEN; -use super::FLOAT_PRECISION; +use super::SHOW_SIG; +use super::MAX_LEN; #[derive(Debug)] #[derive(Clone)] pub struct FloatBase where { - pub val: Float + pub val: BigDecimal } impl FloatBase { - pub fn from(a: T) -> Option where - Float: Assign + AssignRound - { - let v = Float::with_val(FLOAT_PRECISION, a); - return Some(FloatBase{ val: v }); + pub fn new(s: &str) -> FloatBase { + return FloatBase { + val: s.parse().unwrap() + }; } } + impl ToString for FloatBase { fn to_string(&self) -> String { - let (sign, mut string, exp) = self.val.to_sign_string_exp(10, Some(PRINT_LEN)); + // Decimal, looks like xxx.xxx. + // May start with a zero. + let mut s = self.val.to_string(); - // zero, nan, or inf. - let sign = if sign {"-"} else {""}; - if exp.is_none() { return format!("{sign}{string}"); } - let exp = exp.unwrap(); + // Remove negative sign from string + let neg = s.starts_with("-"); + if neg { s = String::from(&s[1..]); } + + // Power of ten + let mut p: i32 = { + if let Some(x) = s.find(".") { + x as i32 + } else { + s.len() as i32 + } + }; + p -= 1; - // Remove trailing zeros. - // At this point, string is guaranteed to be nonzero. - while string.chars().last().unwrap() == '0' { - string.remove(string.len() - 1); + // We no longer need a decimal point in our string. + // also, trim off leading zeros and adjust power. + let mut s: &str = &s.replace(".", ""); + s = &s[0..]; + s = s.trim_end_matches('0'); + while s.starts_with('0') { + s = &s[1..]; + p -= 1; } - let exp_u: usize; - if exp < 0 { - exp_u = (-exp).try_into().unwrap() - } else { - exp_u = exp.try_into().unwrap() - } + // Pick significant digits and round + let mut s = String::from(s); + if s.len() > SHOW_SIG { + let round; + if s.len() != SHOW_SIG + 1 { + round = s[SHOW_SIG..SHOW_SIG+1].parse().unwrap(); + } else { round = 0; } - if exp_u >= PRINT_LEN { - // Exponential notation - let pre = &string[0..1]; - let post = &string[1..]; + s = String::from(&s[0..SHOW_SIG]); - format!( - "{pre}{}{post}e{}", - if post.len() != 0 {"."} else {""}, - //if exp > 0 {"+"} else {""}, - exp - 1 - ) - } else { - if exp <= 0 { // Decimal, needs `0.` and leading zeros - format!( - "{sign}0.{}{string}", - "0".repeat(exp_u) - ) - } else if exp_u < string.len() { // Decimal, needs only `.` - format!( - "{sign}{}.{}", - &string[0..exp_u], - &string[exp_u..] - ) - } else { // Integer, needs trailing zeros - format!( - "{sign}{string}{}", - "0".repeat(exp_u - string.len()) - ) + if round >= 5 { + let new = s[s.len()-1..s.len()].parse::().unwrap() + 1u8; + if new != 10 { + s = format!("{}{new}", &s[0..s.len()-1]); + } } } - } -} + s = format!("{s}{}", "0".repeat(SHOW_SIG - s.len())); + // at this point, s is guaranteed to have exactly SHOW_SIG digits. + let neg = if neg {"-"} else {""}; -macro_rules! foward { - ( $x:ident ) => { - fn $x(&self) -> Option { - Some(FloatBase{ val: self.val.clone().$x()}) + if (p.abs() as usize) < MAX_LEN { + if p >= 0 { + let q = p as usize; + + let first = &s[0..q+1]; + let mut rest = &s[q+1..]; + rest = rest.trim_end_matches('0'); + if rest == "" { + return format!("{neg}{first}"); + } else { + return format!("{neg}{first}.{rest}"); + } + } else { + let q = p.abs() as usize; + let t = format!("0.{}{s}", "0".repeat(q-1)); + return format!("{neg}{}", t.trim_end_matches('0')); + } + + // Print full scientific notation + } else { + let first = &s[0..1]; + let mut rest = &s[1..]; + rest = rest.trim_end_matches('0'); + if rest == "" { + return format!("{neg}{first}e{p}"); + } else { + return format!("{neg}{first}.{rest}e{p}"); + } } } } + + impl ScalarBase for FloatBase { - fn from_f64(f: f64) -> Option { - let v = Float::with_val(FLOAT_PRECISION, f); - return Some(FloatBase{ val: v }); - } - fn from_string(s: &str) -> Option { - let v = Float::parse(s); + let v = BigDecimal::from_str(s); let v = match v { Ok(x) => x, Err(_) => return None }; - return Some( - FloatBase{ val: - Float::with_val(FLOAT_PRECISION, v) - } - ); + return Some(FloatBase{ val: v }); } - foward!(fract); + //foward!(fract); fn is_zero(&self) -> bool {self.val.is_zero()} - fn is_one(&self) -> bool {self.val == Float::with_val(FLOAT_PRECISION, 1)} - fn is_negative(&self) -> bool { self.val.is_sign_negative() } - fn is_positive(&self) -> bool { self.val.is_sign_positive() } + fn is_one(&self) -> bool {self.val == BigDecimal::from_str("1").unwrap()} + fn is_negative(&self) -> bool { self.val.sign() == num::bigint::Sign::Minus } + fn is_positive(&self) -> bool { self.val.sign() == num::bigint::Sign::Plus } - fn is_int(&self) -> bool { - self.fract() == FloatBase::from_f64(0f64) + fn is_int(&self) -> bool { self.val.is_integer() } + + fn abs(&self) -> Option { Some(FloatBase{ val: self.val.abs() }) } + fn round(&self) -> Option { Some(FloatBase{ val: self.val.round(0) }) } + + fn floor(&self) -> Option { + let (_, scale) = self.val.as_bigint_and_exponent(); + Some(FloatBase{ val: self.val.with_scale_round(scale, RoundingMode::Down) }) } - foward!(abs); - foward!(floor); - foward!(ceil); - foward!(round); - - foward!(sin); - foward!(cos); - foward!(tan); - foward!(csc); - foward!(sec); - foward!(cot); - foward!(asin); - foward!(acos); - foward!(atan); - - foward!(sinh); - foward!(cosh); - foward!(tanh); - foward!(csch); - foward!(sech); - foward!(coth); - foward!(asinh); - foward!(acosh); - foward!(atanh); - - foward!(exp); - foward!(ln); - foward!(log10); - foward!(log2); - - fn log(&self, base: FloatBase) -> Option { - Some(FloatBase{ val: self.val.clone().log10() } / base.log10().unwrap()) + fn ceil(&self) -> Option { + let (_, scale) = self.val.as_bigint_and_exponent(); + Some(FloatBase{ val: self.val.with_scale_round(scale, RoundingMode::Up) }) } - fn pow(&self, base: FloatBase) -> Option { - Some(FloatBase{ val: self.val.clone().pow(base.val)}) + fn fract(&self) -> Option { Some(self.clone() - self.floor().unwrap()) } + + + fn sin(&self) -> Option { + let c0: BigDecimal = "1.276278962".parse().unwrap(); + let c1: BigDecimal = "-.285261569".parse().unwrap(); + let c2: BigDecimal = "0.009118016".parse().unwrap(); + let c3: BigDecimal = "-.000136587".parse().unwrap(); + let c4: BigDecimal = "0.000001185".parse().unwrap(); + let c5: BigDecimal = "-.000000007".parse().unwrap(); + + + // z should be between -0.25 to 0.25 (percent of a full circle) + let z: BigDecimal = self.val.clone() / 360f64; + let w = BigDecimal::from(4) * z; + let x: BigDecimal = 2 * w.clone() * w.clone() - 1; + + let p = ( + c0 * 1 + + c1 * x.clone() + + c2 * (2 * x.clone()*x.clone() - 1) + + c3 * (4 * x.clone()*x.clone()*x.clone() - 3 * x.clone()) + + c4 * (8 * x.clone()*x.clone()*x.clone()*x.clone() - 8 * x.clone()*x.clone() + 1) + + c5 * (16 * x.clone()*x.clone()*x.clone()*x.clone()*x.clone() - 20 * x.clone()*x.clone()*x.clone() + 5 * x.clone()) + ) * w; + + return Some(FloatBase{ val: p }) + } + fn cos(&self) -> Option { Some(FloatBase{ val: "1".parse().unwrap() }) } + fn tan(&self) -> Option { Some(FloatBase{ val: "1".parse().unwrap() }) } + fn csc(&self) -> Option { Some(FloatBase{ val: "1".parse().unwrap() }) } + fn sec(&self) -> Option { Some(FloatBase{ val: "1".parse().unwrap() }) } + fn cot(&self) -> Option { Some(FloatBase{ val: "1".parse().unwrap() }) } + fn asin(&self) -> Option { Some(FloatBase{ val: "1".parse().unwrap() }) } + fn acos(&self) -> Option { Some(FloatBase{ val: "1".parse().unwrap() }) } + fn atan(&self) -> Option { Some(FloatBase{ val: "1".parse().unwrap() }) } + fn sinh(&self) -> Option { Some(FloatBase{ val: "1".parse().unwrap() }) } + fn cosh(&self) -> Option { Some(FloatBase{ val: "1".parse().unwrap() }) } + fn tanh(&self) -> Option { Some(FloatBase{ val: "1".parse().unwrap() }) } + fn csch(&self) -> Option { Some(FloatBase{ val: "1".parse().unwrap() }) } + fn sech(&self) -> Option { Some(FloatBase{ val: "1".parse().unwrap() }) } + fn coth(&self) -> Option { Some(FloatBase{ val: "1".parse().unwrap() }) } + fn asinh(&self) -> Option { Some(FloatBase{ val: "1".parse().unwrap() }) } + fn acosh(&self) -> Option { Some(FloatBase{ val: "1".parse().unwrap() }) } + fn atanh(&self) -> Option { Some(FloatBase{ val: "1".parse().unwrap() }) } + fn exp(&self) -> Option { Some(FloatBase{ val: "1".parse().unwrap() }) } + fn ln(&self) -> Option { Some(FloatBase{ val: "1".parse().unwrap() }) } + fn log10(&self) -> Option { Some(FloatBase{ val: "1".parse().unwrap() }) } + fn log2(&self) -> Option { Some(FloatBase{ val: "1".parse().unwrap() }) } + + + fn log(&self, _base: FloatBase) -> Option { + Some(FloatBase{ val: "1".parse().unwrap() }) + } + + fn pow(&self, _base: FloatBase) -> Option { + Some(FloatBase{ val: "1".parse().unwrap() }) } } @@ -223,7 +268,7 @@ impl Div for FloatBase { impl DivAssign for FloatBase where { fn div_assign(&mut self, other: Self) { - self.val /= other.val; + self.val = self.val.clone() / other.val; } } @@ -240,11 +285,11 @@ impl Rem for FloatBase { fn rem(self, modulus: FloatBase) -> Self::Output { if { - (!self.fract().unwrap().is_zero()) || - (!modulus.fract().unwrap().is_zero()) + (!self.is_int()) || + (!modulus.is_int()) } { panic!() } - FloatBase{val : self.val.trunc() % modulus.val.trunc()} + FloatBase{val : self.val.round(0) % modulus.val.round(0)} } } From b9cfe719a6854a743e89ac266effdbf7a8e997fd Mon Sep 17 00:00:00 2001 From: Mark Date: Sun, 3 Sep 2023 16:03:24 -0700 Subject: [PATCH 23/30] Cleaned up to_string functions --- src/quantity/scalar/f64base.rs | 86 ++---------------------------- src/quantity/scalar/floatbase.rs | 86 ++---------------------------- src/quantity/scalar/mod.rs | 89 +++++++++++++++++++++++++++++++- 3 files changed, 94 insertions(+), 167 deletions(-) diff --git a/src/quantity/scalar/f64base.rs b/src/quantity/scalar/f64base.rs index 4695e67..5e9bcaf 100644 --- a/src/quantity/scalar/f64base.rs +++ b/src/quantity/scalar/f64base.rs @@ -8,8 +8,8 @@ use std::ops::{ use std::cmp::Ordering; use super::ScalarBase; -use super::SHOW_SIG; -use super::MAX_LEN; +use super::dec_to_sci; + macro_rules! foward { ( $x:ident ) => { @@ -27,87 +27,7 @@ pub struct F64Base where { impl ToString for F64Base { fn to_string(&self) -> String { - // Decimal, looks like xxx.xxx. - // May start with a zero. - let mut s = self.val.to_string(); - - // Remove negative sign from string - let neg = s.starts_with("-"); - if neg { s = String::from(&s[1..]); } - - // Power of ten - let mut p: i32 = { - if let Some(x) = s.find(".") { - x as i32 - } else { - s.len() as i32 - } - }; - p -= 1; - - // We no longer need a decimal point in our string. - // also, trim off leading zeros and adjust power. - let mut s: &str = &s.replace(".", ""); - s = &s[0..]; - s = s.trim_end_matches('0'); - while s.starts_with('0') { - s = &s[1..]; - p -= 1; - } - - - // Pick significant digits and round - let mut s = String::from(s); - if s.len() > SHOW_SIG { - let round; - if s.len() != SHOW_SIG + 1 { - round = s[SHOW_SIG..SHOW_SIG+1].parse().unwrap(); - } else { round = 0; } - - s = String::from(&s[0..SHOW_SIG]); - - if round >= 5 { - let new = s[s.len()-1..s.len()].parse::().unwrap() + 1u8; - if new != 10 { - s = format!("{}{new}", &s[0..s.len()-1]); - } - } - } - - s = format!("{s}{}", "0".repeat(SHOW_SIG - s.len())); - // at this point, s is guaranteed to have exactly SHOW_SIG digits. - - let neg = if neg {"-"} else {""}; - - if (p.abs() as usize) < MAX_LEN { - if p >= 0 { - let q = p as usize; - - let first = &s[0..q+1]; - let mut rest = &s[q+1..]; - rest = rest.trim_end_matches('0'); - if rest == "" { - return format!("{neg}{first}"); - } else { - return format!("{neg}{first}.{rest}"); - } - } else { - let q = p.abs() as usize; - let t = format!("0.{}{s}", "0".repeat(q-1)); - return format!("{neg}{}", t.trim_end_matches('0')); - } - - // Print full scientific notation - } else { - let first = &s[0..1]; - let mut rest = &s[1..]; - rest = rest.trim_end_matches('0'); - if rest == "" { - return format!("{neg}{first}e{p}"); - } else { - return format!("{neg}{first}.{rest}e{p}"); - } - } + return dec_to_sci(self.val.to_string()); } } diff --git a/src/quantity/scalar/floatbase.rs b/src/quantity/scalar/floatbase.rs index 84a37c9..77ce0d4 100644 --- a/src/quantity/scalar/floatbase.rs +++ b/src/quantity/scalar/floatbase.rs @@ -14,8 +14,8 @@ use std::ops::{ use std::cmp::Ordering; use super::ScalarBase; -use super::SHOW_SIG; -use super::MAX_LEN; +use super::dec_to_sci; + #[derive(Debug)] #[derive(Clone)] @@ -34,87 +34,7 @@ impl FloatBase { impl ToString for FloatBase { fn to_string(&self) -> String { - // Decimal, looks like xxx.xxx. - // May start with a zero. - let mut s = self.val.to_string(); - - // Remove negative sign from string - let neg = s.starts_with("-"); - if neg { s = String::from(&s[1..]); } - - // Power of ten - let mut p: i32 = { - if let Some(x) = s.find(".") { - x as i32 - } else { - s.len() as i32 - } - }; - p -= 1; - - // We no longer need a decimal point in our string. - // also, trim off leading zeros and adjust power. - let mut s: &str = &s.replace(".", ""); - s = &s[0..]; - s = s.trim_end_matches('0'); - while s.starts_with('0') { - s = &s[1..]; - p -= 1; - } - - - // Pick significant digits and round - let mut s = String::from(s); - if s.len() > SHOW_SIG { - let round; - if s.len() != SHOW_SIG + 1 { - round = s[SHOW_SIG..SHOW_SIG+1].parse().unwrap(); - } else { round = 0; } - - s = String::from(&s[0..SHOW_SIG]); - - if round >= 5 { - let new = s[s.len()-1..s.len()].parse::().unwrap() + 1u8; - if new != 10 { - s = format!("{}{new}", &s[0..s.len()-1]); - } - } - } - - s = format!("{s}{}", "0".repeat(SHOW_SIG - s.len())); - // at this point, s is guaranteed to have exactly SHOW_SIG digits. - - let neg = if neg {"-"} else {""}; - - if (p.abs() as usize) < MAX_LEN { - if p >= 0 { - let q = p as usize; - - let first = &s[0..q+1]; - let mut rest = &s[q+1..]; - rest = rest.trim_end_matches('0'); - if rest == "" { - return format!("{neg}{first}"); - } else { - return format!("{neg}{first}.{rest}"); - } - } else { - let q = p.abs() as usize; - let t = format!("0.{}{s}", "0".repeat(q-1)); - return format!("{neg}{}", t.trim_end_matches('0')); - } - - // Print full scientific notation - } else { - let first = &s[0..1]; - let mut rest = &s[1..]; - rest = rest.trim_end_matches('0'); - if rest == "" { - return format!("{neg}{first}e{p}"); - } else { - return format!("{neg}{first}.{rest}e{p}"); - } - } + return dec_to_sci(self.val.to_string()); } } diff --git a/src/quantity/scalar/mod.rs b/src/quantity/scalar/mod.rs index 8f8b8ed..7775380 100644 --- a/src/quantity/scalar/mod.rs +++ b/src/quantity/scalar/mod.rs @@ -18,4 +18,91 @@ pub use f64base::F64Base as FloatBase; mod scalar; pub use self::scalar::Scalar; -pub use self::scalar::ScalarBase; \ No newline at end of file +pub use self::scalar::ScalarBase; + + + +// Convert a string to scientific notation, +// with parameters SHOW_SIG and MAX_LEN. +// +// input (s): a decimal of any length, like 123123.123123 +// s may start with an optional `-` sign. +pub(in self) fn dec_to_sci(mut s: String) -> String { + // Remove negative sign from string + let neg = s.starts_with("-"); + if neg { s = String::from(&s[1..]); } + + // Power of ten + let mut p: i32 = { + if let Some(x) = s.find(".") { + x as i32 + } else { + s.len() as i32 + } + }; + p -= 1; + + // We no longer need a decimal point in our string. + // also, trim off leading zeros and adjust power. + let mut s: &str = &s.replace(".", ""); + s = &s[0..]; + s = s.trim_end_matches('0'); + while s.starts_with('0') { + s = &s[1..]; + p -= 1; + } + + + // Pick significant digits and round + let mut s = String::from(s); + if s.len() > SHOW_SIG { + let round; + if s.len() != SHOW_SIG + 1 { + round = s[SHOW_SIG..SHOW_SIG+1].parse().unwrap(); + } else { round = 0; } + + s = String::from(&s[0..SHOW_SIG]); + + if round >= 5 { + let new = s[s.len()-1..s.len()].parse::().unwrap() + 1u8; + if new != 10 { + s = format!("{}{new}", &s[0..s.len()-1]); + } + } + } + + s = format!("{s}{}", "0".repeat(SHOW_SIG - s.len())); + // at this point, s is guaranteed to have exactly SHOW_SIG digits. + + let neg = if neg {"-"} else {""}; + + if (p.abs() as usize) < MAX_LEN { + if p >= 0 { + let q = p as usize; + + let first = &s[0..q+1]; + let mut rest = &s[q+1..]; + rest = rest.trim_end_matches('0'); + if rest == "" { + return format!("{neg}{first}"); + } else { + return format!("{neg}{first}.{rest}"); + } + } else { + let q = p.abs() as usize; + let t = format!("0.{}{s}", "0".repeat(q-1)); + return format!("{neg}{}", t.trim_end_matches('0')); + } + + // Print full scientific notation + } else { + let first = &s[0..1]; + let mut rest = &s[1..]; + rest = rest.trim_end_matches('0'); + if rest == "" { + return format!("{neg}{first}e{p}"); + } else { + return format!("{neg}{first}.{rest}e{p}"); + } + } +} \ No newline at end of file From e1ba2a9c1f8baa9e2a387af159efc9a60d3b6de2 Mon Sep 17 00:00:00 2001 From: Mark Date: Sun, 3 Sep 2023 16:03:34 -0700 Subject: [PATCH 24/30] Whitespace --- src/quantity/scalar/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quantity/scalar/mod.rs b/src/quantity/scalar/mod.rs index 7775380..1b4833c 100644 --- a/src/quantity/scalar/mod.rs +++ b/src/quantity/scalar/mod.rs @@ -27,7 +27,7 @@ pub use self::scalar::ScalarBase; // // input (s): a decimal of any length, like 123123.123123 // s may start with an optional `-` sign. -pub(in self) fn dec_to_sci(mut s: String) -> String { +pub(in self) fn dec_to_sci(mut s: String) -> String { // Remove negative sign from string let neg = s.starts_with("-"); if neg { s = String::from(&s[1..]); } From 682205f5e1ee589249df653637cb41599f1f3bf9 Mon Sep 17 00:00:00 2001 From: Mark Date: Mon, 4 Sep 2023 13:49:20 -0700 Subject: [PATCH 25/30] Fixed function evaluator --- src/evaluate/evaluate.rs | 2 +- src/evaluate/function.rs | 65 ++++++++++++++++++++-------------------- 2 files changed, 33 insertions(+), 34 deletions(-) diff --git a/src/evaluate/evaluate.rs b/src/evaluate/evaluate.rs index 4c46d95..d80109a 100644 --- a/src/evaluate/evaluate.rs +++ b/src/evaluate/evaluate.rs @@ -63,7 +63,7 @@ pub fn evaluate( context.get_variable(&s) }, - Expression::Operator(_, Operator::Function(_), _) => { Some(eval_function(g)?) }, + Expression::Operator(_, Operator::Function(_), _) => { eval_function(g)? }, Expression::Operator(_, _, _) => { eval_operator(context, g)? }, }; diff --git a/src/evaluate/function.rs b/src/evaluate/function.rs index 005523d..e24a391 100644 --- a/src/evaluate/function.rs +++ b/src/evaluate/function.rs @@ -26,7 +26,7 @@ fn to_radians(q: Quantity) -> Result { -pub fn eval_function(g: &Expression) -> Result { +pub fn eval_function(g: &Expression) -> Result, (LineLocation, DaisyError)> { let Expression::Operator(loc, Operator::Function(f), args) = g else {unreachable!()}; @@ -41,113 +41,112 @@ pub fn eval_function(g: &Expression) -> Result { return Ok(Expression::Quantity(*loc + *l, q.without_unit())); } - Function::ToBase => { return Ok(Expression::Quantity(*loc + *l, q.convert_to_base())); } + Function::NoUnit => { return Ok(Some(Expression::Quantity(*loc + *l, q.without_unit()))); } + Function::ToBase => { return Ok(Some(Expression::Quantity(*loc + *l, q.convert_to_base()))); } Function::Abs => { if !q.unitless() { return Err((*loc + *l, DaisyError::IncompatibleUnit));} - return Ok(Expression::Quantity(*loc + *l, q.abs())); + return Ok(Some(Expression::Quantity(*loc + *l, q.abs()))); }, Function::Floor => { if !q.unitless() { return Err((*loc + *l, DaisyError::IncompatibleUnit));} - return Ok(Expression::Quantity(*loc + *l, q.floor())); + return Ok(Some(Expression::Quantity(*loc + *l, q.floor()))); }, Function::Ceil => { if !q.unitless() { return Err((*loc + *l, DaisyError::IncompatibleUnit));} - return Ok(Expression::Quantity(*loc + *l, q.ceil())); + return Ok(Some(Expression::Quantity(*loc + *l, q.ceil()))); }, Function::Round => { if !q.unitless() { return Err((*loc + *l, DaisyError::IncompatibleUnit));} - return Ok(Expression::Quantity(*loc + *l, q.round())); + return Ok(Some(Expression::Quantity(*loc + *l, q.round()))); }, Function::NaturalLog => { if !q.unitless() { return Err((*loc + *l, DaisyError::IncompatibleUnit));} - return Ok(Expression::Quantity(*loc + *l, q.ln())); + return Ok(Some(Expression::Quantity(*loc + *l, q.ln()))); }, Function::TenLog => { if !q.unitless() { return Err((*loc + *l, DaisyError::IncompatibleUnit));} - return Ok(Expression::Quantity(*loc + *l, q.log10())); + return Ok(Some(Expression::Quantity(*loc + *l, q.log10()))); }, Function::Sin => { let Ok(q) = to_radians(q.clone()) else { return Err((*loc + *l, DaisyError::IncompatibleUnit)); }; - return Ok(Expression::Quantity(*loc + *l, q.sin())); + return Ok(Some(Expression::Quantity(*loc + *l, q.sin()))); }, Function::Cos => { let Ok(q) = to_radians(q.clone()) else { return Err((*loc + *l, DaisyError::IncompatibleUnit)); }; - return Ok(Expression::Quantity(*loc + *l, q.cos())); + return Ok(Some(Expression::Quantity(*loc + *l, q.cos()))); }, Function::Tan => { let Ok(q) = to_radians(q.clone()) else { return Err((*loc + *l, DaisyError::IncompatibleUnit)); }; - return Ok(Expression::Quantity(*loc + *l, q.tan())); + return Ok(Some(Expression::Quantity(*loc + *l, q.tan()))); }, Function::Csc => { let Ok(q) = to_radians(q.clone()) else { return Err((*loc + *l, DaisyError::IncompatibleUnit)); }; - return Ok(Expression::Quantity(*loc + *l, q.csc())); + return Ok(Some(Expression::Quantity(*loc + *l, q.csc()))); }, Function::Sec => { let Ok(q) = to_radians(q.clone()) else { return Err((*loc + *l, DaisyError::IncompatibleUnit)); }; - return Ok(Expression::Quantity(*loc + *l, q.sec())); + return Ok(Some(Expression::Quantity(*loc + *l, q.sec()))); }, Function::Cot => { let Ok(q) = to_radians(q.clone()) else { return Err((*loc + *l, DaisyError::IncompatibleUnit)); }; - return Ok(Expression::Quantity(*loc + *l, q.cot())); + return Ok(Some(Expression::Quantity(*loc + *l, q.cot()))); }, Function::Sinh => { let Ok(q) = to_radians(q.clone()) else { return Err((*loc + *l, DaisyError::IncompatibleUnit)); }; - return Ok(Expression::Quantity(*loc + *l, q.sinh())); + return Ok(Some(Expression::Quantity(*loc + *l, q.sinh()))); }, Function::Cosh => { let Ok(q) = to_radians(q.clone()) else { return Err((*loc + *l, DaisyError::IncompatibleUnit)); }; - return Ok(Expression::Quantity(*loc + *l, q.cosh())); + return Ok(Some(Expression::Quantity(*loc + *l, q.cosh()))); }, Function::Tanh => { let Ok(q) = to_radians(q.clone()) else { return Err((*loc + *l, DaisyError::IncompatibleUnit)); }; - return Ok(Expression::Quantity(*loc + *l, q.tanh())); + return Ok(Some(Expression::Quantity(*loc + *l, q.tanh()))); }, Function::Csch => { let Ok(q) = to_radians(q.clone()) else { return Err((*loc + *l, DaisyError::IncompatibleUnit)); }; - return Ok(Expression::Quantity(*loc + *l, q.csch())); + return Ok(Some(Expression::Quantity(*loc + *l, q.csch()))); }, Function::Sech => { let Ok(q) = to_radians(q.clone()) else { return Err((*loc + *l, DaisyError::IncompatibleUnit)); }; - return Ok(Expression::Quantity(*loc + *l, q.sech())); + return Ok(Some(Expression::Quantity(*loc + *l, q.sech()))); }, Function::Coth => { let Ok(q) = to_radians(q.clone()) else { return Err((*loc + *l, DaisyError::IncompatibleUnit)); }; - return Ok(Expression::Quantity(*loc + *l, q.coth())); + return Ok(Some(Expression::Quantity(*loc + *l, q.coth()))); }, Function::Asin => { if !q.unitless() { return Err((*loc + *l, DaisyError::IncompatibleUnit));} - return Ok(Expression::Quantity(*loc + *l, q.asin())); + return Ok(Some(Expression::Quantity(*loc + *l, q.asin()))); }, Function::Acos => { if !q.unitless() { return Err((*loc + *l, DaisyError::IncompatibleUnit));} - return Ok(Expression::Quantity(*loc + *l, q.acos())); + return Ok(Some(Expression::Quantity(*loc + *l, q.acos()))); }, Function::Atan => { if !q.unitless() { return Err((*loc + *l, DaisyError::IncompatibleUnit));} - return Ok(Expression::Quantity(*loc + *l, q.atan())); + return Ok(Some(Expression::Quantity(*loc + *l, q.atan()))); }, Function::Asinh => { if !q.unitless() { return Err((*loc + *l, DaisyError::IncompatibleUnit));} - return Ok(Expression::Quantity(*loc + *l, q.asinh())); + return Ok(Some(Expression::Quantity(*loc + *l, q.asinh()))); }, Function::Acosh => { if !q.unitless() { return Err((*loc + *l, DaisyError::IncompatibleUnit));} - return Ok(Expression::Quantity(*loc + *l, q.acosh())); + return Ok(Some(Expression::Quantity(*loc + *l, q.acosh()))); }, Function::Atanh => { if !q.unitless() { return Err((*loc + *l, DaisyError::IncompatibleUnit));} - return Ok(Expression::Quantity(*loc + *l, q.atanh())); + return Ok(Some(Expression::Quantity(*loc + *l, q.atanh()))); }, @@ -160,7 +159,7 @@ pub fn eval_function(g: &Expression) -> Result { let mut k = Quantity::new_rational(1f64).unwrap(); @@ -172,7 +171,7 @@ pub fn eval_function(g: &Expression) -> Result { if !q.unitless() { return Err((*loc + *l, DaisyError::IncompatibleUnit));} @@ -181,7 +180,7 @@ pub fn eval_function(g: &Expression) -> Result { if !q.unitless() { return Err((*loc + *l, DaisyError::IncompatibleUnit));} @@ -191,7 +190,7 @@ pub fn eval_function(g: &Expression) -> Result Date: Wed, 20 Sep 2023 11:06:37 -0700 Subject: [PATCH 26/30] Minor cleanup --- src/context.rs | 6 +++--- src/evaluate/operator.rs | 3 ++- src/quantity/unit/unit.rs | 1 - 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/context.rs b/src/context.rs index c61444c..3839f2f 100644 --- a/src/context.rs +++ b/src/context.rs @@ -61,7 +61,7 @@ impl Config { #[derive(Debug)] -#[derive(Clone)] +//#[derive(Clone)] pub struct Context { pub config: Config, @@ -76,12 +76,12 @@ pub struct Context { // General functions impl Context { pub fn new() -> Context { - Context{ + Context { config: Config::new(), history: Vec::new(), variables: HashMap::new(), functions: HashMap::new(), - shadow: HashMap::new(), + shadow: HashMap::new() } } diff --git a/src/evaluate/operator.rs b/src/evaluate/operator.rs index f5fbf8a..0851212 100644 --- a/src/evaluate/operator.rs +++ b/src/evaluate/operator.rs @@ -164,7 +164,8 @@ pub fn eval_operator(context: &mut Context, g: &Expression) -> Result Date: Wed, 20 Sep 2023 11:07:47 -0700 Subject: [PATCH 27/30] Updated scalar print routine --- src/quantity/scalar/f64base.rs | 28 ++++++++++++++++++++- src/quantity/scalar/floatbase.rs | 30 ++++++++++++++++++++++- src/quantity/scalar/mod.rs | 42 ++++++++++---------------------- 3 files changed, 69 insertions(+), 31 deletions(-) diff --git a/src/quantity/scalar/f64base.rs b/src/quantity/scalar/f64base.rs index 5e9bcaf..f9f445e 100644 --- a/src/quantity/scalar/f64base.rs +++ b/src/quantity/scalar/f64base.rs @@ -27,7 +27,33 @@ pub struct F64Base where { impl ToString for F64Base { fn to_string(&self) -> String { - return dec_to_sci(self.val.to_string()); + // Remove negative sign from string + let mut s = self.val.to_string(); + + let neg = s.starts_with("-"); + if neg { s = String::from(&s[1..]); } + + // Power of ten + let mut p: i64 = { + if let Some(x) = s.find(".") { + x as i64 + } else { + s.len() as i64 + } + }; + p -= 1; + + // We no longer need a decimal point in our string. + // also, trim off leading zeros and adjust power. + let mut s: &str = &s.replace(".", ""); + s = &s[0..]; + s = s.trim_end_matches('0'); + while s.starts_with('0') { + s = &s[1..]; + p -= 1; + } + + return dec_to_sci(neg, s.to_string(), p); } } diff --git a/src/quantity/scalar/floatbase.rs b/src/quantity/scalar/floatbase.rs index 77ce0d4..94ff120 100644 --- a/src/quantity/scalar/floatbase.rs +++ b/src/quantity/scalar/floatbase.rs @@ -34,7 +34,35 @@ impl FloatBase { impl ToString for FloatBase { fn to_string(&self) -> String { - return dec_to_sci(self.val.to_string()); + + if self.val.is_nan() { + return "NaN".to_string(); + } else if self.val.is_inf_neg() { + return "-Inf".to_string(); + } else if self.val.is_inf_pos() { + return "+Inf".to_string(); + } + + + // Already in scientific notation,we just need to trim significant digits. + let mut _a = self.val.round(32, astro_float::RoundingMode::Up).to_string(); + let mut _b = _a.split('e'); + + let mut s = String::from(_b.next().unwrap()); // Decimal + let p: i64 = _b.next().unwrap().parse().unwrap(); // Exponent + + // Remove negative sign from string + let neg = s.starts_with("-"); + if neg { s = String::from(&s[1..]); } + + // We no longer need a decimal point in our string. + // also, trim off leading zeros and adjust power. + let mut s: &str = &s.replace(".", ""); + s = &s[0..]; + s = s.trim_end_matches('0'); + s = s.trim_start_matches('0'); + + return dec_to_sci(neg, s.to_string(), p); } } diff --git a/src/quantity/scalar/mod.rs b/src/quantity/scalar/mod.rs index 1b4833c..976998d 100644 --- a/src/quantity/scalar/mod.rs +++ b/src/quantity/scalar/mod.rs @@ -25,36 +25,17 @@ pub use self::scalar::ScalarBase; // Convert a string to scientific notation, // with parameters SHOW_SIG and MAX_LEN. // -// input (s): a decimal of any length, like 123123.123123 -// s may start with an optional `-` sign. -pub(in self) fn dec_to_sci(mut s: String) -> String { - // Remove negative sign from string - let neg = s.starts_with("-"); - if neg { s = String::from(&s[1..]); } - - // Power of ten - let mut p: i32 = { - if let Some(x) = s.find(".") { - x as i32 - } else { - s.len() as i32 - } - }; - p -= 1; - - // We no longer need a decimal point in our string. - // also, trim off leading zeros and adjust power. - let mut s: &str = &s.replace(".", ""); - s = &s[0..]; - s = s.trim_end_matches('0'); - while s.starts_with('0') { - s = &s[1..]; - p -= 1; - } - +// input: +// neg: true if negative +// s: decimal portion. Must contain only digits and a single decimal point. +// zeros must be stripped from both ends. +// p: power of ten to multiply by. +// +// So, (-1)^(neg) + (s * 10^p) should give us our number. +#[allow(dead_code)] +pub(in self) fn dec_to_sci(neg: bool, mut s: String, p: i64) -> String { // Pick significant digits and round - let mut s = String::from(s); if s.len() > SHOW_SIG { let round; if s.len() != SHOW_SIG + 1 { @@ -77,6 +58,8 @@ pub(in self) fn dec_to_sci(mut s: String) -> String { let neg = if neg {"-"} else {""}; if (p.abs() as usize) < MAX_LEN { + // Print whole decimal + if p >= 0 { let q = p as usize; @@ -94,8 +77,9 @@ pub(in self) fn dec_to_sci(mut s: String) -> String { return format!("{neg}{}", t.trim_end_matches('0')); } - // Print full scientific notation } else { + // Print full scientific notation + let first = &s[0..1]; let mut rest = &s[1..]; rest = rest.trim_end_matches('0'); From 31c368f7d8916482827ba28e73af0f1d902634e5 Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 20 Sep 2023 11:09:28 -0700 Subject: [PATCH 28/30] Removed dependencies for astrofloat --- Cargo.lock | 24 ++---------------------- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c133079..c64ad28 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,19 +8,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" -[[package]] -name = "bigdecimal" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "454bca3db10617b88b566f205ed190aedb0e0e6dd4cad61d3988a72e8c5594cb" -dependencies = [ - "autocfg", - "libm", - "num-bigint", - "num-integer", - "num-traits", -] - [[package]] name = "bitflags" version = "1.3.2" @@ -37,7 +24,6 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" name = "daisycalc" version = "1.0.1" dependencies = [ - "bigdecimal", "cfg-if", "num", "termion", @@ -62,15 +48,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.140" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" - -[[package]] -name = "libm" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "memchr" diff --git a/Cargo.toml b/Cargo.toml index b08301d..a0ff38a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,8 +28,8 @@ cfg-if = "1.0.0" [target.'cfg(target_family = "unix")'.dependencies] termion = "2.0.1" -bigdecimal = "0.4.1" num = "0.4.1" +#astro-float = "0.7.1" [build-dependencies] toml = "0.7.4" \ No newline at end of file From 315be575eeba689dfdad17db2693dbec7ae0ce74 Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 20 Sep 2023 11:09:44 -0700 Subject: [PATCH 29/30] Fixed small function --- src/quantity/scalar/scalar.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quantity/scalar/scalar.rs b/src/quantity/scalar/scalar.rs index e6a0d5a..0901c5f 100644 --- a/src/quantity/scalar/scalar.rs +++ b/src/quantity/scalar/scalar.rs @@ -184,7 +184,7 @@ impl Scalar { pub fn is_nan(&self) -> bool { match self { - Scalar::Float {..} => {false}, + Scalar::Float{ v } => {v.val.is_nan()}, Scalar::Rational {..} => {false} } } From d906c474c565f6b1f5a89402ffc2cf01924c41b5 Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 20 Sep 2023 11:17:15 -0700 Subject: [PATCH 30/30] Version bump --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c64ad28..62b4b82 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -22,7 +22,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "daisycalc" -version = "1.0.1" +version = "1.1.0" dependencies = [ "cfg-if", "num", diff --git a/Cargo.toml b/Cargo.toml index a0ff38a..327e09e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "daisycalc" -version = "1.0.1" +version = "1.1.0" edition = "2021" build = "buildscript/main.rs" license = "GPL-3.0-only"