@@ -383,7 +383,12 @@ private static SEXP applyBinaryPreservingInt(
383383 int len = Math .max (i1 .size (), i2 .size ());
384384 return SEXPs .integer (
385385 IntStream .range (0 , len )
386- .map (i -> (int ) fn .applyAsDouble (i1 .get (i % i1 .size ()), i2 .get (i % i2 .size ())))
386+ .map (
387+ i ->
388+ i1 .get (i % i1 .size ()) == Constants .NA_INT
389+ || i2 .get (i % i2 .size ()) == Constants .NA_INT
390+ ? Constants .NA_INT
391+ : (int ) fn .applyAsDouble (i1 .get (i % i1 .size ()), i2 .get (i % i2 .size ())))
387392 .toArray ());
388393 }
389394 case RealSXP r1 when s2 instanceof RealSXP r2 -> {
@@ -393,23 +398,94 @@ private static SEXP applyBinaryPreservingInt(
393398 .mapToDouble (i -> fn .applyAsDouble (r1 .get (i % r1 .size ()), r2 .get (i % r2 .size ())))
394399 .toArray ());
395400 }
401+ case LglSXP l1 when s2 instanceof LglSXP l2 -> {
402+ int len = Math .max (l1 .size (), l2 .size ());
403+ return SEXPs .integer (
404+ IntStream .range (0 , len )
405+ .map (
406+ i ->
407+ l1 .get (i % l1 .size ()) == Logical .NA || l2 .get (i % l2 .size ()) == Logical .NA
408+ ? Constants .NA_INT
409+ : (int )
410+ fn .applyAsDouble (
411+ l1 .get (i % l1 .size ()).toInt (), l2 .get (i % l2 .size ()).toInt ()))
412+ .toArray ());
413+ }
396414 case IntSXP i1 when s2 instanceof RealSXP r2 -> {
397415 int len = Math .max (i1 .size (), r2 .size ());
398416 return SEXPs .real (
399417 IntStream .range (0 , len )
400- .mapToDouble (i -> fn .applyAsDouble (i1 .get (i % i1 .size ()), r2 .get (i % r2 .size ())))
418+ .mapToDouble (
419+ i ->
420+ i1 .get (i % i1 .size ()) == Constants .NA_INT
421+ ? Double .NaN
422+ : fn .applyAsDouble (i1 .get (i % i1 .size ()), r2 .get (i % r2 .size ())))
423+ .toArray ());
424+ }
425+ case IntSXP i1 when s2 instanceof LglSXP l2 -> {
426+ int len = Math .max (i1 .size (), l2 .size ());
427+ return SEXPs .real (
428+ IntStream .range (0 , len )
429+ .mapToDouble (
430+ i ->
431+ i1 .get (i % i1 .size ()) == Constants .NA_INT
432+ || l2 .get (i % l2 .size ()) == Logical .NA
433+ ? Double .NaN
434+ : fn .applyAsDouble (
435+ i1 .get (i % i1 .size ()), l2 .get (i % l2 .size ()).toInt ()))
401436 .toArray ());
402437 }
403438 case RealSXP r1 when s2 instanceof IntSXP i2 -> {
404439 int len = Math .max (r1 .size (), i2 .size ());
405440 return SEXPs .real (
406441 IntStream .range (0 , len )
407- .mapToDouble (i -> fn .applyAsDouble (r1 .get (i % r1 .size ()), i2 .get (i % i2 .size ())))
442+ .mapToDouble (
443+ i ->
444+ i2 .get (i % i2 .size ()) == Constants .NA_INT
445+ ? Double .NaN
446+ : fn .applyAsDouble (r1 .get (i % r1 .size ()), i2 .get (i % i2 .size ())))
447+ .toArray ());
448+ }
449+ case RealSXP r1 when s2 instanceof LglSXP l2 -> {
450+ int len = Math .max (r1 .size (), l2 .size ());
451+ return SEXPs .real (
452+ IntStream .range (0 , len )
453+ .mapToDouble (
454+ i ->
455+ l2 .get (i % l2 .size ()) == Logical .NA
456+ ? Double .NaN
457+ : fn .applyAsDouble (
458+ r1 .get (i % r1 .size ()), l2 .get (i % l2 .size ()).toInt ()))
459+ .toArray ());
460+ }
461+ case LglSXP l1 when s2 instanceof IntSXP i2 -> {
462+ int len = Math .max (l1 .size (), i2 .size ());
463+ return SEXPs .real (
464+ IntStream .range (0 , len )
465+ .mapToDouble (
466+ i ->
467+ l1 .get (i % l1 .size ()) == Logical .NA
468+ || i2 .get (i % i2 .size ()) == Constants .NA_INT
469+ ? Double .NaN
470+ : fn .applyAsDouble (
471+ l1 .get (i % l1 .size ()).toInt (), i2 .get (i % i2 .size ())))
472+ .toArray ());
473+ }
474+ case LglSXP l1 when s2 instanceof RealSXP r2 -> {
475+ int len = Math .max (l1 .size (), r2 .size ());
476+ return SEXPs .real (
477+ IntStream .range (0 , len )
478+ .mapToDouble (
479+ i ->
480+ l1 .get (i % l1 .size ()) == Logical .NA || r2 .get (i % r2 .size ()).isNaN ()
481+ ? Double .NaN
482+ : fn .applyAsDouble (
483+ l1 .get (i % l1 .size ()).toInt (), r2 .get (i % r2 .size ())))
408484 .toArray ());
409485 }
410486 default -> {}
411487 }
412- throw interpreter .fail ("`" + ctx + "` generic requires numeric args" );
488+ throw interpreter .fail ("`" + ctx + "` generic requires logical or numeric args" );
413489 }
414490
415491 /// Apply unary math op on SEXP, preserving int type (for +, -)
@@ -422,14 +498,29 @@ private static SEXP applyUnaryPreservingInt(
422498 return SEXPs .real (fn .applyAsDouble (s .asScalarReal ().get ()));
423499 }
424500 // Vector operations
425- if (s instanceof IntSXP iv ) {
426- return SEXPs .integer (
427- IntStream .range (0 , iv .size ()).map (i -> (int ) fn .applyAsDouble (iv .get (i ))).toArray ());
428- } else if (s instanceof RealSXP rv ) {
429- return SEXPs .real (
430- IntStream .range (0 , rv .size ()).mapToDouble (i -> fn .applyAsDouble (rv .get (i ))).toArray ());
431- }
432- throw interpreter .fail ("`" + ctx + "` unary requires a numeric arg" );
501+ return switch (s ) {
502+ case IntSXP iv ->
503+ SEXPs .integer (
504+ IntStream .range (0 , iv .size ())
505+ .map (
506+ i ->
507+ iv .get (i ) == Constants .NA_INT
508+ ? Constants .NA_INT
509+ : (int ) fn .applyAsDouble (iv .get (i )))
510+ .toArray ());
511+ case RealSXP rv ->
512+ SEXPs .real (
513+ IntStream .range (0 , rv .size ())
514+ .mapToDouble (i -> fn .applyAsDouble (rv .get (i )))
515+ .toArray ());
516+ case LglSXP lv ->
517+ SEXPs .integer (
518+ IntStream .range (0 , lv .size ())
519+ .map (i -> lv .get (i ) == Logical .NA ? Constants .NA_INT : lv .get (i ).toInt ())
520+ .map (j -> (int ) fn .applyAsDouble (j ))
521+ .toArray ());
522+ default -> throw interpreter .fail ("`" + ctx + "` unary requires a logical or numeric arg" );
523+ };
433524 }
434525
435526 private static void registerBinaryMathToRealBuiltin (
@@ -2488,22 +2579,31 @@ private static Value sexpToValueOfType(
24882579 }
24892580
24902581 private static double sexpToDouble (SEXP sexp , InternalInterpreter interpreter , String ctx ) {
2491- if (sexp .asScalarInteger ().isPresent ()) return sexp .asScalarInteger ().get ();
2582+ if (sexp .asScalarInteger ().isPresent ())
2583+ return sexp .asScalarInteger ().get () == Constants .NA_INT
2584+ ? Double .NaN
2585+ : sexp .asScalarInteger ().get ();
24922586 if (sexp .asScalarReal ().isPresent ()) return sexp .asScalarReal ().get ();
24932587 if (sexp .asScalarLogical ().isPresent ()) return sexp .asScalarLogical ().get ().toInt ();
24942588 throw interpreter .fail (ctx + " requires a numeric scalar" );
24952589 }
24962590
24972591 private static @ Nullable Double sexpToDoubleOpt (SEXP sexp ) {
24982592 if (sexp .asScalarReal ().isPresent ()) return sexp .asScalarReal ().get ();
2499- if (sexp .asScalarInteger ().isPresent ()) return (double ) sexp .asScalarInteger ().get ();
2593+ if (sexp .asScalarInteger ().isPresent ())
2594+ return sexp .asScalarInteger ().get () == Constants .NA_INT
2595+ ? Double .NaN
2596+ : (double ) sexp .asScalarInteger ().get ();
25002597 if (sexp .asScalarLogical ().isPresent ()) return (double ) sexp .asScalarLogical ().get ().toInt ();
25012598 return null ;
25022599 }
25032600
25042601 private static int sexpToInt (SEXP sexp , InternalInterpreter interpreter , String ctx ) {
25052602 if (sexp .asScalarInteger ().isPresent ()) return sexp .asScalarInteger ().get ();
2506- if (sexp .asScalarReal ().isPresent ()) return (int ) sexp .asScalarReal ().get ().doubleValue ();
2603+ if (sexp .asScalarReal ().isPresent ())
2604+ return sexp .asScalarReal ().get ().isNaN ()
2605+ ? Constants .NA_INT
2606+ : (int ) sexp .asScalarReal ().get ().doubleValue ();
25072607 if (sexp .asScalarLogical ().isPresent ()) return sexp .asScalarLogical ().get ().toInt ();
25082608 throw interpreter .fail (ctx + " requires a numeric scalar" );
25092609 }
0 commit comments