@@ -255,6 +255,7 @@ impl Add for JaqElement {
255
255
// Sequences and strings concatenate
256
256
( List ( a) , List ( b) ) => ion_rs:: List :: from_iter ( a. into_iter ( ) . chain ( b) ) . into ( ) ,
257
257
( SExp ( a) , SExp ( b) ) => ion_rs:: SExp :: from_iter ( a. into_iter ( ) . chain ( b) ) . into ( ) ,
258
+ //TODO: Does it make sense to concatenate a String and a Symbol? What type results?
258
259
( String ( a) , String ( b) ) => format ! ( "{}{}" , a. text( ) , b. text( ) ) . into ( ) ,
259
260
( Symbol ( a) , Symbol ( b) ) => match ( a. text ( ) , b. text ( ) ) {
260
261
( Some ( ta) , Some ( tb) ) => format ! ( "{}{}" , ta, tb) ,
@@ -264,22 +265,18 @@ impl Add for JaqElement {
264
265
. into ( ) ,
265
266
266
267
// Structs merge
267
- //TODO: Recursively remove duplicate fields, see doc comment for method
268
+ //TODO: Recursively remove duplicate fields, see doc comment for rules
268
269
( Struct ( a) , Struct ( b) ) => a. clone_builder ( ) . with_fields ( b. fields ( ) ) . build ( ) . into ( ) ,
269
270
270
271
// Number types, only lossless operations
271
272
( Int ( a) , Int ( b) ) => ( a + b) . into ( ) ,
272
- ( Float ( a) , Float ( b) ) => ( a + b) . into ( ) ,
273
+ ( Float ( a) , Float ( b) ) => a . add ( b) . into ( ) ,
273
274
( Decimal ( a) , Decimal ( b) ) => a. add ( b) . into ( ) ,
274
275
( Decimal ( a) , Int ( b) ) | ( Int ( b) , Decimal ( a) ) => a. add ( b) . into ( ) ,
275
276
276
277
// Only try potentially lossy Float conversions when we've run out of the other options
277
- ( a @ Int ( _) | a @ Decimal ( _) , Float ( b) ) | ( Float ( b) , a @ Int ( _) | a @ Decimal ( _) ) => {
278
- let Some ( f) = a. clone ( ) . to_f64 ( ) else {
279
- return jaq_unary_error ( a, "cannot be an f64" ) ;
280
- } ;
281
- ( f + b) . into ( )
282
- }
278
+ ( a @ Int ( _) | a @ Decimal ( _) , Float ( b) ) => ( a. to_f64 ( ) . unwrap ( ) + b) . into ( ) ,
279
+ ( Float ( a) , b @ Int ( _) | b @ Decimal ( _) ) => ( a + b. to_f64 ( ) . unwrap ( ) ) . into ( ) ,
283
280
284
281
( a, b) => return jaq_binary_error ( a, b, "cannot be added" ) ,
285
282
} ;
@@ -301,40 +298,28 @@ impl Sub for JaqElement {
301
298
use ion_math:: { DecimalMath , ToFloat } ;
302
299
use Value :: * ;
303
300
301
+ // b.iter.contains() will make these implementations O(N^2).
302
+ // Neither Element nor Value implement Hash or Ord, so faster lookup isn't available
303
+ // Perhaps someday we can do something more clever with ionhash or IonOrd?
304
+ fn remove_elements ( a : Sequence , b : & Sequence ) -> impl Iterator < Item = Element > + ' _ {
305
+ a. into_iter ( ) . filter ( |i| !b. iter ( ) . contains ( i) )
306
+ }
307
+
304
308
let elt: Element = match ( lhv, rhv) {
305
309
// Sequences and strings do set subtraction with RHS
306
- // b.iter.contains() will make these implementations O(N^2).
307
- // Neither Element nor Value implement Hash or Ord, so faster lookup isn't available
308
- // Perhaps someday we can do something more clever with ion-hash or IonOrd?
309
- ( List ( a) , List ( b) ) => {
310
- let a_less_b = a. into_iter ( ) . filter ( |i| !b. iter ( ) . contains ( i) ) ;
311
- ion_rs:: List :: from_iter ( a_less_b) . into ( )
312
- }
313
- ( SExp ( a) , SExp ( b) ) => {
314
- let a_less_b = a. into_iter ( ) . filter ( |i| !b. iter ( ) . contains ( i) ) ;
315
- ion_rs:: SExp :: from_iter ( a_less_b) . into ( )
316
- }
310
+ ( List ( a) , List ( b) ) => ion_rs:: List :: from_iter ( remove_elements ( a, & b) ) . into ( ) ,
311
+ ( SExp ( a) , SExp ( b) ) => ion_rs:: SExp :: from_iter ( remove_elements ( a, & b) ) . into ( ) ,
317
312
318
313
// Number types, only lossless operations
319
314
( Int ( a) , Int ( b) ) => ( a + -b) . into ( ) , //TODO: use bare - with ion-rs > rc.11
320
- ( Float ( a) , Float ( b) ) => ( a - b) . into ( ) ,
315
+ ( Float ( a) , Float ( b) ) => a . sub ( b) . into ( ) ,
321
316
( Decimal ( a) , Decimal ( b) ) => a. sub ( b) . into ( ) ,
322
317
( Decimal ( a) , Int ( b) ) => a. sub ( b) . into ( ) ,
323
318
( Int ( a) , Decimal ( b) ) => a. sub ( b) . into ( ) ,
324
319
325
320
// Only try potentially lossy Float conversions when we've run out of the other options
326
- ( a @ Int ( _) | a @ Decimal ( _) , Float ( b) ) => {
327
- let Some ( f) = a. clone ( ) . to_f64 ( ) else {
328
- return jaq_unary_error ( a, "cannot be an f64" ) ;
329
- } ;
330
- ( f - b) . into ( )
331
- }
332
- ( Float ( a) , b @ Int ( _) | b @ Decimal ( _) ) => {
333
- let Some ( f) = b. clone ( ) . to_f64 ( ) else {
334
- return jaq_unary_error ( b, "cannot be an f64" ) ;
335
- } ;
336
- ( a - f) . into ( )
337
- }
321
+ ( a @ Int ( _) | a @ Decimal ( _) , Float ( b) ) => ( a. to_f64 ( ) . unwrap ( ) - b) . into ( ) ,
322
+ ( Float ( a) , b @ Int ( _) | b @ Decimal ( _) ) => ( a - b. to_f64 ( ) . unwrap ( ) ) . into ( ) ,
338
323
339
324
( a, b) => return jaq_binary_error ( a, b, "cannot be subtracted" ) ,
340
325
} ;
@@ -368,12 +353,11 @@ impl Mul for JaqElement {
368
353
369
354
( Symbol ( a) , Int ( b) ) | ( Int ( b) , Symbol ( a) ) => match ( b. as_usize ( ) , a. text ( ) ) {
370
355
( Some ( n) , Some ( t) ) => t. repeat ( n) . into ( ) ,
371
- //TODO: Handle symbols with unknown text?
372
- _ => Null ( IonType :: Null ) . into ( ) ,
356
+ _ => Null ( IonType :: Null ) . into ( ) , //TODO: Handle symbols with unknown text? How?
373
357
} ,
374
358
375
359
// Structs merge recursively
376
- //TODO: Recursively remove duplicate fields, recursively merge if struct fields collide
360
+ //TODO: Recursively remove duplicate fields, see doc comments for rules
377
361
( Struct ( a) , Struct ( b) ) => a. clone_builder ( ) . with_fields ( b. fields ( ) ) . build ( ) . into ( ) ,
378
362
379
363
// Number types, only lossless operations
@@ -384,12 +368,8 @@ impl Mul for JaqElement {
384
368
( Decimal ( a) , Int ( b) ) | ( Int ( b) , Decimal ( a) ) => a. mul ( b) . into ( ) ,
385
369
386
370
// Only try potentially lossy Float conversions when we've run out of the other options
387
- ( a @ Int ( _) | a @ Decimal ( _) , Float ( b) ) | ( Float ( b) , a @ Int ( _) | a @ Decimal ( _) ) => {
388
- let Some ( f) = a. clone ( ) . to_f64 ( ) else {
389
- return jaq_unary_error ( a, "cannot be an f64" ) ;
390
- } ;
391
- ( f * b) . into ( )
392
- }
371
+ ( a @ Int ( _) | a @ Decimal ( _) , Float ( b) ) => ( a. to_f64 ( ) . unwrap ( ) * b) . into ( ) ,
372
+ ( Float ( a) , b @ Int ( _) | b @ Decimal ( _) ) => ( a * b. to_f64 ( ) . unwrap ( ) ) . into ( ) ,
393
373
394
374
( a, b) => return jaq_binary_error ( a, b, "cannot be multiplied" ) ,
395
375
} ;
@@ -413,13 +393,13 @@ impl Div for JaqElement {
413
393
let elt: Element = match ( lhv, rhv) {
414
394
// Dividing a string by another splits the first using the second as separators.
415
395
( String ( a) , String ( b) ) => {
416
- let split = a. text ( ) . split ( b. text ( ) ) ;
417
- let iter = split. map ( |s| String ( s . into ( ) ) ) . map ( Element :: from) ;
396
+ let ( ta , tb ) = ( a. text ( ) , b. text ( ) ) ;
397
+ let iter = ta . split ( tb ) . map ( ion_rs :: Str :: from ) . map ( Element :: from) ;
418
398
ion_rs:: List :: from_iter ( iter) . into ( )
419
399
}
420
400
( Symbol ( a) , Symbol ( b) ) => match ( a. text ( ) , b. text ( ) ) {
421
401
( Some ( ta) , Some ( tb) ) => {
422
- let iter = ta. split ( tb) . map ( |s| Symbol ( s . into ( ) ) ) . map ( Element :: from) ;
402
+ let iter = ta. split ( tb) . map ( ion_rs :: Symbol :: from ) . map ( Element :: from) ;
423
403
ion_rs:: List :: from_iter ( iter)
424
404
}
425
405
//TODO: Handle symbols with unknown text?
@@ -435,18 +415,8 @@ impl Div for JaqElement {
435
415
( Int ( a) , Decimal ( b) ) => a. div ( b) . into ( ) ,
436
416
437
417
// Only try potentially lossy Float conversions when we've run out of the other options
438
- ( a @ Int ( _) | a @ Decimal ( _) , Float ( b) ) => {
439
- let Some ( f) = a. clone ( ) . to_f64 ( ) else {
440
- return jaq_unary_error ( a, "cannot be an f64" ) ;
441
- } ;
442
- ( f / b) . into ( )
443
- }
444
- ( Float ( a) , b @ Int ( _) | b @ Decimal ( _) ) => {
445
- let Some ( f) = b. clone ( ) . to_f64 ( ) else {
446
- return jaq_unary_error ( b, "cannot be an f64" ) ;
447
- } ;
448
- ( a / f) . into ( )
449
- }
418
+ ( a @ Int ( _) | a @ Decimal ( _) , Float ( b) ) => ( a. to_f64 ( ) . unwrap ( ) / b) . into ( ) ,
419
+ ( Float ( a) , b @ Int ( _) | b @ Decimal ( _) ) => ( a / b. to_f64 ( ) . unwrap ( ) ) . into ( ) ,
450
420
451
421
( a, b) => return jaq_binary_error ( a, b, "cannot be divided" ) ,
452
422
} ;
@@ -473,18 +443,8 @@ impl Rem for JaqElement {
473
443
( Int ( a) , Decimal ( b) ) => a. rem ( b) . into ( ) ,
474
444
475
445
// Only try potentially lossy Float conversions when we've run out of the other options
476
- ( a @ Int ( _) | a @ Decimal ( _) , Float ( b) ) => {
477
- let Some ( f) = a. clone ( ) . to_f64 ( ) else {
478
- return jaq_unary_error ( a, "cannot be an f64" ) ;
479
- } ;
480
- ( f % b) . into ( )
481
- }
482
- ( Float ( a) , b @ Int ( _) | b @ Decimal ( _) ) => {
483
- let Some ( f) = b. clone ( ) . to_f64 ( ) else {
484
- return jaq_unary_error ( b, "cannot be an f64" ) ;
485
- } ;
486
- ( a % f) . into ( )
487
- }
446
+ ( a @ Int ( _) | a @ Decimal ( _) , Float ( b) ) => ( a. to_f64 ( ) . unwrap ( ) % b) . into ( ) ,
447
+ ( Float ( a) , b @ Int ( _) | b @ Decimal ( _) ) => ( a % b. to_f64 ( ) . unwrap ( ) ) . into ( ) ,
488
448
489
449
( a, b) => return jaq_binary_error ( a, b, "cannot be divided (remainder)" ) ,
490
450
} ;
@@ -506,7 +466,7 @@ impl Neg for JaqElement {
506
466
// Only number types can be negated
507
467
Int ( a) => ( -a) . into ( ) ,
508
468
Float ( a) => ( -a) . into ( ) ,
509
- Decimal ( a) => ( -a. to_big_decimal ( ) ) . to_decimal ( ) . into ( ) ,
469
+ Decimal ( a) => ( -a. into_big_decimal ( ) ) . into_decimal ( ) . into ( ) ,
510
470
511
471
other => return jaq_unary_error ( other, "cannot be negated" ) ,
512
472
} ;
@@ -643,7 +603,7 @@ impl jaq_std::ValT for JaqElement {
643
603
fn as_isize ( & self ) -> Option < isize > {
644
604
match self . 0 . value ( ) {
645
605
Value :: Int ( i) => i. expect_i64 ( ) . unwrap ( ) . to_isize ( ) ,
646
- Value :: Decimal ( d) => d. to_big_decimal ( ) . to_isize ( ) ,
606
+ Value :: Decimal ( d) => d. into_big_decimal ( ) . to_isize ( ) ,
647
607
_ => None ,
648
608
}
649
609
}
@@ -672,32 +632,32 @@ pub(crate) mod ion_math {
672
632
673
633
/// We can't provide math traits for Decimal directly, so we have a helper trait
674
634
pub ( crate ) trait DecimalMath : Sized {
675
- fn to_big_decimal ( self ) -> BigDecimal ;
676
- fn to_decimal ( self ) -> Decimal ;
635
+ fn into_big_decimal ( self ) -> BigDecimal ;
636
+ fn into_decimal ( self ) -> Decimal ;
677
637
678
638
fn add ( self , v2 : impl DecimalMath ) -> Decimal {
679
- ( self . to_big_decimal ( ) + v2. to_big_decimal ( ) ) . to_decimal ( )
639
+ ( self . into_big_decimal ( ) + v2. into_big_decimal ( ) ) . into_decimal ( )
680
640
}
681
641
682
642
fn sub ( self , v2 : impl DecimalMath ) -> Decimal {
683
- ( self . to_big_decimal ( ) - v2. to_big_decimal ( ) ) . to_decimal ( )
643
+ ( self . into_big_decimal ( ) - v2. into_big_decimal ( ) ) . into_decimal ( )
684
644
}
685
645
686
646
fn mul ( self , v2 : impl DecimalMath ) -> Decimal {
687
- ( self . to_big_decimal ( ) * v2. to_big_decimal ( ) ) . to_decimal ( )
647
+ ( self . into_big_decimal ( ) * v2. into_big_decimal ( ) ) . into_decimal ( )
688
648
}
689
649
690
650
fn div ( self , v2 : impl DecimalMath ) -> Decimal {
691
- ( self . to_big_decimal ( ) / v2. to_big_decimal ( ) ) . to_decimal ( )
651
+ ( self . into_big_decimal ( ) / v2. into_big_decimal ( ) ) . into_decimal ( )
692
652
}
693
653
694
654
fn rem ( self , v2 : impl DecimalMath ) -> Decimal {
695
- ( self . to_big_decimal ( ) % v2. to_big_decimal ( ) ) . to_decimal ( )
655
+ ( self . into_big_decimal ( ) % v2. into_big_decimal ( ) ) . into_decimal ( )
696
656
}
697
657
}
698
658
699
659
impl DecimalMath for Decimal {
700
- fn to_big_decimal ( self ) -> BigDecimal {
660
+ fn into_big_decimal ( self ) -> BigDecimal {
701
661
let magnitude = self . coefficient ( ) . magnitude ( ) . as_u128 ( ) . unwrap ( ) ;
702
662
let bigint = match self . coefficient ( ) . sign ( ) {
703
663
Sign :: Negative => -BigInt :: from ( magnitude) ,
@@ -706,35 +666,41 @@ pub(crate) mod ion_math {
706
666
BigDecimal :: new ( bigint, self . scale ( ) )
707
667
}
708
668
709
- fn to_decimal ( self ) -> Decimal {
669
+ fn into_decimal ( self ) -> Decimal {
710
670
self
711
671
}
712
672
}
713
673
714
674
impl DecimalMath for Int {
715
- fn to_big_decimal ( self ) -> BigDecimal {
675
+ fn into_big_decimal ( self ) -> BigDecimal {
716
676
let data = self . expect_i128 ( ) . unwrap ( ) ; // error case is unreachable with current ion-rs
717
677
BigDecimal :: from ( data)
718
678
}
719
679
720
- fn to_decimal ( self ) -> Decimal {
680
+ fn into_decimal ( self ) -> Decimal {
721
681
let data = self . expect_i128 ( ) . unwrap ( ) ; // error case is unreachable with current ion-rs
722
682
Decimal :: new ( data, 0 )
723
683
}
724
684
}
725
685
726
686
impl DecimalMath for BigDecimal {
727
- fn to_big_decimal ( self ) -> BigDecimal {
687
+ fn into_big_decimal ( self ) -> BigDecimal {
728
688
self
729
689
}
730
690
731
- fn to_decimal ( self ) -> Decimal {
691
+ fn into_decimal ( self ) -> Decimal {
732
692
let ( coeff, exponent) = self . into_bigint_and_exponent ( ) ;
733
693
let data = coeff. to_i128 ( ) . unwrap ( ) ;
734
694
Decimal :: new ( data, -exponent)
735
695
}
736
696
}
737
697
698
+ /// A helper trait to allow conversion of various Ion value types into f64. This is inherently a
699
+ /// lossy conversion for most possible expressible Decimal and Integer values even inside f64's
700
+ /// range of expression, so we accept that and move on. The only `None` case for any of these
701
+ /// conversions is when converting a non-numeric `Value` type. A large enough `Int` or `Decimal`
702
+ /// may convert to `Inf` as a float, but that's just the cost of doing business with floating
703
+ /// point math.
738
704
pub ( crate ) trait ToFloat {
739
705
fn to_f64 ( self ) -> Option < f64 > ;
740
706
}
@@ -747,16 +713,13 @@ pub(crate) mod ion_math {
747
713
748
714
impl ToFloat for Int {
749
715
fn to_f64 ( self ) -> Option < f64 > {
750
- self . as_i128 ( ) . and_then ( |data| {
751
- let float = data as f64 ;
752
- ( float as i128 == data) . then_some ( float)
753
- } )
716
+ self . as_i128 ( ) . map ( |data| data as f64 )
754
717
}
755
718
}
756
719
757
720
impl ToFloat for Decimal {
758
721
fn to_f64 ( self ) -> Option < f64 > {
759
- self . to_big_decimal ( ) . to_f64 ( )
722
+ self . into_big_decimal ( ) . to_f64 ( )
760
723
}
761
724
}
762
725
0 commit comments