Add engineering mode

This commit is contained in:
PaddiM8 2024-03-23 01:34:07 +01:00
parent 22ccb03ec9
commit 90b6bed227
5 changed files with 105 additions and 32 deletions

View File

@ -1,6 +1,7 @@
mod output; mod output;
mod repl; mod repl;
use kalk::kalk_value::ScientificNotationFormat;
use kalk::parser; use kalk::parser;
use seahorse::{App, Context, Flag, FlagType}; use seahorse::{App, Context, Flag, FlagType};
use std::env; use std::env;
@ -24,6 +25,10 @@ fn main() {
.description("Specify number precision") .description("Specify number precision")
.alias("p"), .alias("p"),
) )
.flag(
Flag::new("eng", FlagType::Bool)
.description("Engineering mode")
)
.flag( .flag(
Flag::new("angle-unit", FlagType::String) Flag::new("angle-unit", FlagType::String)
.description("Unit used for angles, either rad or deg. This can also be specified using an environment variable with the name 'ANGLE_UNIT'.") .description("Unit used for angles, either rad or deg. This can also be specified using an environment variable with the name 'ANGLE_UNIT'.")
@ -58,6 +63,12 @@ fn default_action(context: &Context) {
let precision = context let precision = context
.int_flag("precision") .int_flag("precision")
.unwrap_or(output::DEFAULT_PRECISION as isize) as u32; .unwrap_or(output::DEFAULT_PRECISION as isize) as u32;
let format = if context.bool_flag("eng") {
ScientificNotationFormat::Engineering
} else {
ScientificNotationFormat::Normal
};
if let Ok(max_recursion_depth) = context.int_flag("max-recursion-depth") { if let Ok(max_recursion_depth) = context.int_flag("max-recursion-depth") {
parser_context = parser_context.set_max_recursion_depth(max_recursion_depth as u32); parser_context = parser_context.set_max_recursion_depth(max_recursion_depth as u32);
} }
@ -72,7 +83,7 @@ fn default_action(context: &Context) {
if context.args.is_empty() { if context.args.is_empty() {
// REPL // REPL
repl::start(&mut parser_context, precision); repl::start(&mut parser_context, precision, format);
} else { } else {
// Direct output // Direct output
output::eval( output::eval(
@ -80,6 +91,7 @@ fn default_action(context: &Context) {
&context.args.join(" "), &context.args.join(" "),
precision, precision,
10u8, 10u8,
format,
); );
} }
} }

View File

@ -1,9 +1,9 @@
use ansi_term::Colour::Red; use ansi_term::Colour::Red;
use kalk::parser; use kalk::{kalk_value::ScientificNotationFormat, parser};
pub(crate) const DEFAULT_PRECISION: u32 = 63; pub(crate) const DEFAULT_PRECISION: u32 = 63;
pub fn eval(parser: &mut parser::Context, input: &str, precision: u32, base: u8) { pub fn eval(parser: &mut parser::Context, input: &str, precision: u32, base: u8, format: ScientificNotationFormat) {
match parser::eval(parser, input, precision) { match parser::eval(parser, input, precision) {
Ok(Some(mut result)) => { Ok(Some(mut result)) => {
if !result.set_radix(base) { if !result.set_radix(base) {
@ -13,7 +13,7 @@ pub fn eval(parser: &mut parser::Context, input: &str, precision: u32, base: u8)
} }
if precision == DEFAULT_PRECISION { if precision == DEFAULT_PRECISION {
println!("{}", result.to_string_pretty()); println!("{}", result.to_string_pretty_format(format));
return; return;
} }

View File

@ -1,5 +1,6 @@
use crate::output; use crate::output;
use ansi_term::Colour::{self, Cyan}; use ansi_term::Colour::{self, Cyan};
use kalk::kalk_value::ScientificNotationFormat;
use kalk::parser; use kalk::parser;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use regex::Captures; use regex::Captures;
@ -24,7 +25,7 @@ struct Context {
base: u8, base: u8,
} }
pub fn start(parser: &mut parser::Context, precision: u32) { pub fn start(parser: &mut parser::Context, precision: u32, format: ScientificNotationFormat) {
let mut editor = Editor::<RLHelper>::new(); let mut editor = Editor::<RLHelper>::new();
editor.set_helper(Some(RLHelper { editor.set_helper(Some(RLHelper {
highlighter: LineHighlighter {}, highlighter: LineHighlighter {},
@ -66,7 +67,7 @@ pub fn start(parser: &mut parser::Context, precision: u32) {
match readline { match readline {
Ok(input) => { Ok(input) => {
editor.add_history_entry(input.as_str()); editor.add_history_entry(input.as_str());
eval_repl(&mut repl, parser, &input, precision); eval_repl(&mut repl, parser, &input, precision, format);
} }
Err(ReadlineError::Interrupted) => break, Err(ReadlineError::Interrupted) => break,
_ => break, _ => break,
@ -78,7 +79,7 @@ pub fn start(parser: &mut parser::Context, precision: u32) {
} }
} }
fn eval_repl(repl: &mut self::Context, parser: &mut parser::Context, input: &str, precision: u32) { fn eval_repl(repl: &mut self::Context, parser: &mut parser::Context, input: &str, precision: u32, format: ScientificNotationFormat) {
if let Some(file_name) = input.strip_prefix("load ") { if let Some(file_name) = input.strip_prefix("load ") {
if let Some(file_path) = crate::get_input_file_by_name(file_name) { if let Some(file_path) = crate::get_input_file_by_name(file_name) {
crate::load_input_file(&file_path, precision, parser); crate::load_input_file(&file_path, precision, parser);
@ -109,7 +110,7 @@ fn eval_repl(repl: &mut self::Context, parser: &mut parser::Context, input: &str
"clear" => print!("\x1B[2J"), "clear" => print!("\x1B[2J"),
"exit" => process::exit(0), "exit" => process::exit(0),
"help" => print_cli_help(), "help" => print_cli_help(),
_ => output::eval(parser, input, precision, repl.base), _ => output::eval(parser, input, precision, repl.base, format),
} }
} }

View File

@ -1,6 +1,6 @@
use wasm_bindgen::prelude::wasm_bindgen; use wasm_bindgen::prelude::wasm_bindgen;
use crate::kalk_value::{ComplexNumberType, KalkValue, ScientificNotation}; use crate::kalk_value::{ComplexNumberType, KalkValue, ScientificNotation, ScientificNotationFormat};
#[wasm_bindgen] #[wasm_bindgen]
pub struct CalculationResult { pub struct CalculationResult {
@ -36,15 +36,15 @@ impl CalculationResult {
self.value.to_string_big() self.value.to_string_big()
} }
#[wasm_bindgen(js_name = toPrettyString)] #[wasm_bindgen(js_name = toPrettyStringWithFormat)]
pub fn to_string_pretty(&self) -> String { pub fn to_string_pretty_format(&self, format: ScientificNotationFormat) -> String {
let value = if self.radix == 10 { let value = if self.radix == 10 {
self.value.to_string_pretty_radix(10) self.value.to_string_pretty_radix(10, format)
} else { } else {
format!( format!(
"{}\n{}", "{}\n{}",
self.value.to_string_pretty_radix(10), self.value.to_string_pretty_radix(10, format),
self.value.to_string_pretty_radix(self.radix), self.value.to_string_pretty_radix(self.radix, format),
) )
}; };
@ -55,6 +55,11 @@ impl CalculationResult {
} }
} }
#[wasm_bindgen(js_name = toPrettyString)]
pub fn to_string_pretty(&self) -> String {
self.to_string_pretty_format(ScientificNotationFormat::Normal)
}
#[wasm_bindgen(js_name = getValue)] #[wasm_bindgen(js_name = getValue)]
pub fn to_f64(&self) -> f64 { pub fn to_f64(&self) -> f64 {
self.value.to_f64() self.value.to_f64()

View File

@ -2,7 +2,7 @@
pub mod with_rug; pub mod with_rug;
#[cfg(feature = "rug")] #[cfg(feature = "rug")]
use rug::Float; use rug::{Float, ops::Pow};
#[cfg(feature = "rug")] #[cfg(feature = "rug")]
pub use with_rug::*; pub use with_rug::*;
@ -108,7 +108,6 @@ macro_rules! as_number_or_zero {
#[wasm_bindgen] #[wasm_bindgen]
#[derive(Clone)] #[derive(Clone)]
pub struct ScientificNotation { pub struct ScientificNotation {
pub negative: bool,
pub value: f64, pub value: f64,
pub exponent: i32, pub exponent: i32,
pub imaginary: bool, pub imaginary: bool,
@ -121,17 +120,42 @@ pub enum ComplexNumberType {
Imaginary, Imaginary,
} }
#[wasm_bindgen]
#[derive(Clone, Copy)]
pub enum ScientificNotationFormat {
Normal,
Engineering,
}
#[wasm_bindgen] #[wasm_bindgen]
impl ScientificNotation { impl ScientificNotation {
#[wasm_bindgen(js_name = toString)] #[wasm_bindgen(js_name = toString)]
pub fn to_js_string(&self) -> String { pub fn to_js_string(&self) -> String {
self.to_string() self.to_string()
} }
pub fn to_string_format(&self, format: ScientificNotationFormat) -> String {
match format {
ScientificNotationFormat::Normal => self.to_string(),
ScientificNotationFormat::Engineering => self.to_string_eng(),
}
}
fn to_string_eng(&self) -> String {
let exponent = self.exponent - 1;
let modulo = exponent % 3;
let value = self.value * 10_f64.powi(modulo);
ScientificNotation {
value,
exponent: exponent - modulo + 1,
imaginary: self.imaginary,
}.to_string()
}
} }
impl std::fmt::Display for ScientificNotation { impl std::fmt::Display for ScientificNotation {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let sign = if self.negative { "-" } else { "" };
let digits_and_mul = if self.value == 1f64 { let digits_and_mul = if self.value == 1f64 {
String::new() String::new()
} else { } else {
@ -140,11 +164,10 @@ impl std::fmt::Display for ScientificNotation {
write!( write!(
f, f,
"{}{}10^{} {}", "{}10^{}{}",
sign,
digits_and_mul, digits_and_mul,
self.exponent - 1, self.exponent - 1,
if self.imaginary { "i" } else { "" } if self.imaginary { " i" } else { "" }
) )
} }
} }
@ -288,7 +311,7 @@ impl KalkValue {
} }
} }
pub fn to_string_pretty_radix(&self, radix: u8) -> String { pub fn to_string_pretty_radix(&self, radix: u8, format: ScientificNotationFormat) -> String {
let (real, imaginary, unit) = match self { let (real, imaginary, unit) = match self {
KalkValue::Number(real, imaginary, unit) => (real, imaginary, unit), KalkValue::Number(real, imaginary, unit) => (real, imaginary, unit),
_ => return self.to_string(), _ => return self.to_string(),
@ -308,21 +331,31 @@ impl KalkValue {
let mut new_real = real.clone(); let mut new_real = real.clone();
let mut new_imaginary = imaginary.clone(); let mut new_imaginary = imaginary.clone();
let mut has_scientific_notation = false; let mut has_scientific_notation = false;
let result_str = if (-6..8).contains(&sci_notation_real.exponent) || real == &0f64 { let is_engineering_mode = matches!(format, ScientificNotationFormat::Engineering);
let result_str = if is_engineering_mode {
has_scientific_notation = true;
sci_notation_real.to_string_format(ScientificNotationFormat::Engineering)
} else if (-6..8).contains(&sci_notation_real.exponent) || real == &0f64 {
self.to_string_real(radix) self.to_string_real(radix)
} else if sci_notation_real.exponent <= -14 { } else if sci_notation_real.exponent <= -14 {
new_real = float!(0); new_real = float!(0);
String::from("0") String::from("0")
} else if radix == 10 { } else if radix == 10 {
has_scientific_notation = true; has_scientific_notation = true;
sci_notation_real.to_string().trim().to_string() sci_notation_real.to_string_format(format)
} else { } else {
return String::new(); return String::new();
}; };
let sci_notation_imaginary = self.to_scientific_notation(ComplexNumberType::Imaginary); let sci_notation_imaginary = self.to_scientific_notation(ComplexNumberType::Imaginary);
let result_str_imaginary = if (-6..8).contains(&sci_notation_imaginary.exponent) let result_str_imaginary = if is_engineering_mode {
has_scientific_notation = true;
sci_notation_imaginary.to_string_format(ScientificNotationFormat::Engineering)
} else if (-6..8).contains(&sci_notation_imaginary.exponent)
|| imaginary == &0f64 || imaginary == &0f64
|| imaginary == &1f64 || imaginary == &1f64
{ {
@ -333,7 +366,7 @@ impl KalkValue {
} else if radix == 10 { } else if radix == 10 {
has_scientific_notation = true; has_scientific_notation = true;
sci_notation_imaginary.to_string().trim().to_string() sci_notation_imaginary.to_string_format(format)
} else { } else {
return String::new(); return String::new();
}; };
@ -368,15 +401,15 @@ impl KalkValue {
if estimate != output && radix == 10 { if estimate != output && radix == 10 {
output.push_str(&format!("{}", estimate)); output.push_str(&format!("{}", estimate));
} }
} else if has_scientific_notation { } else if has_scientific_notation && !is_engineering_mode {
output.insert_str(0, &format!("{}", self)); output.insert_str(0, &format!("{}", self));
} }
output output
} }
pub fn to_string_pretty(&self) -> String { pub fn to_string_pretty(&self, format: ScientificNotationFormat) -> String {
self.to_string_pretty_radix(10) self.to_string_pretty_radix(10, format)
} }
pub fn to_string_with_unit(&self) -> String { pub fn to_string_with_unit(&self) -> String {
@ -516,7 +549,6 @@ impl KalkValue {
let exponent = value.abs().log10().floor() as i32 + 1; let exponent = value.abs().log10().floor() as i32 + 1;
ScientificNotation { ScientificNotation {
negative: value < 0f64,
value: value / (10f64.powf(exponent as f64 - 1f64) as f64), value: value / (10f64.powf(exponent as f64 - 1f64) as f64),
// I... am not sure what else to do... // I... am not sure what else to do...
exponent, exponent,
@ -1309,7 +1341,6 @@ fn pow(x: f64, y: f64) -> f64 {
#[cfg(feature = "rug")] #[cfg(feature = "rug")]
fn pow(x: Float, y: Float) -> Float { fn pow(x: Float, y: Float) -> Float {
use rug::ops::Pow;
x.pow(y) x.pow(y)
} }
@ -1377,9 +1408,11 @@ impl From<i32> for KalkValue {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::kalk_value::{spaced, KalkValue}; use crate::kalk_value::{spaced, KalkValue, ScientificNotationFormat};
use crate::test_helpers::cmp; use crate::test_helpers::cmp;
use super::ScientificNotation;
#[test] #[test]
fn test_spaced() { fn test_spaced() {
assert_eq!(spaced("1"), String::from("1")); assert_eq!(spaced("1"), String::from("1"));
@ -1543,8 +1576,30 @@ mod tests {
(float!(3.00000000004), float!(0.0), "3"), (float!(3.00000000004), float!(0.0), "3"),
]; ];
for (real, imaginary, output) in in_out { for (real, imaginary, output) in in_out {
let result = KalkValue::Number(real, imaginary, None).to_string_pretty(); let result = KalkValue::Number(real, imaginary, None).to_string_pretty(ScientificNotationFormat::Normal);
assert_eq!(output, result); assert_eq!(output, result);
} }
} }
#[test]
fn test_eng_mode() {
let in_out = vec![
(1.23, 0, "1.23×10^0"),
(1.23, 1, "12.3×10^0"),
(1.23, 2, "123×10^0"),
(1.23, 3, "1.23×10^3"),
(1.23, 4, "12.3×10^3"),
(1.23, 5, "123×10^3"),
(1.23, 6, "1.23×10^6"),
];
for (value, exponent, output) in in_out {
let sci = ScientificNotation {
value,
exponent: exponent + 1,
imaginary: false,
};
assert_eq!(sci.to_string_format(ScientificNotationFormat::Engineering), output);
}
}
} }