1#[cfg(feature = "rug")]
2pub mod with_rug;
3
4#[cfg(feature = "rug")]
5use rug::{ops::Pow, Float};
6
7#[cfg(not(feature = "rug"))]
8pub mod regular;
9
10mod rounding;
11
12use crate::ast::Expr;
13use crate::errors::KalkError;
14use crate::radix;
15use wasm_bindgen::prelude::*;
16
17use self::rounding::EstimationResult;
18
19const ACCEPTABLE_COMPARISON_MARGIN: f64 = 0.00000001;
20
21#[cfg(feature = "rug")]
22pub(crate) type KalkFloat = rug::Float;
23
24#[cfg(not(feature = "rug"))]
25pub(crate) type KalkFloat = f64;
26
27#[macro_export]
28#[cfg(not(feature = "rug"))]
29macro_rules! float {
30 ($x:expr) => {{
31 $x.clone() as f64
32 }};
33}
34
35#[macro_export]
36#[cfg(feature = "rug")]
37macro_rules! float {
38 ($x:expr) => {{
39 use rug::Float;
40 Float::with_val(1024, $x)
41 }};
42}
43
44#[macro_export]
45#[cfg(not(feature = "rug"))]
46macro_rules! primitive {
47 ($x:expr) => {{
48 $x.clone()
49 }};
50}
51
52#[macro_export]
53#[cfg(feature = "rug")]
54macro_rules! primitive {
55 ($x:expr) => {{
56 $x.to_f64()
57 }};
58}
59
60#[macro_export]
61macro_rules! as_number_or_return {
62 ($x:expr) => {{
63 if let KalkValue::Number(real, imaginary, unit) = $x {
64 (
65 real,
66 if imaginary == -0f64 {
67 float!(0)
68 } else {
69 imaginary
70 },
71 unit,
72 )
73 } else {
74 return Err(KalkError::UnexpectedType(
75 $x.get_type_name(),
76 vec![String::from("number")],
77 ));
78 }
79 }};
80}
81
82#[macro_export]
83macro_rules! as_vector_or_return {
84 ($x:expr) => {{
85 if let KalkValue::Vector(values) = $x {
86 if values.len() == 0 {
87 return Err(KalkError::Expected(String::from("a non-empty vector")));
88 }
89
90 values
91 } else {
92 return Err(KalkError::UnexpectedType(
93 $x.get_type_name(),
94 vec![String::from("vector")],
95 ));
96 }
97 }};
98}
99
100#[macro_export]
101macro_rules! as_number_or_zero {
102 ($x:expr) => {{
103 use $crate::float;
104 if let KalkValue::Number(real, imaginary, unit) = $x {
105 (real, imaginary, unit)
106 } else {
107 (float!(0), float!(0), None)
108 }
109 }};
110}
111
112#[wasm_bindgen]
113#[derive(Clone)]
114pub struct ScientificNotation {
115 pub value: f64,
116 pub exponent: i32,
117 pub imaginary: bool,
118}
119
120#[wasm_bindgen]
121#[derive(PartialEq, Eq)]
122pub enum ComplexNumberType {
123 Real,
124 Imaginary,
125}
126
127#[wasm_bindgen]
128#[derive(Clone, Copy)]
129pub enum ScientificNotationFormat {
130 Normal,
131 Engineering,
132}
133
134#[wasm_bindgen]
135impl ScientificNotation {
136 #[wasm_bindgen(js_name = toString)]
137 pub fn to_js_string(&self) -> String {
138 self.to_string()
139 }
140
141 pub fn to_string_format(&self, format: ScientificNotationFormat) -> String {
142 match format {
143 ScientificNotationFormat::Normal => self.to_string(),
144 ScientificNotationFormat::Engineering => self.to_string_eng(),
145 }
146 }
147
148 fn to_string_eng(&self) -> String {
149 let exponent = self.exponent - 1;
150 let modulo = exponent % 3;
151 let value = self.value * 10_f64.powi(modulo);
152
153 ScientificNotation {
154 value,
155 exponent: exponent - modulo + 1,
156 imaginary: self.imaginary,
157 }
158 .to_string()
159 }
160}
161
162impl std::fmt::Display for ScientificNotation {
163 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
164 let digits_and_mul = if self.value == 1f64 {
165 String::new()
166 } else {
167 format!("{}×", format_number(self.value))
168 };
169
170 write!(
171 f,
172 "{}10^{}{}",
173 digits_and_mul,
174 self.exponent - 1,
175 if self.imaginary { " i" } else { "" }
176 )
177 }
178}
179
180#[derive(PartialEq, Debug, Clone)]
181pub enum KalkValue {
182 #[cfg(not(feature = "rug"))]
183 Number(f64, f64, Option<String>),
184 #[cfg(feature = "rug")]
185 Number(Float, Float, Option<String>),
186 Boolean(bool),
187 Vector(Vec<KalkValue>),
188 Matrix(Vec<Vec<KalkValue>>),
189}
190
191impl std::fmt::Display for KalkValue {
192 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
193 match self {
194 KalkValue::Number(real, imaginary, _) => {
195 let as_str = format_number_big(real);
196
197 if self.has_imaginary() {
198 let imaginary_as_str = format_number_big(&imaginary.clone().abs());
199 let sign = if imaginary < &0f64 { "-" } else { "+" };
200
201 if &as_str == "0" {
202 write!(f, "{}", imaginary_as_str)
203 } else {
204 write!(f, "{} {} {}i", as_str, sign, imaginary_as_str)
205 }
206 } else {
207 write!(f, "{}", as_str)
208 }
209 }
210 KalkValue::Boolean(is_true) => {
211 if *is_true {
212 write!(f, "true")
213 } else {
214 write!(f, "false")
215 }
216 }
217 KalkValue::Vector(values) => {
218 let get_estimation: fn(&KalkValue) -> String = |x| {
219 x.estimate()
220 .unwrap_or_else(|| EstimationResult {
221 value: x.to_string(),
222 is_exact: false,
223 })
224 .value
225 };
226
227 write!(
228 f,
229 "({})",
230 values
231 .iter()
232 .map(get_estimation)
233 .collect::<Vec<String>>()
234 .join(", ")
235 )
236 }
237 KalkValue::Matrix(rows) => {
238 let mut value_strings = Vec::new();
239 let mut longest = 0;
240 for row in rows {
241 for value in row {
242 let value_str = value
243 .estimate()
244 .unwrap_or_else(|| EstimationResult {
245 value: value.to_string(),
246 is_exact: false,
247 })
248 .value;
249 longest = longest.max(value_str.len());
250 value_strings.push(format!("{},", value_str));
251 }
252
253 value_strings.last_mut().unwrap().pop(); value_strings.push(String::from("\n"));
255 }
256
257 let mut result = String::from("[");
258 for value_str in value_strings {
259 if value_str == "\n" {
260 result.push_str("\n ");
261 } else {
262 result.push_str(&format!("{:width$} ", value_str, width = longest + 1));
263 }
264 }
265
266 result.pop(); result.pop(); result.pop(); result.pop(); result = result.trim().to_string();
271 result.push(']');
272
273 write!(f, "{}", result)
274 }
275 }
276 }
277}
278
279impl KalkValue {
280 pub fn nan() -> Self {
281 KalkValue::Number(float!(f64::NAN), float!(0f64), None)
282 }
283
284 pub fn get_type_name(&self) -> String {
285 match self {
286 KalkValue::Number(_, _, _) => String::from("number"),
287 KalkValue::Boolean(_) => String::from("boolean"),
288 KalkValue::Vector(_) => String::from("vector"),
289 KalkValue::Matrix(_) => String::from("matrix"),
290 }
291 }
292
293 pub fn to_string_big(&self) -> String {
294 fn trim_num(num_str: String) -> String {
295 num_str
296 .trim_end_matches('0')
297 .trim_end_matches('.')
298 .to_string()
299 }
300
301 if let KalkValue::Number(real, imaginary, _) = self {
302 if !self.has_imaginary() {
303 return trim_num(real.to_string());
304 }
305
306 let sign = if imaginary < &0f64 { "-" } else { "+" };
307 format!(
308 "{} {} {}i",
309 spaced(&trim_num(real.to_string())),
310 sign,
311 spaced(&trim_num(imaginary.to_string()))
312 )
313 } else {
314 trim_num(self.to_string())
315 }
316 }
317
318 pub fn to_string_real(&self, radix: u8) -> String {
319 radix::to_radix_pretty(self.to_f64(), radix)
320 }
321
322 pub fn to_string_imaginary(&self, radix: u8, include_i: bool) -> String {
323 let value = radix::to_radix_pretty(self.imaginary_to_f64(), radix);
324 if include_i && value == "1" {
325 String::from("i")
326 } else if include_i && value == "-1" {
327 String::from("-i")
328 } else if include_i {
329 format!("{}i", value)
330 } else {
331 value
332 }
333 }
334
335 pub fn to_string_pretty_radix(&self, radix: u8, format: ScientificNotationFormat) -> String {
336 let (real, imaginary, unit) = match self {
337 KalkValue::Number(real, imaginary, unit) => (real, imaginary, unit),
338 _ => return self.to_string(),
339 };
340
341 let real_f64 = self.to_f64();
342 let imaginary_f64 = self.imaginary_to_f64();
343 if real_f64.is_nan() || imaginary_f64.is_nan() {
344 return String::from("Not defined.");
345 }
346
347 if real_f64.is_infinite() {
348 return format!("{}∞", if real_f64.is_sign_negative() { "-" } else { "" });
349 }
350
351 let sci_notation_real = self.to_scientific_notation(ComplexNumberType::Real);
352 let mut new_real = real.clone();
353 let mut new_imaginary = imaginary.clone();
354 let mut has_scientific_notation = false;
355 let is_engineering_mode = matches!(format, ScientificNotationFormat::Engineering);
356 let result_str = if is_engineering_mode {
357 has_scientific_notation = true;
358
359 sci_notation_real.to_string_format(ScientificNotationFormat::Engineering)
360 } else if (-6..8).contains(&sci_notation_real.exponent) || real == &0f64 {
361 self.to_string_real(radix)
362 } else if sci_notation_real.exponent <= -14 {
363 new_real = float!(0);
364
365 String::from("0")
366 } else if radix == 10 {
367 has_scientific_notation = true;
368
369 sci_notation_real.to_string_format(format)
370 } else {
371 self.to_string_real(radix)
372 };
373
374 let sci_notation_imaginary = self.to_scientific_notation(ComplexNumberType::Imaginary);
375 let result_str_imaginary = if is_engineering_mode {
376 has_scientific_notation = true;
377
378 sci_notation_imaginary.to_string_format(ScientificNotationFormat::Engineering)
379 } else if (-6..8).contains(&sci_notation_imaginary.exponent)
380 || imaginary == &0f64
381 || imaginary == &1f64
382 {
383 self.to_string_imaginary(radix, true)
384 } else if sci_notation_imaginary.exponent <= -14 {
385 new_imaginary = float!(0);
386 String::from("0")
387 } else if radix == 10 {
388 has_scientific_notation = true;
389
390 sci_notation_imaginary.to_string_format(format)
391 } else {
392 self.to_string_real(radix)
393 };
394
395 let mut output = result_str;
396 if imaginary != &0f64 && new_imaginary != 0f64 && result_str_imaginary != "0" {
397 if output == "0" {
400 output = String::new();
401 }
402
403 if !output.is_empty() {
405 output.push_str(&format!(
406 " {} {}",
407 if imaginary < &0f64 { "-" } else { "+" },
408 result_str_imaginary.trim_start_matches('-'),
409 ));
410 } else {
411 output.push_str(&result_str_imaginary);
412 }
413 }
414
415 if let Some(unit) = unit {
416 output.push_str(&format!(" {}", unit));
417 }
418
419 let new_value = KalkValue::Number(new_real, new_imaginary, unit.clone());
420
421 if let Some(estimate) = new_value.estimate() {
422 if estimate.value != output && radix == 10 {
423 let equal_sign = if estimate.is_exact { "=" } else { "≈" };
424 output.push_str(&format!(" {equal_sign} {}", estimate.value));
425 }
426 } else if has_scientific_notation && !is_engineering_mode {
427 output.insert_str(0, &format!("{} ≈ ", self));
428 }
429
430 output
431 }
432
433 pub fn to_string_pretty(&self, format: ScientificNotationFormat) -> String {
434 self.to_string_pretty_radix(10, format)
435 }
436
437 pub fn to_string_with_unit(&self) -> String {
438 match self {
439 KalkValue::Number(_, _, unit) => {
440 format!("{} {}", self, unit.as_ref().unwrap_or(&String::new()))
441 }
442 _ => self.to_string(),
443 }
444 }
445
446 pub fn estimate(&self) -> Option<EstimationResult> {
448 let rounded_real = rounding::estimate(self, ComplexNumberType::Real);
449 let rounded_imaginary = rounding::estimate(self, ComplexNumberType::Imaginary);
450
451 if let (None, None) = (&rounded_real, &rounded_imaginary) {
452 return None;
453 }
454
455 let mut output = String::new();
456 let mut real_is_exact = rounded_real.is_none();
457 if let Some(result) = rounded_real {
458 real_is_exact = result.is_exact;
459 output.push_str(&result.value);
460 } else if self.has_real() {
461 output.push_str(&self.to_string_real(10));
462 }
463
464 let mut imaginary_is_exact = rounded_imaginary.is_none();
465 let imaginary_value = if let Some(result) = rounded_imaginary {
466 imaginary_is_exact = result.is_exact;
467
468 Some(result.value)
469 } else if self.has_imaginary() {
470 Some(self.to_string_imaginary(10, false))
471 } else {
472 None
473 };
474
475 let is_exact = real_is_exact && imaginary_is_exact;
476 if let Some(value) = imaginary_value {
477 if output == "0" {
479 output = String::new();
480 }
481
482 if value == "0" {
483 if output.is_empty() {
486 return Some(EstimationResult {
487 value: String::from("0"),
488 is_exact,
489 });
490 }
491 } else {
492 let sign = if value.starts_with('-') { "-" } else { "+" };
493 let value = match value.as_ref() {
494 "1" => String::from("i"),
495 "-1" => String::from("-i"),
496 _ => format!("{}i", value),
497 };
498
499 if !output.is_empty() {
501 output.push_str(&format!(" {} {}", sign, value.trim_start_matches('-')));
502 } else {
503 output.push_str(&value);
504 }
505 }
506 }
507
508 Some(EstimationResult {
509 value: output,
510 is_exact,
511 })
512 }
513
514 pub fn round(&self) -> Option<KalkValue> {
516 let rounded_real = rounding::round(self, ComplexNumberType::Real);
517 let rounded_imaginary = rounding::round(self, ComplexNumberType::Imaginary);
518
519 if let (None, None) = (&rounded_real, &rounded_imaginary) {
520 return None;
521 }
522
523 let (original_real, original_imaginary, unit) = match self {
524 KalkValue::Number(real, imaginary, unit) => (real, imaginary, unit),
525 _ => return None,
526 };
527
528 Some(KalkValue::Number(
529 if let Some(KalkValue::Number(real, _, _)) = rounded_real {
530 real
531 } else {
532 original_real.clone()
533 },
534 if let Some(KalkValue::Number(_, imaginary, _)) = rounded_imaginary {
535 imaginary
536 } else {
537 original_imaginary.clone()
538 },
539 unit.clone(),
540 ))
541 }
542
543 pub fn round_if_needed(self) -> KalkValue {
544 if let Some(rounded) = self.round() {
545 rounded
546 } else {
547 self
548 }
549 }
550
551 pub fn has_real(&self) -> bool {
552 if let KalkValue::Number(real, _, _) = self {
553 real != &0f64
554 } else {
555 false
556 }
557 }
558
559 pub fn has_imaginary(&self) -> bool {
560 if let KalkValue::Number(_, imaginary, _) = self {
561 imaginary != &0f64 && imaginary != &-0f64
562 } else {
563 false
564 }
565 }
566
567 pub fn is_nan(&self) -> bool {
568 if let KalkValue::Number(real, imaginary, _) = self {
569 real.is_nan() || imaginary.is_nan()
570 } else {
571 false
572 }
573 }
574
575 pub fn is_finite(&self) -> bool {
576 if let KalkValue::Number(real, imaginary, _) = self {
578 real.is_finite() && imaginary.is_finite()
579 } else {
580 false
581 }
582 }
589
590 pub fn to_scientific_notation(
591 &self,
592 complex_number_type: ComplexNumberType,
593 ) -> ScientificNotation {
594 let value = match complex_number_type {
595 ComplexNumberType::Real => self.to_f64(),
596 ComplexNumberType::Imaginary => self.imaginary_to_f64(),
597 };
598 let exponent = value.abs().log10().floor() as i32 + 1;
599
600 ScientificNotation {
601 value: value / (10f64.powf(exponent as f64 - 1f64)),
602 exponent,
604 imaginary: complex_number_type == ComplexNumberType::Imaginary,
605 }
606 }
607
608 pub fn has_unit(&self) -> bool {
609 if let KalkValue::Number(_, _, unit) = self {
610 unit.is_some()
611 } else {
612 false
613 }
614 }
615
616 pub fn get_unit(&self) -> Option<&String> {
617 if let KalkValue::Number(_, _, unit) = self {
618 unit.as_ref()
619 } else {
620 None
621 }
622 }
623
624 pub(crate) fn convert_to_unit(
625 &self,
626 context: &mut crate::interpreter::Context,
627 to_unit: &str,
628 ) -> Option<KalkValue> {
629 if let KalkValue::Number(real, _, unit) = self {
630 let result = crate::interpreter::convert_unit(
631 context,
632 &Expr::Literal(real.clone()),
633 unit.as_ref(),
634 Some(&to_unit.to_string()),
635 );
636
637 if let Ok(num) = result {
638 Some(num)
639 } else {
640 None
641 }
642 } else {
643 None
644 }
645 }
646
647 pub(crate) fn add(
648 self,
649 context: &mut crate::interpreter::Context,
650 rhs: KalkValue,
651 ) -> Result<KalkValue, KalkError> {
652 let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs);
653 self.add_without_unit(&right)
654 }
655
656 pub(crate) fn sub(
657 self,
658 context: &mut crate::interpreter::Context,
659 rhs: KalkValue,
660 ) -> Result<KalkValue, KalkError> {
661 let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs);
662 self.sub_without_unit(&right)
663 }
664
665 pub(crate) fn mul(
666 self,
667 context: &mut crate::interpreter::Context,
668 rhs: KalkValue,
669 ) -> Result<KalkValue, KalkError> {
670 let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs);
671 self.mul_without_unit(&right)
672 }
673
674 pub(crate) fn div(
675 self,
676 context: &mut crate::interpreter::Context,
677 rhs: KalkValue,
678 ) -> Result<KalkValue, KalkError> {
679 let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs);
680 self.div_without_unit(&right)
681 }
682
683 pub(crate) fn pow(
684 self,
685 context: &mut crate::interpreter::Context,
686 rhs: KalkValue,
687 ) -> Result<KalkValue, KalkError> {
688 let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs);
689 self.pow_without_unit(&right)
690 }
691
692 pub(crate) fn rem(
693 self,
694 context: &mut crate::interpreter::Context,
695 rhs: KalkValue,
696 ) -> Result<KalkValue, KalkError> {
697 Ok(if let KalkValue::Number(real, _, _) = &self {
698 let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs);
699 if let KalkValue::Number(right_real, _, right_unit) = right {
700 KalkValue::Number(real % right_real, float!(0f64), right_unit)
701 } else {
702 self
703 }
704 } else {
705 self
706 })
707 }
708
709 pub(crate) fn eq(
710 self,
711 context: &mut crate::interpreter::Context,
712 rhs: KalkValue,
713 ) -> Result<KalkValue, KalkError> {
714 let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs);
715 self.eq_without_unit(&right)
716 }
717
718 pub(crate) fn not_eq(
719 self,
720 context: &mut crate::interpreter::Context,
721 rhs: KalkValue,
722 ) -> Result<KalkValue, KalkError> {
723 let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs);
724 self.not_eq_without_unit(&right)
725 }
726
727 pub(crate) fn greater_than(
728 self,
729 context: &mut crate::interpreter::Context,
730 rhs: KalkValue,
731 ) -> Result<KalkValue, KalkError> {
732 let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs);
733 self.greater_than_without_unit(&right)
734 }
735
736 pub(crate) fn less_than(
737 self,
738 context: &mut crate::interpreter::Context,
739 rhs: KalkValue,
740 ) -> Result<KalkValue, KalkError> {
741 let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs);
742 self.less_than_without_unit(&right)
743 }
744
745 pub(crate) fn greater_or_equals(
746 self,
747 context: &mut crate::interpreter::Context,
748 rhs: KalkValue,
749 ) -> Result<KalkValue, KalkError> {
750 let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs);
751 if let (KalkValue::Boolean(greater), KalkValue::Boolean(equal)) = (
752 self.greater_than_without_unit(&right)?,
753 self.eq_without_unit(&right)?,
754 ) {
755 Ok(KalkValue::Boolean(greater || equal))
756 } else {
757 unreachable!()
758 }
759 }
760
761 pub(crate) fn less_or_equals(
762 self,
763 context: &mut crate::interpreter::Context,
764 rhs: KalkValue,
765 ) -> Result<KalkValue, KalkError> {
766 let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs);
767 if let (KalkValue::Boolean(less), KalkValue::Boolean(equal)) = (
768 self.less_than_without_unit(&right)?,
769 self.eq_without_unit(&right)?,
770 ) {
771 Ok(KalkValue::Boolean(less || equal))
772 } else {
773 unreachable!()
774 }
775 }
776
777 pub(crate) fn and(self, rhs: &KalkValue) -> Result<KalkValue, KalkError> {
778 match (self, rhs) {
779 (KalkValue::Boolean(boolean), KalkValue::Boolean(boolean_rhs)) => {
780 Ok(KalkValue::Boolean(boolean && *boolean_rhs))
781 }
782 (lhs, rhs) => Err(KalkError::IncompatibleTypesForOperation(
783 String::from("and"),
784 lhs.get_type_name(),
785 rhs.get_type_name(),
786 )),
787 }
788 }
789
790 pub(crate) fn or(self, rhs: &KalkValue) -> Result<KalkValue, KalkError> {
791 match (self, rhs) {
792 (KalkValue::Boolean(boolean), KalkValue::Boolean(boolean_rhs)) => {
793 Ok(KalkValue::Boolean(boolean || *boolean_rhs))
794 }
795 (lhs, rhs) => Err(KalkError::IncompatibleTypesForOperation(
796 String::from("or"),
797 lhs.get_type_name(),
798 rhs.get_type_name(),
799 )),
800 }
801 }
802
803 pub(crate) fn add_without_unit(self, rhs: &KalkValue) -> Result<KalkValue, KalkError> {
804 match (self.clone(), rhs) {
805 (
806 KalkValue::Number(real, imaginary, _),
807 KalkValue::Number(real_rhs, imaginary_rhs, unit),
808 ) => Ok(KalkValue::Number(
809 real + real_rhs,
810 imaginary + imaginary_rhs,
811 unit.clone(),
812 )),
813 (KalkValue::Matrix(_), _) | (_, KalkValue::Matrix(_)) => {
814 calculate_matrix(self, rhs, &KalkValue::add_without_unit)
815 }
816 (KalkValue::Vector(_), _) | (_, KalkValue::Vector(_)) => {
817 calculate_vector(self, rhs, &KalkValue::add_without_unit)
818 }
819 _ => Err(KalkError::IncompatibleTypesForOperation(
820 String::from("addition"),
821 self.get_type_name(),
822 rhs.get_type_name(),
823 )),
824 }
825 }
826
827 pub(crate) fn sub_without_unit(self, rhs: &KalkValue) -> Result<KalkValue, KalkError> {
828 match (self.clone(), rhs) {
829 (
830 KalkValue::Number(real, imaginary, _),
831 KalkValue::Number(real_rhs, imaginary_rhs, unit),
832 ) => Ok(KalkValue::Number(
833 real - real_rhs,
834 imaginary - imaginary_rhs,
835 unit.clone(),
836 )),
837 (KalkValue::Matrix(_), _) | (_, KalkValue::Matrix(_)) => {
838 calculate_matrix(self, rhs, &KalkValue::sub_without_unit)
839 }
840 (KalkValue::Vector(_), _) | (_, KalkValue::Vector(_)) => {
841 calculate_vector(self, rhs, &KalkValue::sub_without_unit)
842 }
843 _ => Err(KalkError::IncompatibleTypesForOperation(
844 String::from("subtraction"),
845 self.get_type_name(),
846 rhs.get_type_name(),
847 )),
848 }
849 }
850
851 pub(crate) fn mul_without_unit(self, rhs: &KalkValue) -> Result<KalkValue, KalkError> {
852 let (lhs, rhs) = match (&self, rhs) {
855 (KalkValue::Matrix(_), KalkValue::Matrix(_)) => (&self, rhs),
856 (_, KalkValue::Matrix(_)) => (rhs, &self),
857 _ => (&self, rhs),
858 };
859
860 match (lhs, rhs) {
861 (
862 KalkValue::Number(real, imaginary, _),
863 KalkValue::Number(real_rhs, imaginary_rhs, unit),
864 ) => Ok(KalkValue::Number(
865 real.clone() * real_rhs - imaginary.clone() * imaginary_rhs,
867 real.clone() * imaginary_rhs + imaginary * real_rhs,
868 unit.clone(),
869 )),
870 (KalkValue::Matrix(_), KalkValue::Number(_, _, _)) => {
871 calculate_matrix(lhs.clone(), rhs, &KalkValue::mul_without_unit)
872 }
873 (KalkValue::Matrix(rows), KalkValue::Vector(values_rhs)) => {
874 if rows.first().unwrap().len() != values_rhs.len() {
875 return Err(KalkError::IncompatibleVectorsMatrixes);
876 }
877
878 let mut new_values: Vec<KalkValue> = Vec::new();
879 for row in rows {
880 let mut sum = KalkValue::from(0);
881 for (x, y) in row.iter().zip(values_rhs) {
882 sum = sum
883 .clone()
884 .add_without_unit(&x.clone().mul_without_unit(y)?)?;
885 }
886
887 new_values.push(sum);
888 }
889
890 Ok(KalkValue::Vector(new_values))
891 }
892 (KalkValue::Matrix(rows), KalkValue::Matrix(rows_rhs)) => {
893 let lhs_columns = rows.first().unwrap();
894 if lhs_columns.len() != rows_rhs.len() {
895 return Err(KalkError::IncompatibleVectorsMatrixes);
896 }
897
898 let rhs_columns = rows_rhs.first().unwrap();
899 let mut result = vec![vec![KalkValue::from(0f64); rhs_columns.len()]; rows.len()];
900
901 for i in 0..rows.len() {
903 for j in 0..rhs_columns.len() {
905 let mut sum = KalkValue::from(0f64);
906
907 for (k, value) in rows[i].iter().enumerate() {
909 let value_rhs = &rows_rhs[k][j];
910 sum =
911 sum.add_without_unit(&value.clone().mul_without_unit(value_rhs)?)?;
912 }
913
914 result[i][j] = sum;
915 }
916 }
917
918 Ok(KalkValue::Matrix(result))
919 }
920 (KalkValue::Vector(values), KalkValue::Number(_, _, _)) => {
921 let mut multiplied_values = Vec::new();
922 for value in values {
923 multiplied_values.push(value.clone().mul_without_unit(rhs)?);
924 }
925
926 Ok(KalkValue::Vector(multiplied_values))
927 }
928 (KalkValue::Vector(values), KalkValue::Vector(values_rhs)) => {
929 if values.len() != values_rhs.len() {
930 return Err(KalkError::IncompatibleVectorsMatrixes);
931 }
932
933 let mut sum = KalkValue::from(0f64);
934 for (value, value_rhs) in values.iter().zip(values_rhs) {
935 sum = sum.add_without_unit(&value.clone().mul_without_unit(value_rhs)?)?;
936 }
937
938 Ok(sum)
939 }
940 _ => Err(KalkError::IncompatibleTypesForOperation(
941 String::from("multiplication"),
942 self.get_type_name(),
943 rhs.get_type_name(),
944 )),
945 }
946 }
947
948 pub(crate) fn div_without_unit(self, rhs: &KalkValue) -> Result<KalkValue, KalkError> {
949 match (self.clone(), rhs.clone()) {
950 (KalkValue::Number(real, _, _), KalkValue::Number(real_rhs, _, unit)) => {
951 if !self.has_imaginary() && !rhs.has_imaginary() {
953 Ok(KalkValue::Number(real / real_rhs, float!(0f64), unit))
954 } else {
955 let conjugate = rhs.get_conjugate()?;
958 let (numerator, numerator_imaginary) =
959 self.mul_without_unit(&conjugate)?.values();
960 let (denominator, _) = rhs.clone().mul_without_unit(&conjugate)?.values();
961 Ok(KalkValue::Number(
962 numerator / denominator.clone(),
963 numerator_imaginary / denominator,
964 unit,
965 ))
966 }
967 }
968 (KalkValue::Matrix(_), _) | (_, KalkValue::Matrix(_)) => {
969 calculate_matrix(self, rhs, &KalkValue::div_without_unit)
970 }
971 (KalkValue::Vector(_), _) | (_, KalkValue::Vector(_)) => {
972 calculate_vector(self, rhs, &KalkValue::div_without_unit)
973 }
974 _ => Err(KalkError::IncompatibleTypesForOperation(
975 String::from("division"),
976 self.get_type_name(),
977 rhs.get_type_name(),
978 )),
979 }
980 }
981
982 pub(crate) fn pow_without_unit(self, rhs: &KalkValue) -> Result<KalkValue, KalkError> {
983 match (self.clone(), rhs) {
984 (
985 KalkValue::Number(real, imaginary, _),
986 KalkValue::Number(real_rhs, imaginary_rhs, unit),
987 ) => {
988 if self.has_imaginary()
989 || imaginary_rhs != &0f64
990 || (real < 0f64 && real_rhs.clone().abs() < 1f64)
991 {
992 let a = real;
993 let b = imaginary;
994 let c = real_rhs;
995 let d = imaginary_rhs;
996 let arg = crate::prelude::funcs::arg(self)?.values().0;
997 let raised = a.clone() * a + b.clone() * b;
998 let exp =
999 pow(raised.clone(), c.clone() / 2f64) * (-d.clone() * arg.clone()).exp();
1000 let polar = c * arg + d.clone() / 2f64 * raised.ln();
1001
1002 Ok(KalkValue::Number(
1003 polar.clone().cos() * exp.clone(),
1004 polar.sin() * exp,
1005 unit.clone(),
1006 ))
1007 } else {
1008 Ok(KalkValue::Number(
1009 pow(real, real_rhs.clone()),
1010 float!(0),
1011 unit.clone(),
1012 ))
1013 }
1014 }
1015 (KalkValue::Matrix(rows), KalkValue::Number(real, _, _)) => {
1016 if real < &0f64 || real.clone().fract() > 0.000001f64 {
1017 return Err(KalkError::Expected(String::from("a positive integer")));
1018 }
1019
1020 if rhs.has_imaginary() {
1021 return Err(KalkError::ExpectedReal);
1022 }
1023
1024 if rows.len() != rows.first().unwrap().len() {
1025 return Err(KalkError::Expected(String::from("a square matrix")));
1026 }
1027
1028 if real == &0f64 {
1029 return Ok(KalkValue::from(1f64));
1030 }
1031
1032 let mut result = KalkValue::from(1f64);
1033 for _ in 0..primitive!(real) as i32 {
1034 result = result.mul_without_unit(&self)?;
1035 }
1036
1037 Ok(result)
1038 }
1039 (KalkValue::Number(_, _, _), KalkValue::Matrix(rows)) => {
1040 let mut new_rows = Vec::new();
1041 for row in rows {
1042 new_rows.push(Vec::new());
1043 for item in row {
1044 new_rows
1045 .last_mut()
1046 .unwrap()
1047 .push(self.clone().pow_without_unit(item)?);
1048 }
1049 }
1050
1051 Ok(KalkValue::Matrix(new_rows))
1052 }
1053 (KalkValue::Vector(_), _) | (_, KalkValue::Vector(_)) => {
1054 calculate_vector(self, rhs, &KalkValue::pow_without_unit)
1055 }
1056 _ => Err(KalkError::IncompatibleTypesForOperation(
1057 String::from("pow"),
1058 self.get_type_name(),
1059 rhs.get_type_name(),
1060 )),
1061 }
1062 }
1063
1064 pub(crate) fn eq_without_unit(&self, rhs: &KalkValue) -> Result<KalkValue, KalkError> {
1065 match (self, rhs) {
1066 (
1067 KalkValue::Number(real, imaginary, _),
1068 KalkValue::Number(real_rhs, imaginary_rhs, _),
1069 ) => Ok(KalkValue::Boolean(
1070 (real.clone() - real_rhs.clone()).abs() < ACCEPTABLE_COMPARISON_MARGIN
1071 && (imaginary.clone() - imaginary_rhs.clone()).abs()
1072 < ACCEPTABLE_COMPARISON_MARGIN,
1073 )),
1074 (KalkValue::Boolean(boolean), KalkValue::Boolean(boolean_rhs)) => {
1075 Ok(KalkValue::Boolean(boolean == boolean_rhs))
1076 }
1077 (KalkValue::Matrix(rows), KalkValue::Matrix(rows_rhs)) => {
1078 let mut matrices_are_equal = true;
1079 for (row, row_rhs) in rows.iter().zip(rows_rhs) {
1080 for (value, value_rhs) in row.iter().zip(row_rhs) {
1081 if let KalkValue::Boolean(are_equal) = value.eq_without_unit(value_rhs)? {
1082 if !are_equal {
1083 matrices_are_equal = false;
1084 }
1085 }
1086 }
1087 }
1088
1089 Ok(KalkValue::Boolean(matrices_are_equal))
1090 }
1091
1092 (KalkValue::Vector(values), KalkValue::Vector(values_rhs)) => {
1093 let mut vecs_are_equal = true;
1094 for (value, value_rhs) in values.iter().zip(values_rhs) {
1095 if let KalkValue::Boolean(are_equal) = value.eq_without_unit(value_rhs)? {
1096 if !are_equal {
1097 vecs_are_equal = false;
1098 }
1099 }
1100 }
1101
1102 Ok(KalkValue::Boolean(vecs_are_equal))
1103 }
1104 _ => Err(KalkError::IncompatibleTypesForOperation(
1105 String::from("equal"),
1106 self.get_type_name(),
1107 rhs.get_type_name(),
1108 )),
1109 }
1110 }
1111
1112 pub(crate) fn not_eq_without_unit(&self, rhs: &KalkValue) -> Result<KalkValue, KalkError> {
1113 match (self, rhs) {
1114 (
1115 KalkValue::Number(real, imaginary, _),
1116 KalkValue::Number(real_rhs, imaginary_rhs, _),
1117 ) => Ok(KalkValue::Boolean(
1118 (real.clone() - real_rhs.clone()).abs() > ACCEPTABLE_COMPARISON_MARGIN
1119 || (imaginary.clone() - imaginary_rhs.clone()).abs()
1120 > ACCEPTABLE_COMPARISON_MARGIN,
1121 )),
1122 (KalkValue::Boolean(boolean), KalkValue::Boolean(boolean_rhs)) => {
1123 Ok(KalkValue::Boolean(boolean != boolean_rhs))
1124 }
1125 (KalkValue::Vector(_), KalkValue::Vector(_))
1126 | (KalkValue::Matrix(_), KalkValue::Matrix(_)) => {
1127 if let KalkValue::Boolean(boolean) = self.eq_without_unit(rhs)? {
1128 Ok(KalkValue::Boolean(!boolean))
1129 } else {
1130 unreachable!()
1131 }
1132 }
1133 _ => Err(KalkError::IncompatibleTypesForOperation(
1134 String::from("not equal"),
1135 self.get_type_name(),
1136 rhs.get_type_name(),
1137 )),
1138 }
1139 }
1140
1141 pub(crate) fn greater_than_without_unit(
1142 &self,
1143 rhs: &KalkValue,
1144 ) -> Result<KalkValue, KalkError> {
1145 if self.has_imaginary() || rhs.has_imaginary() {
1146 return Err(KalkError::ExpectedReal);
1147 }
1148
1149 match (self, rhs) {
1150 (KalkValue::Number(real, _, _), KalkValue::Number(real_rhs, _, _)) => Ok(
1151 KalkValue::Boolean(real.clone() - real_rhs.clone() > ACCEPTABLE_COMPARISON_MARGIN),
1152 ),
1153 _ => Err(KalkError::IncompatibleTypesForOperation(
1154 String::from("greater than"),
1155 self.get_type_name(),
1156 rhs.get_type_name(),
1157 )),
1158 }
1159 }
1160
1161 pub(crate) fn less_than_without_unit(&self, rhs: &KalkValue) -> Result<KalkValue, KalkError> {
1162 if self.has_imaginary() || rhs.has_imaginary() {
1163 return Err(KalkError::ExpectedReal);
1164 }
1165
1166 match (self, rhs) {
1167 (KalkValue::Number(real, _, _), KalkValue::Number(real_rhs, _, _)) => Ok(
1168 KalkValue::Boolean(real.clone() - real_rhs.clone() < -ACCEPTABLE_COMPARISON_MARGIN),
1169 ),
1170 _ => Err(KalkError::IncompatibleTypesForOperation(
1171 String::from("less than"),
1172 self.get_type_name(),
1173 rhs.get_type_name(),
1174 )),
1175 }
1176 }
1177
1178 pub fn get_conjugate(&self) -> Result<KalkValue, KalkError> {
1179 match self {
1180 KalkValue::Number(real, imaginary, unit) => Ok(KalkValue::Number(
1181 real.clone(),
1182 imaginary.clone() * (-1f64),
1183 unit.clone(),
1184 )),
1185 _ => Err(KalkError::UnexpectedType(
1186 self.get_type_name(),
1187 vec![String::from("number")],
1188 )),
1189 }
1190 }
1191}
1192
1193pub fn format_number(input: f64) -> String {
1194 let rounded = format!("{:.1$}", input, 10);
1195 let result = if rounded.contains('.') {
1196 rounded
1197 .trim_end_matches('0')
1198 .trim_end_matches('.')
1199 .to_string()
1200 } else {
1201 rounded
1202 };
1203
1204 spaced(&result)
1205}
1206
1207#[cfg(feature = "rug")]
1208pub fn format_number_big(input: &Float) -> String {
1209 if input.clone().abs().log10() < 0f64 {
1210 return input.to_f64().to_string();
1211 }
1212
1213 let input_str = input.to_string();
1214 let mut result = if input_str.contains('.') {
1215 input_str
1216 .trim_end_matches('0')
1217 .trim_end_matches('.')
1218 .to_string()
1219 } else {
1220 input_str
1221 };
1222
1223 if let Some(dot_index) = result.find('.') {
1224 let decimal_count = result.len() - dot_index;
1225 if decimal_count > 10 {
1226 result = result[..(result.len() - decimal_count + 10)].to_string();
1227 }
1228 }
1229
1230 spaced(&result)
1231}
1232
1233#[cfg(not(feature = "rug"))]
1234pub fn format_number_big(input: &f64) -> String {
1235 format_number(*input)
1236}
1237
1238fn calculate_vector(
1239 x: KalkValue,
1240 y: &KalkValue,
1241 action: &dyn Fn(KalkValue, &KalkValue) -> Result<KalkValue, KalkError>,
1242) -> Result<KalkValue, KalkError> {
1243 match (x, y) {
1244 (KalkValue::Vector(values), KalkValue::Number(_, _, _)) => {
1245 let mut new_values = Vec::new();
1246 for value in values {
1247 new_values.push(action(value.clone(), y)?);
1248 }
1249
1250 Ok(KalkValue::Vector(new_values))
1251 }
1252 (KalkValue::Number(_, _, _), KalkValue::Vector(values_rhs)) => {
1253 let mut new_values = Vec::new();
1254 for value in values_rhs {
1255 new_values.push(action(y.clone(), value)?);
1256 }
1257
1258 Ok(KalkValue::Vector(new_values))
1259 }
1260 (KalkValue::Vector(values), KalkValue::Vector(values_rhs)) => {
1261 if values.len() != values_rhs.len() {
1262 return Err(KalkError::IncompatibleVectorsMatrixes);
1263 }
1264
1265 let mut new_values = Vec::new();
1266 for (value, value_rhs) in values.iter().zip(values_rhs) {
1267 new_values.push(action(value.clone(), value_rhs)?);
1268 }
1269
1270 Ok(KalkValue::Vector(new_values))
1271 }
1272 (x, y) => Err(KalkError::IncompatibleTypesForOperation(
1273 String::from("vector operation"),
1274 x.get_type_name(),
1275 y.get_type_name(),
1276 )),
1277 }
1278}
1279
1280fn calculate_matrix(
1281 x: KalkValue,
1282 y: &KalkValue,
1283 action: &dyn Fn(KalkValue, &KalkValue) -> Result<KalkValue, KalkError>,
1284) -> Result<KalkValue, KalkError> {
1285 let (x, y) = match (&x, y) {
1288 (KalkValue::Matrix(_), KalkValue::Matrix(_)) => (&x, y),
1289 (_, KalkValue::Matrix(_)) => (y, &x),
1290 _ => (&x, y),
1291 };
1292
1293 match (x, y) {
1294 (KalkValue::Matrix(rows), KalkValue::Number(_, _, _)) => {
1295 let mut new_rows = Vec::new();
1296 for row in rows {
1297 new_rows.push(Vec::new());
1298 for item in row {
1299 new_rows.last_mut().unwrap().push(action(item.clone(), y)?);
1300 }
1301 }
1302
1303 Ok(KalkValue::Matrix(new_rows))
1304 }
1305 (KalkValue::Matrix(rows), KalkValue::Vector(values_rhs)) => {
1306 if rows.len() != values_rhs.len() {
1307 return Err(KalkError::IncompatibleVectorsMatrixes);
1308 }
1309
1310 let mut new_rows = Vec::new();
1311 for (i, row) in rows.iter().enumerate() {
1312 new_rows.push(Vec::new());
1313 for value in row {
1314 new_rows
1315 .last_mut()
1316 .unwrap()
1317 .push(action(value.clone(), &values_rhs[i])?)
1318 }
1319 }
1320
1321 Ok(KalkValue::Matrix(new_rows))
1322 }
1323 (KalkValue::Matrix(rows), KalkValue::Matrix(rows_rhs)) => {
1324 if rows.len() != rows_rhs.len()
1325 || rows.first().unwrap().len() != rows_rhs.first().unwrap().len()
1326 {
1327 return Err(KalkError::IncompatibleVectorsMatrixes);
1328 }
1329
1330 let mut new_rows = Vec::new();
1331 for (i, row) in rows.iter().enumerate() {
1332 new_rows.push(Vec::new());
1333 for (j, value) in row.iter().enumerate() {
1334 new_rows
1335 .last_mut()
1336 .unwrap()
1337 .push(action(value.clone(), &rows_rhs[i][j])?)
1338 }
1339 }
1340
1341 Ok(KalkValue::Matrix(new_rows))
1342 }
1343 _ => Err(KalkError::IncompatibleTypesForOperation(
1344 String::from("matrix operation"),
1345 x.get_type_name(),
1346 y.get_type_name(),
1347 )),
1348 }
1349}
1350
1351fn spaced(number_str: &str) -> String {
1352 let dot_pos = number_str.find('.');
1353 let integer_boundary = if let Some(dot_pos) = dot_pos {
1354 dot_pos
1355 } else {
1356 number_str.len()
1357 };
1358
1359 if integer_boundary < 5 {
1360 return number_str.into();
1361 }
1362
1363 let bytes = number_str.as_bytes();
1364 let mut at_decimals = dot_pos.is_some();
1365 let mut i = number_str.len() - 1;
1366 let mut c = 0;
1367 let mut new_str = String::new();
1368 while i > 0 {
1369 if bytes[i] as char == '.' {
1370 new_str.push('.');
1371 at_decimals = false;
1372 i -= 1;
1373 c = 0;
1374 continue;
1375 }
1376
1377 if !at_decimals && c == 3 {
1378 new_str.push(' ');
1379 c = 0;
1380 }
1381
1382 new_str.push(bytes[i] as char);
1383 c += 1;
1384 i -= 1;
1385 }
1386
1387 if c == 3 {
1388 new_str.push(' ');
1389 }
1390 new_str.push(bytes[0] as char);
1391
1392 new_str.chars().rev().collect::<String>()
1393}
1394
1395fn calculate_unit(
1396 context: &mut crate::interpreter::Context,
1397 left: &KalkValue,
1398 right: KalkValue,
1399) -> Option<KalkValue> {
1400 if let (KalkValue::Number(_, _, unit_left), KalkValue::Number(real_right, imaginary_right, _)) =
1401 (left, &right)
1402 {
1403 if left.has_unit() && right.has_unit() {
1404 right.convert_to_unit(context, unit_left.as_ref().unwrap())
1405 } else {
1406 Some(KalkValue::Number(
1407 real_right.clone(),
1408 imaginary_right.clone(),
1409 unit_left.clone(),
1410 ))
1411 }
1412 } else {
1413 None
1414 }
1415}
1416
1417#[cfg(not(feature = "rug"))]
1418fn pow(x: f64, y: f64) -> f64 {
1419 x.powf(y)
1420}
1421
1422#[cfg(feature = "rug")]
1423fn pow(x: Float, y: Float) -> Float {
1424 x.pow(y)
1425}
1426
1427impl From<ScientificNotation> for String {
1428 fn from(val: ScientificNotation) -> Self {
1429 val.to_string()
1430 }
1431}
1432
1433impl From<KalkValue> for String {
1448 fn from(val: KalkValue) -> Self {
1449 val.to_string()
1450 }
1451}
1452
1453impl From<KalkValue> for f64 {
1454 fn from(val: KalkValue) -> Self {
1455 val.to_f64()
1456 }
1457}
1458
1459impl From<f64> for KalkValue {
1460 fn from(x: f64) -> Self {
1461 KalkValue::Number(float!(x), float!(0), None)
1462 }
1463}
1464
1465impl From<f32> for KalkValue {
1466 fn from(x: f32) -> Self {
1467 KalkValue::Number(float!(x), float!(0), None)
1468 }
1469}
1470
1471impl From<i128> for KalkValue {
1472 fn from(x: i128) -> Self {
1473 KalkValue::Number(float!(x), float!(0), None)
1474 }
1475}
1476
1477impl From<i64> for KalkValue {
1478 fn from(x: i64) -> Self {
1479 KalkValue::Number(float!(x), float!(0), None)
1480 }
1481}
1482
1483impl From<i32> for KalkValue {
1484 fn from(x: i32) -> Self {
1485 KalkValue::Number(float!(x), float!(0), None)
1486 }
1487}
1488
1489#[cfg(test)]
1490mod tests {
1491 use crate::kalk_value::{spaced, KalkValue, ScientificNotationFormat};
1492 use crate::test_helpers::cmp;
1493
1494 use super::ScientificNotation;
1495
1496 #[test]
1497 fn test_spaced() {
1498 assert_eq!(spaced("1"), String::from("1"));
1499 assert_eq!(spaced("10"), String::from("10"));
1500 assert_eq!(spaced("100"), String::from("100"));
1501 assert_eq!(spaced("1000"), String::from("1000"));
1502 assert_eq!(spaced("10000"), String::from("10 000"));
1503 assert_eq!(spaced("100000"), String::from("100 000"));
1504 assert_eq!(spaced("1000000"), String::from("1 000 000"));
1505 assert_eq!(spaced("10000000"), String::from("10 000 000"));
1506 assert_eq!(spaced("1.12345"), String::from("1.12345"));
1507 assert_eq!(spaced("10.12345"), String::from("10.12345"));
1508 assert_eq!(spaced("100.12345"), String::from("100.12345"));
1509 assert_eq!(spaced("1000.12345"), String::from("1000.12345"));
1510 assert_eq!(spaced("10000.12345"), String::from("10 000.12345"));
1511 assert_eq!(spaced("100000.12345"), String::from("100 000.12345"));
1512 assert_eq!(spaced("1000000.12345"), String::from("1 000 000.12345"));
1513 assert_eq!(spaced("10000000.12345"), String::from("10 000 000.12345"));
1514 }
1515
1516 #[test]
1517 fn test_add_complex() {
1518 let in_out = vec![
1519 ((0f64, 0f64), (0f64, 0f64), (0f64, 0f64)),
1520 ((2f64, 0f64), (3f64, 4f64), (5f64, 4f64)),
1521 ((0f64, 2f64), (3f64, 4f64), (3f64, 6f64)),
1522 ((3f64, -2f64), (-3f64, 4f64), (0f64, 2f64)),
1523 ];
1524
1525 for (a, b, expected_result) in in_out {
1526 let actual_result = KalkValue::Number(float!(a.0), float!(a.1), None)
1527 .add_without_unit(&KalkValue::Number(float!(b.0), float!(b.1), None))
1528 .unwrap();
1529 assert_eq!(actual_result.to_f64(), expected_result.0);
1530 assert_eq!(actual_result.imaginary_to_f64(), expected_result.1);
1531 }
1532 }
1533
1534 #[test]
1535 fn test_sub_complex() {
1536 let in_out = vec![
1537 ((0f64, 0f64), (0f64, 0f64), (0f64, 0f64)),
1538 ((2f64, 0f64), (3f64, 4f64), (-1f64, -4f64)),
1539 ((0f64, 2f64), (3f64, 4f64), (-3f64, -2f64)),
1540 ((3f64, -2f64), (-3f64, 4f64), (6f64, -6f64)),
1541 ];
1542
1543 for (a, b, expected_result) in in_out {
1544 let actual_result = KalkValue::Number(float!(a.0), float!(a.1), None)
1545 .sub_without_unit(&KalkValue::Number(float!(b.0), float!(b.1), None))
1546 .unwrap();
1547 assert_eq!(actual_result.to_f64(), expected_result.0);
1548 assert_eq!(actual_result.imaginary_to_f64(), expected_result.1);
1549 }
1550 }
1551
1552 #[test]
1553 fn test_mul_complex() {
1554 let in_out = vec![
1555 ((0f64, 0f64), (0f64, 0f64), (0f64, 0f64)),
1556 ((2f64, 0f64), (3f64, 4f64), (6f64, 8f64)),
1557 ((0f64, 2f64), (3f64, 4f64), (-8f64, 6f64)),
1558 ((3f64, -2f64), (-3f64, 4f64), (-1f64, 18f64)),
1559 ];
1560
1561 for (a, b, expected_result) in in_out {
1562 let actual_result = KalkValue::Number(float!(a.0), float!(a.1), None)
1563 .mul_without_unit(&KalkValue::Number(float!(b.0), float!(b.1), None))
1564 .unwrap();
1565 assert_eq!(actual_result.to_f64(), expected_result.0);
1566 assert_eq!(actual_result.imaginary_to_f64(), expected_result.1);
1567 }
1568 }
1569
1570 #[test]
1571 fn test_div_complex() {
1572 let in_out = vec![
1573 ((2f64, 0f64), (3f64, 4f64), (0.24f64, -0.32f64)),
1574 ((0f64, 2f64), (3f64, 4f64), (0.32f64, 0.24f64)),
1575 ((3f64, -2f64), (-3f64, 4f64), (-0.68f64, -0.24f64)),
1576 ];
1577
1578 for (a, b, expected_result) in in_out {
1579 let actual_result = KalkValue::Number(float!(a.0), float!(a.1), None)
1580 .div_without_unit(&KalkValue::Number(float!(b.0), float!(b.1), None))
1581 .unwrap();
1582 assert_eq!(actual_result.to_f64(), expected_result.0);
1583 assert_eq!(actual_result.imaginary_to_f64(), expected_result.1);
1584 }
1585 }
1586
1587 #[test]
1588 fn test_pow_complex() {
1589 let in_out = vec![
1590 ((2f64, 0f64), (0f64, 3f64), (-0.4869944f64, 0.8734050f64)),
1591 ((2f64, 0f64), (2f64, 3f64), (-1.9479776f64, 3.4936203f64)),
1592 ((0f64, 2f64), (0f64, 3f64), (-0.0043748f64, 0.0078460f64)),
1593 ((3f64, 2f64), (0f64, 3f64), (-0.1304148f64, -0.111153f64)),
1594 ((3f64, 2f64), (4f64, 3f64), (28.8577819f64, -2.422530f64)),
1595 ((-9f64, 0f64), (0.5f64, 0f64), (0f64, 3f64)),
1596 ((-9f64, 0f64), (-0.5f64, 0f64), (0f64, -1f64 / 3f64)),
1597 (
1598 (3f64, 0f64),
1599 (0f64, 1f64 / 3f64),
1600 (0.9336932f64, 0.3580738f64),
1601 ),
1602 (
1603 (3f64, 4f64),
1604 (0f64, 1f64 / 4f64),
1605 (0.7297490f64, 0.3105648f64),
1606 ),
1607 ];
1608
1609 for (a, b, expected_result) in in_out {
1610 let actual_result = KalkValue::Number(float!(a.0), float!(a.1), None)
1611 .pow_without_unit(&KalkValue::Number(float!(b.0), float!(b.1), None))
1612 .unwrap();
1613 assert!(cmp(actual_result.to_f64(), expected_result.0));
1614 assert!(cmp(actual_result.imaginary_to_f64(), expected_result.1));
1615 }
1616 }
1617
1618 #[test]
1619 fn test_to_string_pretty() {
1620 let in_out = vec![
1621 (float!(0.99999), float!(0.0), "0.99999 ≈ 1"),
1622 (float!(0.00000001), float!(0.0), "0.00000001 ≈ 10^-8"),
1623 (float!(-0.99999), float!(0.0), "-0.99999 ≈ -1"),
1624 (float!(0.0), float!(0.99999), "0.99999i ≈ i"),
1625 (float!(0.000000001), float!(0.0), "10^-9 ≈ 0"),
1626 (float!(0.0), float!(0.000000001), "10^-9 i ≈ 0"),
1627 (
1628 float!(0.99999),
1629 float!(0.999999),
1630 "0.99999 + 0.999999i ≈ 1 + i",
1631 ),
1632 (float!(1.0), float!(0.99999), "1 + 0.99999i ≈ 1 + i"),
1633 (
1634 float!(-0.99999),
1635 float!(0.999999),
1636 "-0.99999 + 0.999999i ≈ -1 + i",
1637 ),
1638 (
1639 float!(0.99999),
1640 float!(-0.999999),
1641 "0.99999 - 0.999999i ≈ 1 - i",
1642 ),
1643 (float!(-1.0), float!(0.99999), "-1 + 0.99999i ≈ -1 + i"),
1644 (float!(1.0), float!(-0.99999), "1 - 0.99999i ≈ 1 - i"),
1645 (float!(-0.99999), float!(1.0), "-0.99999 + i ≈ -1 + i"),
1646 (float!(0.99999), float!(-1.0), "0.99999 - i ≈ 1 - i"),
1647 (
1648 float!(0.000000001),
1649 float!(0.000000001),
1650 "10^-9 + 10^-9 i ≈ 0",
1651 ),
1652 (float!(1.0), float!(0.000000001), "1 + 10^-9 i ≈ 1"),
1653 (float!(0.000000001), float!(1.0), "10^-9 + i ≈ i"),
1654 (float!(-1.0), float!(0.000000001), "-1 + 10^-9 i ≈ -1"),
1655 (float!(0.000000001), float!(-1.0), "10^-9 - i ≈ -i"),
1656 (float!(10e-17), float!(1.0), "i"),
1657 (float!(1.0), float!(10e-17), "1"),
1658 (float!(10e-16), float!(0.0), "0"),
1659 (float!(3.00000000004), float!(0.0), "3"),
1660 ];
1661 for (real, imaginary, output) in in_out {
1662 let result = KalkValue::Number(real, imaginary, None)
1663 .to_string_pretty(ScientificNotationFormat::Normal);
1664 assert_eq!(output, result);
1665 }
1666 }
1667
1668 #[test]
1669 fn test_eng_mode() {
1670 let in_out = vec![
1671 (1.23, 0, "1.23×10^0"),
1672 (1.23, 1, "12.3×10^0"),
1673 (1.23, 2, "123×10^0"),
1674 (1.23, 3, "1.23×10^3"),
1675 (1.23, 4, "12.3×10^3"),
1676 (1.23, 5, "123×10^3"),
1677 (1.23, 6, "1.23×10^6"),
1678 ];
1679 for (value, exponent, output) in in_out {
1680 let sci = ScientificNotation {
1681 value,
1682 exponent: exponent + 1,
1683 imaginary: false,
1684 };
1685
1686 assert_eq!(
1687 sci.to_string_format(ScientificNotationFormat::Engineering),
1688 output
1689 );
1690 }
1691 }
1692}