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 repl;
use kalk::kalk_value::ScientificNotationFormat;
use kalk::parser;
use seahorse::{App, Context, Flag, FlagType};
use std::env;
@ -24,6 +25,10 @@ fn main() {
.description("Specify number precision")
.alias("p"),
)
.flag(
Flag::new("eng", FlagType::Bool)
.description("Engineering mode")
)
.flag(
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'.")
@ -58,6 +63,12 @@ fn default_action(context: &Context) {
let precision = context
.int_flag("precision")
.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") {
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() {
// REPL
repl::start(&mut parser_context, precision);
repl::start(&mut parser_context, precision, format);
} else {
// Direct output
output::eval(
@ -80,6 +91,7 @@ fn default_action(context: &Context) {
&context.args.join(" "),
precision,
10u8,
format,
);
}
}

View File

@ -1,9 +1,9 @@
use ansi_term::Colour::Red;
use kalk::parser;
use kalk::{kalk_value::ScientificNotationFormat, parser};
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) {
Ok(Some(mut result)) => {
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 {
println!("{}", result.to_string_pretty());
println!("{}", result.to_string_pretty_format(format));
return;
}

View File

@ -1,5 +1,6 @@
use crate::output;
use ansi_term::Colour::{self, Cyan};
use kalk::kalk_value::ScientificNotationFormat;
use kalk::parser;
use lazy_static::lazy_static;
use regex::Captures;
@ -24,7 +25,7 @@ struct Context {
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();
editor.set_helper(Some(RLHelper {
highlighter: LineHighlighter {},
@ -66,7 +67,7 @@ pub fn start(parser: &mut parser::Context, precision: u32) {
match readline {
Ok(input) => {
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,
_ => 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_path) = crate::get_input_file_by_name(file_name) {
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"),
"exit" => process::exit(0),
"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 crate::kalk_value::{ComplexNumberType, KalkValue, ScientificNotation};
use crate::kalk_value::{ComplexNumberType, KalkValue, ScientificNotation, ScientificNotationFormat};
#[wasm_bindgen]
pub struct CalculationResult {
@ -36,15 +36,15 @@ impl CalculationResult {
self.value.to_string_big()
}
#[wasm_bindgen(js_name = toPrettyString)]
pub fn to_string_pretty(&self) -> String {
#[wasm_bindgen(js_name = toPrettyStringWithFormat)]
pub fn to_string_pretty_format(&self, format: ScientificNotationFormat) -> String {
let value = if self.radix == 10 {
self.value.to_string_pretty_radix(10)
self.value.to_string_pretty_radix(10, format)
} else {
format!(
"{}\n{}",
self.value.to_string_pretty_radix(10),
self.value.to_string_pretty_radix(self.radix),
self.value.to_string_pretty_radix(10, format),
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)]
pub fn to_f64(&self) -> f64 {
self.value.to_f64()

View File

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