From 2b23860917cc72a46862c0230eab6bb591e9f09c Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Fri, 19 Apr 2024 15:15:00 -0700 Subject: [PATCH 1/6] Add impl From<&CompactDecimal> for PluralOperands --- components/plurals/src/operands.rs | 82 ++++++++++++++++++++++++++---- 1 file changed, 72 insertions(+), 10 deletions(-) diff --git a/components/plurals/src/operands.rs b/components/plurals/src/operands.rs index ebb1803afeb..237388dbc1d 100644 --- a/components/plurals/src/operands.rs +++ b/components/plurals/src/operands.rs @@ -6,7 +6,7 @@ use core::isize; use core::num::ParseIntError; use core::str::FromStr; use displaydoc::Display; -use fixed_decimal::FixedDecimal; +use fixed_decimal::{CompactDecimal, FixedDecimal}; /// A full plural operands representation of a number. See [CLDR Plural Rules](http://unicode.org/reports/tr35/tr35-numbers.html#Language_Plural_Rules) for complete operands description. /// Plural operands in compliance with [CLDR Plural Rules](http://unicode.org/reports/tr35/tr35-numbers.html#Language_Plural_Rules). @@ -235,25 +235,26 @@ macro_rules! impl_signed_integer_type { impl_integer_type!(u8 u16 u32 u64 u128 usize); impl_signed_integer_type!(i8 i16 i32 i64 i128 isize); -impl From<&FixedDecimal> for PluralOperands { - /// Converts a [`fixed_decimal::FixedDecimal`] to [`PluralOperands`]. Retains at most 18 - /// digits each from the integer and fraction parts. - fn from(dec: &FixedDecimal) -> Self { +impl PluralOperands { + fn from_significand_and_exponent(dec: &FixedDecimal, exp: i16) -> PluralOperands { + let exp_usize = usize::try_from(exp).unwrap_or(0); + let exp = exp_usize as i16; // infallible because it started as an i16 + let mag_range = dec.magnitude_range(); - let mag_high = core::cmp::min(17, *mag_range.end()); - let mag_low = core::cmp::max(-18, *mag_range.start()); + let mag_high = core::cmp::min(17, *mag_range.end() + exp); + let mag_low = core::cmp::max(-18, *mag_range.start() + exp); let mut i: u64 = 0; for magnitude in (0..=mag_high).rev() { i *= 10; - i += dec.digit_at(magnitude) as u64; + i += dec.digit_at(magnitude - exp) as u64; } let mut f: u64 = 0; let mut t: u64 = 0; let mut w: usize = 0; for magnitude in (mag_low..=-1).rev() { - let digit = dec.digit_at(magnitude) as u64; + let digit = dec.digit_at(magnitude - exp) as u64; f *= 10; f += digit; if digit != 0 { @@ -268,7 +269,68 @@ impl From<&FixedDecimal> for PluralOperands { w, f, t, - c: 0, + c: exp_usize, } } } + +impl From<&FixedDecimal> for PluralOperands { + /// Converts a [`fixed_decimal::FixedDecimal`] to [`PluralOperands`]. Retains at most 18 + /// digits each from the integer and fraction parts. + fn from(dec: &FixedDecimal) -> Self { + Self::from_significand_and_exponent(dec, 0) + } +} + +impl From<&CompactDecimal> for PluralOperands { + /// Converts a [`fixed_decimal::CompactDecimal`] to [`PluralOperands`]. Retains at most 18 + /// digits each from the integer and fraction parts. + /// + /// If the [`CompactDecimal`] exponent is negative, it gets set to 0. + /// + /// # Examples + /// + /// ``` + /// use fixed_decimal::CompactDecimal; + /// use fixed_decimal::FixedDecimal; + /// use icu::locid::locale; + /// use icu::plurals::rules::RawPluralOperands; + /// use icu::plurals::PluralCategory; + /// use icu::plurals::PluralOperands; + /// use icu::plurals::PluralRules; + /// + /// let fixed_decimal = "1000000.20".parse::().unwrap(); + /// let compact_decimal = "1.00000020c6".parse::().unwrap(); + /// + /// assert_eq!( + /// PluralOperands::from(RawPluralOperands { + /// i: 1000000, + /// v: 2, + /// w: 1, + /// f: 20, + /// t: 2, + /// c: 0, + /// }), + /// PluralOperands::from(&fixed_decimal) + /// ); + /// + /// assert_eq!( + /// PluralOperands::from(RawPluralOperands { + /// i: 1000000, + /// v: 2, + /// w: 1, + /// f: 20, + /// t: 2, + /// c: 6, + /// }), + /// PluralOperands::from(&compact_decimal) + /// ); + /// + /// let rules = PluralRules::try_new_cardinal(&locale!("fr").into()).unwrap(); + /// assert_eq!(rules.category_for(&fixed_decimal), PluralCategory::Other); + /// assert_eq!(rules.category_for(&compact_decimal), PluralCategory::Many); + /// ``` + fn from(compact: &CompactDecimal) -> Self { + Self::from_significand_and_exponent(compact.significand(), compact.exponent()) + } +} From 9b2cea309133d987af405e6d4471e571c49f8203 Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Fri, 19 Apr 2024 15:22:36 -0700 Subject: [PATCH 2/6] Switch CompactDecimal exponent type to u8 for better consistency --- CHANGELOG.md | 4 ++++ components/plurals/src/operands.rs | 17 +++++++---------- utils/fixed_decimal/src/compact.rs | 4 ++-- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 05ce041d63d..1a71a2a20ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ - Added `total_cmp` functions to `Locale` and other types to make them easier to use in `BTreeSet` (https://github.com/unicode-org/icu4x/pull/4608) - `icu_locid_transform` - Add `LocaleExpander::minimize_favor_script` (https://github.com/unicode-org/icu4x/pull/4752) + - `icu_plurals` + - Added support for `CompactDecimal` (https://github.com/unicode-org/icu4x/pull/4828) - `icu_properties` - Add `Aran` script code (https://github.com/unicode-org/icu4x/pull/4426) - Mark additional constructors as `const` (https://github.com/unicode-org/icu4x/pull/4584, https://github.com/unicode-org/icu4x/pull/4574) @@ -47,6 +49,8 @@ - Deprecate `Hebrew::new_always_precomputing()`, `Date::try_new_hebrew_date_with_calendar()`, `DateTime::try_new_hebrew_datetime_with_calendar()`. The new implementation of the Hebrew calendar is faster and we do not need APIs for precomputation. (https://github.com/unicode-org/icu4x/pulls/4532) - `databake` - Add `impl Bake for PhantomData` (https://github.com/unicode-org/icu4x/pull/4663) + - `fixed_decimal` + - Changed type of compact exponent from `i16` to `u8` (https://github.com/unicode-org/icu4x/pull/4828) - `litemap` - Add `impl IntoIterator for LiteMap` by splitting `StoreIterableMut` trait (https://github.com/unicode-org/icu4x/pull/4359) - `yoke` diff --git a/components/plurals/src/operands.rs b/components/plurals/src/operands.rs index 237388dbc1d..7a718f549b6 100644 --- a/components/plurals/src/operands.rs +++ b/components/plurals/src/operands.rs @@ -236,25 +236,24 @@ impl_integer_type!(u8 u16 u32 u64 u128 usize); impl_signed_integer_type!(i8 i16 i32 i64 i128 isize); impl PluralOperands { - fn from_significand_and_exponent(dec: &FixedDecimal, exp: i16) -> PluralOperands { - let exp_usize = usize::try_from(exp).unwrap_or(0); - let exp = exp_usize as i16; // infallible because it started as an i16 + fn from_significand_and_exponent(dec: &FixedDecimal, exp: u8) -> PluralOperands { + let exp_i16 = i16::from(exp); let mag_range = dec.magnitude_range(); - let mag_high = core::cmp::min(17, *mag_range.end() + exp); - let mag_low = core::cmp::max(-18, *mag_range.start() + exp); + let mag_high = core::cmp::min(17, *mag_range.end() + exp_i16); + let mag_low = core::cmp::max(-18, *mag_range.start() + exp_i16); let mut i: u64 = 0; for magnitude in (0..=mag_high).rev() { i *= 10; - i += dec.digit_at(magnitude - exp) as u64; + i += dec.digit_at(magnitude - exp_i16) as u64; } let mut f: u64 = 0; let mut t: u64 = 0; let mut w: usize = 0; for magnitude in (mag_low..=-1).rev() { - let digit = dec.digit_at(magnitude - exp) as u64; + let digit = dec.digit_at(magnitude - exp_i16) as u64; f *= 10; f += digit; if digit != 0 { @@ -269,7 +268,7 @@ impl PluralOperands { w, f, t, - c: exp_usize, + c: usize::from(exp), } } } @@ -286,8 +285,6 @@ impl From<&CompactDecimal> for PluralOperands { /// Converts a [`fixed_decimal::CompactDecimal`] to [`PluralOperands`]. Retains at most 18 /// digits each from the integer and fraction parts. /// - /// If the [`CompactDecimal`] exponent is negative, it gets set to 0. - /// /// # Examples /// /// ``` diff --git a/utils/fixed_decimal/src/compact.rs b/utils/fixed_decimal/src/compact.rs index f80d4796b6e..393c0356115 100644 --- a/utils/fixed_decimal/src/compact.rs +++ b/utils/fixed_decimal/src/compact.rs @@ -22,7 +22,7 @@ use crate::FixedDecimal; #[derive(Debug, Clone, PartialEq)] pub struct CompactDecimal { significand: FixedDecimal, - exponent: i16, + exponent: u8, } impl CompactDecimal { @@ -74,7 +74,7 @@ impl CompactDecimal { /// assert_eq!(CompactDecimal::from_str("+1.20c6").unwrap().exponent(), 6); /// assert_eq!(CompactDecimal::from_str("1729").unwrap().exponent(), 0); /// ``` - pub fn exponent(&self) -> i16 { + pub fn exponent(&self) -> u8 { self.exponent } } From 69eb9f282649edfcaf695b8667f79d685417aa3f Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Fri, 19 Apr 2024 15:27:08 -0700 Subject: [PATCH 3/6] i16 -> u8 --- .../experimental/src/compactdecimal/compactdecimal.rs | 6 +++--- components/experimental/src/compactdecimal/error.rs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/components/experimental/src/compactdecimal/compactdecimal.rs b/components/experimental/src/compactdecimal/compactdecimal.rs index c9003914d5e..2c195bbc00b 100644 --- a/components/experimental/src/compactdecimal/compactdecimal.rs +++ b/components/experimental/src/compactdecimal/compactdecimal.rs @@ -629,14 +629,14 @@ impl CompactDecimalFormatter { &'l self, value: &'l CompactDecimal, ) -> Result, CompactDecimalError> { - let log10_type = value.significand().nonzero_magnitude_start() + value.exponent(); + let log10_type = value.significand().nonzero_magnitude_start() + i16::from(value.exponent()); let (plural_map, expected_exponent) = self.plural_map_and_exponent_for_magnitude(log10_type); - if value.exponent() != i16::from(expected_exponent) { + if value.exponent() != expected_exponent { return Err(CompactDecimalError::Exponent { actual: value.exponent(), - expected: i16::from(expected_exponent), + expected: expected_exponent, log10_type, }); } diff --git a/components/experimental/src/compactdecimal/error.rs b/components/experimental/src/compactdecimal/error.rs index 29b7c529046..7a46acf3717 100644 --- a/components/experimental/src/compactdecimal/error.rs +++ b/components/experimental/src/compactdecimal/error.rs @@ -32,9 +32,9 @@ pub enum CompactDecimalError { #[displaydoc("Expected compact exponent {expected} for 10^{log10_type}, got {actual}")] Exponent { /// The compact decimal exponent passed to the formatter. - actual: i16, + actual: u8, /// The appropriate compact decimal exponent for a number of the given magnitude. - expected: i16, + expected: u8, /// The magnitude of the number being formatted. log10_type: i16, }, From e6cf7a7c81ba1bdd83350b1a837bbe0a3b64a740 Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Mon, 29 Apr 2024 19:00:17 -0700 Subject: [PATCH 4/6] fmt --- components/experimental/src/compactdecimal/compactdecimal.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/experimental/src/compactdecimal/compactdecimal.rs b/components/experimental/src/compactdecimal/compactdecimal.rs index 2c195bbc00b..d98f9fe4ac2 100644 --- a/components/experimental/src/compactdecimal/compactdecimal.rs +++ b/components/experimental/src/compactdecimal/compactdecimal.rs @@ -629,7 +629,8 @@ impl CompactDecimalFormatter { &'l self, value: &'l CompactDecimal, ) -> Result, CompactDecimalError> { - let log10_type = value.significand().nonzero_magnitude_start() + i16::from(value.exponent()); + let log10_type = + value.significand().nonzero_magnitude_start() + i16::from(value.exponent()); let (plural_map, expected_exponent) = self.plural_map_and_exponent_for_magnitude(log10_type); From 27f5bc35acb7ddb5016e920fbf35f6c6e256e262 Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Mon, 29 Apr 2024 19:00:52 -0700 Subject: [PATCH 5/6] clippy --- utils/fixed_decimal/src/compact.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/fixed_decimal/src/compact.rs b/utils/fixed_decimal/src/compact.rs index 393c0356115..98af2e76dba 100644 --- a/utils/fixed_decimal/src/compact.rs +++ b/utils/fixed_decimal/src/compact.rs @@ -30,7 +30,7 @@ impl CompactDecimal { pub fn from_significand_and_exponent(significand: FixedDecimal, exponent: u8) -> Self { Self { significand, - exponent: exponent.into(), + exponent, } } From 579a45d1eb498db23075c1c14927fbdd397c5bc3 Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Mon, 29 Apr 2024 19:07:02 -0700 Subject: [PATCH 6/6] Change tutorials to fix resolver issue --- tutorials/rust/baked/src/main.rs | 2 +- tutorials/rust/custom_compiled/src/main.rs | 2 +- tutorials/rust/default/src/main.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tutorials/rust/baked/src/main.rs b/tutorials/rust/baked/src/main.rs index f1ac7c1a03c..b53dd4a5e87 100644 --- a/tutorials/rust/baked/src/main.rs +++ b/tutorials/rust/baked/src/main.rs @@ -18,7 +18,7 @@ impl_data_provider!(BakedProvider); fn main() { let rules = PluralRules::try_new_cardinal_unstable(&BakedProvider, &locale!("ru").into()) .expect("locale 'ru' should be present in the baked data"); - let result = rules.category_for(&3.into()); + let result = rules.category_for(3); assert_eq!(result, PluralCategory::Few); println!("{:?}", result); } diff --git a/tutorials/rust/custom_compiled/src/main.rs b/tutorials/rust/custom_compiled/src/main.rs index b58445870e3..31d8dfcb285 100644 --- a/tutorials/rust/custom_compiled/src/main.rs +++ b/tutorials/rust/custom_compiled/src/main.rs @@ -25,7 +25,7 @@ use icu::plurals::PluralRules; fn main() { let rules = PluralRules::try_new_cardinal(&locale!("ru").into()) .expect("locale 'ru' should be present in the compiled data"); - let result = rules.category_for(&3.into()); + let result = rules.category_for(3); assert_eq!(result, PluralCategory::Few); println!("{result:?}"); } diff --git a/tutorials/rust/default/src/main.rs b/tutorials/rust/default/src/main.rs index 2b19deee790..52670896dc1 100644 --- a/tutorials/rust/default/src/main.rs +++ b/tutorials/rust/default/src/main.rs @@ -13,7 +13,7 @@ use icu::plurals::PluralRules; fn main() { let rules = PluralRules::try_new_cardinal(&locale!("ru").into()) .expect("locale 'ru' should be present in the compiled data"); - let result = rules.category_for(&3.into()); + let result = rules.category_for(3); assert_eq!(result, PluralCategory::Few); println!("{result:?}"); }