|
1 | 1 | // spell-checker:ignore ddfe |
2 | 2 | use itertools::{Itertools, PeekingNext}; |
| 3 | +use malachite_base::num::basic::floats::PrimitiveFloat; |
3 | 4 | use malachite_bigint::{BigInt, Sign}; |
| 5 | +use num_complex::Complex64; |
4 | 6 | use num_traits::FromPrimitive; |
5 | 7 | use num_traits::{Signed, cast::ToPrimitive}; |
6 | 8 | use rustpython_literal::float; |
@@ -612,6 +614,126 @@ impl FormatSpec { |
612 | 614 | } |
613 | 615 | } |
614 | 616 |
|
| 617 | + pub fn format_complex(&self, num: &Complex64) -> Result<String, FormatSpecError> { |
| 618 | + let (formatted_re, formatted_im) = self.format_complex_re_im(num)?; |
| 619 | + // Enclose in parentheses if there is no format type and formatted_re is not empty |
| 620 | + let magnitude_str = if self.format_type.is_none() && !formatted_re.is_empty() { |
| 621 | + format!("({formatted_re}{formatted_im})") |
| 622 | + } else { |
| 623 | + format!("{formatted_re}{formatted_im}") |
| 624 | + }; |
| 625 | + if let Some(FormatAlign::AfterSign) = &self.align { |
| 626 | + return Err(FormatSpecError::AlignmentFlag); |
| 627 | + } |
| 628 | + match &self.fill.unwrap_or(' '.into()).to_char() { |
| 629 | + Some('0') => Err(FormatSpecError::ZeroPadding), |
| 630 | + _ => self.format_sign_and_align(&AsciiStr::new(&magnitude_str), "", FormatAlign::Right), |
| 631 | + } |
| 632 | + } |
| 633 | + |
| 634 | + fn format_complex_re_im(&self, num: &Complex64) -> Result<(String, String), FormatSpecError> { |
| 635 | + // Format real part |
| 636 | + let mut formatted_re = String::new(); |
| 637 | + if num.re != 0.0 || num.re.is_negative_zero() || self.format_type.is_some() { |
| 638 | + let sign_re = if num.re.is_sign_negative() && !num.is_nan() { |
| 639 | + "-" |
| 640 | + } else { |
| 641 | + match self.sign.unwrap_or(FormatSign::Minus) { |
| 642 | + FormatSign::Plus => "+", |
| 643 | + FormatSign::Minus => "", |
| 644 | + FormatSign::MinusOrSpace => " ", |
| 645 | + } |
| 646 | + }; |
| 647 | + let re = self.format_complex_float(num.re)?; |
| 648 | + formatted_re = format!("{sign_re}{re}"); |
| 649 | + } |
| 650 | + // Format imaginary part |
| 651 | + let sign_im = if num.im.is_sign_negative() && !num.im.is_nan() { |
| 652 | + "-" |
| 653 | + } else if formatted_re.is_empty() { |
| 654 | + "" |
| 655 | + } else { |
| 656 | + "+" |
| 657 | + }; |
| 658 | + let im = self.format_complex_float(num.im)?; |
| 659 | + Ok((formatted_re, format!("{sign_im}{im}j"))) |
| 660 | + } |
| 661 | + |
| 662 | + fn format_complex_float(&self, num: f64) -> Result<String, FormatSpecError> { |
| 663 | + self.validate_format(FormatType::FixedPoint(Case::Lower))?; |
| 664 | + let precision = self.precision.unwrap_or(6); |
| 665 | + let magnitude = num.abs(); |
| 666 | + let magnitude_str = match &self.format_type { |
| 667 | + Some(FormatType::Decimal) |
| 668 | + | Some(FormatType::Binary) |
| 669 | + | Some(FormatType::Octal) |
| 670 | + | Some(FormatType::Hex(_)) |
| 671 | + | Some(FormatType::String) |
| 672 | + | Some(FormatType::Character) |
| 673 | + | Some(FormatType::Number(Case::Upper)) |
| 674 | + | Some(FormatType::Percentage) => { |
| 675 | + let ch = char::from(self.format_type.as_ref().unwrap()); |
| 676 | + Err(FormatSpecError::UnknownFormatCode(ch, "complex")) |
| 677 | + } |
| 678 | + Some(FormatType::FixedPoint(case)) => Ok(float::format_fixed( |
| 679 | + precision, |
| 680 | + magnitude, |
| 681 | + *case, |
| 682 | + self.alternate_form, |
| 683 | + )), |
| 684 | + Some(FormatType::GeneralFormat(case)) | Some(FormatType::Number(case)) => { |
| 685 | + let precision = if precision == 0 { 1 } else { precision }; |
| 686 | + Ok(float::format_general( |
| 687 | + precision, |
| 688 | + magnitude, |
| 689 | + *case, |
| 690 | + self.alternate_form, |
| 691 | + false, |
| 692 | + )) |
| 693 | + } |
| 694 | + Some(FormatType::Exponent(case)) => Ok(float::format_exponent( |
| 695 | + precision, |
| 696 | + magnitude, |
| 697 | + *case, |
| 698 | + self.alternate_form, |
| 699 | + )), |
| 700 | + None => match magnitude { |
| 701 | + magnitude if magnitude.is_nan() => Ok("nan".to_owned()), |
| 702 | + magnitude if magnitude.is_infinite() => Ok("inf".to_owned()), |
| 703 | + _ => match self.precision { |
| 704 | + Some(precision) => Ok(float::format_general( |
| 705 | + precision, |
| 706 | + magnitude, |
| 707 | + Case::Lower, |
| 708 | + self.alternate_form, |
| 709 | + true, |
| 710 | + )), |
| 711 | + None => { |
| 712 | + if magnitude.fract() == 0.0 { |
| 713 | + Ok(magnitude.trunc().to_string()) |
| 714 | + } else { |
| 715 | + Ok(magnitude.to_string()) |
| 716 | + } |
| 717 | + } |
| 718 | + }, |
| 719 | + }, |
| 720 | + }?; |
| 721 | + match &self.grouping_option { |
| 722 | + Some(fg) => { |
| 723 | + let sep = match fg { |
| 724 | + FormatGrouping::Comma => ',', |
| 725 | + FormatGrouping::Underscore => '_', |
| 726 | + }; |
| 727 | + let inter = self.get_separator_interval().try_into().unwrap(); |
| 728 | + let len = magnitude_str.len() as i32; |
| 729 | + let separated_magnitude = |
| 730 | + FormatSpec::add_magnitude_separators_for_char(magnitude_str, inter, sep, len); |
| 731 | + Ok(separated_magnitude) |
| 732 | + } |
| 733 | + None => Ok(magnitude_str), |
| 734 | + } |
| 735 | + } |
| 736 | + |
615 | 737 | fn format_sign_and_align<T>( |
616 | 738 | &self, |
617 | 739 | magnitude_str: &T, |
@@ -701,6 +823,8 @@ pub enum FormatSpecError { |
701 | 823 | NotAllowed(&'static str), |
702 | 824 | UnableToConvert, |
703 | 825 | CodeNotInRange, |
| 826 | + ZeroPadding, |
| 827 | + AlignmentFlag, |
704 | 828 | NotImplemented(char, &'static str), |
705 | 829 | } |
706 | 830 |
|
|
0 commit comments