@@ -6,10 +6,10 @@ use core::ops::{Div, Mul};
6
6
use core:: str:: FromStr ;
7
7
use std:: collections:: { BTreeMap , VecDeque } ;
8
8
9
- use fraction:: GenericFraction ;
10
9
use icu_provider:: DataError ;
11
10
use icu_unitsconversion:: provider:: { ConversionInfo , Exactness , Sign } ;
12
- use num_bigint:: BigUint ;
11
+ use num_bigint:: BigInt ;
12
+ use num_rational:: Ratio ;
13
13
14
14
use crate :: transform:: cldr:: cldr_serde:: units:: units_constants:: Constant ;
15
15
@@ -273,9 +273,7 @@ pub fn process_constants<'a>(
273
273
/// - " 1.5 E -2 " is converted to 15/1000
274
274
/// - " 1.5 E - 2" is an invalid scientific notation number
275
275
/// - "1.5E-2.5" is an invalid scientific notation number
276
- pub fn convert_scientific_notation_to_fraction (
277
- number : & str ,
278
- ) -> Result < GenericFraction < BigUint > , DataError > {
276
+ pub fn convert_scientific_notation_to_fraction ( number : & str ) -> Result < Ratio < BigInt > , DataError > {
279
277
let mut parts = number. split ( 'E' ) ;
280
278
let base = parts. next ( ) . unwrap_or ( "1" ) . trim ( ) ;
281
279
let exponent = parts. next ( ) . unwrap_or ( "0" ) . trim ( ) ;
@@ -285,51 +283,70 @@ pub fn convert_scientific_notation_to_fraction(
285
283
) ) ;
286
284
}
287
285
288
- let base = GenericFraction :: < BigUint > :: from_str ( base)
289
- . map_err ( |_| DataError :: custom ( "the base is not a valid decimal number" ) ) ?;
286
+ let mut split = base. split ( '.' ) ;
287
+ let base_whole = split. next ( ) . ok_or ( DataError :: custom ( "the base is empty" ) ) ?;
288
+ let base_whole = if base_whole. is_empty ( ) {
289
+ BigInt :: from ( 0 )
290
+ } else {
291
+ BigInt :: from_str ( base_whole) . map_err ( |_| {
292
+ DataError :: custom ( "the whole part of the base is not a valid whole number" )
293
+ } ) ?
294
+ } ;
295
+
296
+ let base_dec = if let Some ( dec_str) = split. next ( ) {
297
+ let dec = BigInt :: from_str ( dec_str) . map_err ( |_| {
298
+ DataError :: custom ( "the decimal part of the base is not a valid whole number" )
299
+ } ) ?;
300
+ let exp = dec_str. chars ( ) . filter ( char:: is_ascii_digit) . count ( ) ;
301
+ Ratio :: new ( dec, BigInt :: from ( 10u32 ) . pow ( exp as u32 ) )
302
+ } else {
303
+ Ratio :: new ( BigInt :: from ( 0 ) , BigInt :: from ( 1 ) )
304
+ } ;
305
+
306
+ let base = base_dec + base_whole;
307
+
290
308
let exponent = i32:: from_str ( exponent)
291
309
. map_err ( |_| DataError :: custom ( "the exponent is not a valid integer" ) ) ?;
292
310
293
311
let result = if exponent >= 0 {
294
- base. mul ( GenericFraction :: new (
295
- BigUint :: from ( 10u32 ) . pow ( exponent as u32 ) ,
296
- BigUint :: from ( 1u32 ) ,
312
+ base. mul ( Ratio :: new (
313
+ BigInt :: from ( 10u32 ) . pow ( exponent as u32 ) ,
314
+ BigInt :: from ( 1u32 ) ,
297
315
) )
298
316
} else {
299
- base. div ( GenericFraction :: new (
300
- BigUint :: from ( 10u32 ) . pow ( ( -exponent) as u32 ) ,
301
- BigUint :: from ( 1u32 ) ,
317
+ base. div ( Ratio :: new (
318
+ BigInt :: from ( 10u32 ) . pow ( ( -exponent) as u32 ) ,
319
+ BigInt :: from ( 1u32 ) ,
302
320
) )
303
321
} ;
304
-
305
322
Ok ( result)
306
323
}
307
324
308
325
// TODO: move this to the comment above.
309
326
#[ test]
310
327
fn test_convert_scientific_notation_to_fraction ( ) {
311
328
let input = "1E2" ;
312
- let expected = GenericFraction :: < BigUint > :: new ( BigUint :: from ( 100u32 ) , BigUint :: from ( 1u32 ) ) ;
329
+ let expected = Ratio :: < BigInt > :: new ( BigInt :: from ( 100u32 ) , BigInt :: from ( 1u32 ) ) ;
313
330
let actual = convert_scientific_notation_to_fraction ( input) . unwrap ( ) ;
314
331
assert_eq ! ( expected, actual) ;
315
332
316
333
let input = "1E-2" ;
317
- let expected = GenericFraction :: < BigUint > :: new ( BigUint :: from ( 1u32 ) , BigUint :: from ( 100u32 ) ) ;
334
+ let expected = Ratio :: < BigInt > :: new ( BigInt :: from ( 1u32 ) , BigInt :: from ( 100u32 ) ) ;
318
335
let actual = convert_scientific_notation_to_fraction ( input) . unwrap ( ) ;
319
336
assert_eq ! ( expected, actual) ;
320
337
321
338
let input = "1.5E2" ;
322
- let expected = GenericFraction :: < BigUint > :: new ( BigUint :: from ( 150u32 ) , BigUint :: from ( 1u32 ) ) ;
339
+ let expected = Ratio :: < BigInt > :: new ( BigInt :: from ( 150u32 ) , BigInt :: from ( 1u32 ) ) ;
323
340
let actual = convert_scientific_notation_to_fraction ( input) . unwrap ( ) ;
324
341
assert_eq ! ( expected, actual) ;
325
342
326
343
let input = "1.5E-2" ;
327
- let expected = GenericFraction :: < BigUint > :: new ( BigUint :: from ( 15u32 ) , BigUint :: from ( 1000u32 ) ) ;
344
+ let expected = Ratio :: < BigInt > :: new ( BigInt :: from ( 15u32 ) , BigInt :: from ( 1000u32 ) ) ;
328
345
let actual = convert_scientific_notation_to_fraction ( input) . unwrap ( ) ;
329
346
assert_eq ! ( expected, actual) ;
330
347
331
348
let input = " 1.5 E -2 " ;
332
- let expected = GenericFraction :: < BigUint > :: new ( BigUint :: from ( 15u32 ) , BigUint :: from ( 1000u32 ) ) ;
349
+ let expected = Ratio :: < BigInt > :: new ( BigInt :: from ( 15u32 ) , BigInt :: from ( 1000u32 ) ) ;
333
350
let actual = convert_scientific_notation_to_fraction ( input) . unwrap ( ) ;
334
351
assert_eq ! ( expected, actual) ;
335
352
@@ -340,6 +357,11 @@ fn test_convert_scientific_notation_to_fraction() {
340
357
let input = "1.5E-2.5" ;
341
358
let actual = convert_scientific_notation_to_fraction ( input) ;
342
359
assert ! ( actual. is_err( ) ) ;
360
+
361
+ let input = "0.308" ;
362
+ let expected = Ratio :: < BigInt > :: new ( BigInt :: from ( 308 ) , BigInt :: from ( 1000u32 ) ) ;
363
+ let actual = convert_scientific_notation_to_fraction ( input) . unwrap ( ) ;
364
+ assert_eq ! ( expected, actual) ;
343
365
}
344
366
345
367
/// Determines if a string contains any alphabetic characters.
@@ -390,28 +412,19 @@ pub fn is_scientific_number(s: &str) -> bool {
390
412
}
391
413
392
414
/// Transforms a fractional number into byte numerators, byte denominators, and a sign.
393
- pub fn flatten_fraction (
394
- fraction : GenericFraction < BigUint > ,
395
- ) -> Result < ( Vec < u8 > , Vec < u8 > , Sign ) , DataError > {
396
- let numerator = match fraction. numer ( ) {
397
- Some ( numerator) => numerator. to_bytes_le ( ) ,
398
- None => return Err ( DataError :: custom ( "the numerator is too large" ) ) ,
399
- } ;
400
-
401
- let denominator = match fraction. denom ( ) {
402
- Some ( denominator) => denominator. to_bytes_le ( ) ,
403
- None => return Err ( DataError :: custom ( "the denominator is too large" ) ) ,
404
- } ;
405
-
406
- let sign = match fraction. sign ( ) {
407
- Some ( fraction:: Sign :: Plus ) => Sign :: Positive ,
408
- Some ( fraction:: Sign :: Minus ) => Sign :: Negative ,
409
- None => {
410
- return Err ( DataError :: custom ( "the sign is not defined" ) ) ;
411
- }
415
+ pub fn flatten_fraction ( fraction : Ratio < BigInt > ) -> Result < ( Vec < u8 > , Vec < u8 > , Sign ) , DataError > {
416
+ let ( n_sign, numer) = fraction. numer ( ) . to_bytes_le ( ) ;
417
+ let ( d_sign, denom) = fraction. denom ( ) . to_bytes_le ( ) ;
418
+
419
+ // Ratio's constructor sets denom > 0 but it's worth
420
+ // checking in case we decide to switch to new_raw() to avoid reducing
421
+ let sign = if n_sign * d_sign == num_bigint:: Sign :: Minus {
422
+ Sign :: Negative
423
+ } else {
424
+ Sign :: Positive
412
425
} ;
413
426
414
- Ok ( ( numerator , denominator , sign) )
427
+ Ok ( ( numer , denom , sign) )
415
428
}
416
429
417
430
/// Converts slices of numerator and denominator strings to a fraction.
@@ -425,8 +438,8 @@ pub fn flatten_fraction(
425
438
pub fn convert_slices_to_fraction (
426
439
numerator_strings : & [ & str ] ,
427
440
denominator_strings : & [ & str ] ,
428
- ) -> Result < GenericFraction < BigUint > , DataError > {
429
- let mut fraction = GenericFraction :: new ( BigUint :: from ( 1u32 ) , BigUint :: from ( 1u32 ) ) ;
441
+ ) -> Result < Ratio < BigInt > , DataError > {
442
+ let mut fraction = Ratio :: new ( BigInt :: from ( 1u32 ) , BigInt :: from ( 1u32 ) ) ;
430
443
431
444
for numerator in numerator_strings {
432
445
let num_fraction = convert_scientific_notation_to_fraction ( numerator) ?;
@@ -446,19 +459,19 @@ pub fn convert_slices_to_fraction(
446
459
fn test_convert_array_of_strings_to_fraction ( ) {
447
460
let numerator: Vec < & str > = vec ! [ "1" ] ;
448
461
let denominator: Vec < & str > = vec ! [ "2" ] ;
449
- let expected = GenericFraction :: new ( BigUint :: from ( 1u32 ) , BigUint :: from ( 2u32 ) ) ;
462
+ let expected = Ratio :: new ( BigInt :: from ( 1i32 ) , BigInt :: from ( 2i32 ) ) ;
450
463
let actual = convert_slices_to_fraction ( & numerator, & denominator) . unwrap ( ) ;
451
464
assert_eq ! ( expected, actual) ;
452
465
453
466
let numerator = vec ! [ "1" , "2" ] ;
454
467
let denominator = vec ! [ "3" , "1E2" ] ;
455
- let expected = GenericFraction :: new ( BigUint :: from ( 2u32 ) , BigUint :: from ( 300u32 ) ) ;
468
+ let expected = Ratio :: new ( BigInt :: from ( 2i32 ) , BigInt :: from ( 300i32 ) ) ;
456
469
let actual = convert_slices_to_fraction ( & numerator, & denominator) . unwrap ( ) ;
457
470
assert_eq ! ( expected, actual) ;
458
471
459
472
let numerator = vec ! [ "1" , "2" ] ;
460
473
let denominator = vec ! [ "3" , "1E-2" ] ;
461
- let expected = GenericFraction :: new ( BigUint :: from ( 200u32 ) , BigUint :: from ( 3u32 ) ) ;
474
+ let expected = Ratio :: new ( BigInt :: from ( 200i32 ) , BigInt :: from ( 3i32 ) ) ;
462
475
let actual = convert_slices_to_fraction ( & numerator, & denominator) . unwrap ( ) ;
463
476
assert_eq ! ( expected, actual) ;
464
477
@@ -469,13 +482,13 @@ fn test_convert_array_of_strings_to_fraction() {
469
482
470
483
let numerator = vec ! [ "1E2" ] ;
471
484
let denominator = vec ! [ "2" ] ;
472
- let expected = GenericFraction :: new ( BigUint :: from ( 50u32 ) , BigUint :: from ( 1u32 ) ) ;
485
+ let expected = Ratio :: new ( BigInt :: from ( 50i32 ) , BigInt :: from ( 1i32 ) ) ;
473
486
let actual = convert_slices_to_fraction ( & numerator, & denominator) . unwrap ( ) ;
474
487
assert_eq ! ( expected, actual) ;
475
488
476
489
let numerator = vec ! [ "1E2" , "2" ] ;
477
490
let denominator = vec ! [ "3" , "1E2" ] ;
478
- let expected = GenericFraction :: new ( BigUint :: from ( 2u32 ) , BigUint :: from ( 3u32 ) ) ;
491
+ let expected = Ratio :: new ( BigInt :: from ( 2i32 ) , BigInt :: from ( 3i32 ) ) ;
479
492
let actual = convert_slices_to_fraction ( & numerator, & denominator) . unwrap ( ) ;
480
493
assert_eq ! ( expected, actual) ;
481
494
}
0 commit comments