Skip to main content

miniextendr_api/altrep_data/
builtins.rs

1//! Built-in ALTREP data implementations for standard Rust types.
2//!
3//! Provides `AltIntegerData`, `AltRealData`, `AltLogicalData`, `AltRawData`,
4//! `AltStringData`, and `AltComplexData` implementations for:
5//!
6//! - `Vec<T>` — owned vector (most common)
7//! - `Box<[T]>` — boxed slice
8//! - `Range<T>` — lazy arithmetic sequences (integer, real)
9//! - `&'static [T]` — borrowed slices (zero-copy from static data)
10//! - `[T; N]` — fixed-size arrays
11
12use std::borrow::Cow;
13use std::ops::Range;
14
15use crate::altrep_traits::NA_REAL;
16use crate::ffi::{Rcomplex, SEXP};
17
18use super::{
19    AltComplexData, AltIntegerData, AltLogicalData, AltRawData, AltRealData, AltStringData,
20    AltrepDataptr, AltrepLen, AltrepSerialize, Logical, Sortedness,
21};
22
23// region: Helper macros to reduce repetition
24
25/// Implement AltrepLen for Vec<$elem>
26macro_rules! impl_len_vec {
27    ($elem:ty) => {
28        impl AltrepLen for Vec<$elem> {
29            fn len(&self) -> usize {
30                Vec::len(self)
31            }
32        }
33    };
34}
35
36/// Implement AltrepLen for Box<[$elem]>
37macro_rules! impl_len_boxed {
38    ($elem:ty) => {
39        impl AltrepLen for Box<[$elem]> {
40            fn len(&self) -> usize {
41                <[$elem]>::len(self)
42            }
43        }
44    };
45}
46
47/// Implement AltrepLen for [$elem; N]
48macro_rules! impl_len_array {
49    ($elem:ty) => {
50        impl<const N: usize> AltrepLen for [$elem; N] {
51            fn len(&self) -> usize {
52                N
53            }
54        }
55    };
56}
57
58/// Implement AltrepLen for &[$elem]
59macro_rules! impl_len_slice {
60    ($elem:ty) => {
61        impl AltrepLen for &[$elem] {
62            fn len(&self) -> usize {
63                <[$elem]>::len(self)
64            }
65        }
66    };
67}
68
69/// Implement AltrepDataptr for Vec<$elem> (types with direct memory access)
70macro_rules! impl_dataptr_vec {
71    ($elem:ty) => {
72        impl AltrepDataptr<$elem> for Vec<$elem> {
73            fn dataptr(&mut self, _writable: bool) -> Option<*mut $elem> {
74                Some(self.as_mut_ptr())
75            }
76
77            fn dataptr_or_null(&self) -> Option<*const $elem> {
78                Some(self.as_ptr())
79            }
80        }
81    };
82}
83
84/// Implement AltrepDataptr for Box<[$elem]> (types with direct memory access)
85macro_rules! impl_dataptr_boxed {
86    ($elem:ty) => {
87        impl AltrepDataptr<$elem> for Box<[$elem]> {
88            fn dataptr(&mut self, _writable: bool) -> Option<*mut $elem> {
89                Some(self.as_mut_ptr())
90            }
91
92            fn dataptr_or_null(&self) -> Option<*const $elem> {
93                Some(self.as_ptr())
94            }
95        }
96    };
97}
98
99/// Implement AltrepSerialize for types that can be cloned and converted to/from R.
100///
101/// This serializes by converting to a native R vector, ensuring the data survives
102/// even if the Rust package isn't loaded when unserializing.
103macro_rules! impl_serialize {
104    ($ty:ty) => {
105        impl AltrepSerialize for $ty {
106            fn serialized_state(&self) -> SEXP {
107                use crate::into_r::IntoR;
108                self.clone().into_sexp()
109            }
110
111            fn unserialize(state: SEXP) -> Option<Self> {
112                use crate::from_r::TryFromSexp;
113                <$ty>::try_from_sexp(state).ok()
114            }
115        }
116    };
117}
118// endregion
119
120// region: AltrepSerialize implementations for Vec<T>
121
122impl_serialize!(Vec<i32>);
123impl_serialize!(Vec<f64>);
124impl_serialize!(Vec<u8>);
125impl_serialize!(Vec<bool>);
126impl_serialize!(Vec<String>);
127impl_serialize!(Vec<Option<String>>);
128impl_serialize!(Vec<Rcomplex>);
129// Cow string vectors: serialize hits R's CHARSXP cache (no string data copy),
130// unserialize borrows via charsxp_to_cow (zero-copy for UTF-8).
131impl_serialize!(Vec<std::borrow::Cow<'static, str>>);
132impl_serialize!(Vec<Option<std::borrow::Cow<'static, str>>>);
133// endregion
134
135// region: AltrepSerialize implementations for Box<[T]>
136
137// Box<[T]> types don't have direct TryFromSexp implementations, so we manually
138// implement serialization by converting to Vec and back.
139
140impl AltrepSerialize for Box<[i32]> {
141    fn serialized_state(&self) -> SEXP {
142        use crate::into_r::IntoR;
143        self.to_vec().into_sexp()
144    }
145
146    fn unserialize(state: SEXP) -> Option<Self> {
147        use crate::from_r::TryFromSexp;
148        Vec::<i32>::try_from_sexp(state)
149            .ok()
150            .map(|v| v.into_boxed_slice())
151    }
152}
153
154impl AltrepSerialize for Box<[f64]> {
155    fn serialized_state(&self) -> SEXP {
156        use crate::into_r::IntoR;
157        self.to_vec().into_sexp()
158    }
159
160    fn unserialize(state: SEXP) -> Option<Self> {
161        use crate::from_r::TryFromSexp;
162        Vec::<f64>::try_from_sexp(state)
163            .ok()
164            .map(|v| v.into_boxed_slice())
165    }
166}
167
168impl AltrepSerialize for Box<[u8]> {
169    fn serialized_state(&self) -> SEXP {
170        use crate::into_r::IntoR;
171        self.to_vec().into_sexp()
172    }
173
174    fn unserialize(state: SEXP) -> Option<Self> {
175        use crate::from_r::TryFromSexp;
176        Vec::<u8>::try_from_sexp(state)
177            .ok()
178            .map(|v| v.into_boxed_slice())
179    }
180}
181
182impl AltrepSerialize for Box<[bool]> {
183    fn serialized_state(&self) -> SEXP {
184        use crate::into_r::IntoR;
185        self.to_vec().into_sexp()
186    }
187
188    fn unserialize(state: SEXP) -> Option<Self> {
189        use crate::from_r::TryFromSexp;
190        Vec::<bool>::try_from_sexp(state)
191            .ok()
192            .map(|v| v.into_boxed_slice())
193    }
194}
195
196impl AltrepSerialize for Box<[String]> {
197    fn serialized_state(&self) -> SEXP {
198        use crate::into_r::IntoR;
199        self.to_vec().into_sexp()
200    }
201
202    fn unserialize(state: SEXP) -> Option<Self> {
203        use crate::from_r::TryFromSexp;
204        Vec::<String>::try_from_sexp(state)
205            .ok()
206            .map(|v| v.into_boxed_slice())
207    }
208}
209
210impl AltrepSerialize for Box<[Rcomplex]> {
211    fn serialized_state(&self) -> SEXP {
212        use crate::into_r::IntoR;
213        self.to_vec().into_sexp()
214    }
215
216    fn unserialize(state: SEXP) -> Option<Self> {
217        use crate::from_r::TryFromSexp;
218        Vec::<Rcomplex>::try_from_sexp(state)
219            .ok()
220            .map(|v| v.into_boxed_slice())
221    }
222}
223// endregion
224
225// region: Built-in implementations for Vec<T>
226
227impl_len_vec!(i32);
228
229impl AltIntegerData for Vec<i32> {
230    fn elt(&self, i: usize) -> i32 {
231        self[i]
232    }
233
234    fn as_slice(&self) -> Option<&[i32]> {
235        Some(self.as_slice())
236    }
237
238    fn get_region(&self, start: usize, len: usize, buf: &mut [i32]) -> usize {
239        let end = (start + len).min(self.len());
240        let actual_len = end.saturating_sub(start);
241        if actual_len > 0 {
242            buf[..actual_len].copy_from_slice(&self[start..end]);
243        }
244        actual_len
245    }
246
247    fn no_na(&self) -> Option<bool> {
248        Some(!self.contains(&i32::MIN))
249    }
250
251    fn sum(&self, na_rm: bool) -> Option<i64> {
252        let mut sum: i64 = 0;
253        for &x in self.iter() {
254            if x == i32::MIN {
255                if !na_rm {
256                    return None; // NA propagates
257                }
258            } else {
259                sum += x as i64;
260            }
261        }
262        Some(sum)
263    }
264
265    fn min(&self, na_rm: bool) -> Option<i32> {
266        let mut min = i32::MAX;
267        let mut found = false;
268        for &x in self.iter() {
269            if x == i32::MIN {
270                if !na_rm {
271                    return None;
272                }
273            } else {
274                found = true;
275                min = min.min(x);
276            }
277        }
278        if found { Some(min) } else { None }
279    }
280
281    fn max(&self, na_rm: bool) -> Option<i32> {
282        let mut max = i32::MIN + 1; // Avoid NA sentinel
283        let mut found = false;
284        for &x in self.iter() {
285            if x == i32::MIN {
286                if !na_rm {
287                    return None;
288                }
289            } else {
290                found = true;
291                max = max.max(x);
292            }
293        }
294        if found { Some(max) } else { None }
295    }
296}
297
298impl_dataptr_vec!(i32);
299
300impl_len_vec!(f64);
301
302impl AltRealData for Vec<f64> {
303    fn elt(&self, i: usize) -> f64 {
304        self[i]
305    }
306
307    fn as_slice(&self) -> Option<&[f64]> {
308        Some(self.as_slice())
309    }
310
311    fn get_region(&self, start: usize, len: usize, buf: &mut [f64]) -> usize {
312        let end = (start + len).min(self.len());
313        let actual_len = end.saturating_sub(start);
314        if actual_len > 0 {
315            buf[..actual_len].copy_from_slice(&self[start..end]);
316        }
317        actual_len
318    }
319
320    fn no_na(&self) -> Option<bool> {
321        // NA_real_ is a specific NaN bit pattern; regular NaN is not NA in R.
322        Some(!self.iter().any(|x| x.to_bits() == NA_REAL.to_bits()))
323    }
324
325    fn sum(&self, na_rm: bool) -> Option<f64> {
326        let mut sum = 0.0;
327        for &x in self.iter() {
328            if x.to_bits() == NA_REAL.to_bits() {
329                // R's NA_real_: propagates as NA unless removed.
330                if !na_rm {
331                    return Some(NA_REAL);
332                }
333            } else if x.is_nan() {
334                // Regular IEEE NaN: propagates as NaN unless removed.
335                if !na_rm {
336                    return Some(f64::NAN);
337                }
338            } else {
339                sum += x;
340            }
341        }
342        Some(sum)
343    }
344
345    fn min(&self, na_rm: bool) -> Option<f64> {
346        let mut min = f64::INFINITY;
347        let mut found = false;
348        for &x in self.iter() {
349            if x.to_bits() == NA_REAL.to_bits() {
350                if !na_rm {
351                    return Some(NA_REAL);
352                }
353            } else if x.is_nan() {
354                if !na_rm {
355                    return Some(f64::NAN);
356                }
357            } else {
358                found = true;
359                min = min.min(x);
360            }
361        }
362        if found { Some(min) } else { None }
363    }
364
365    fn max(&self, na_rm: bool) -> Option<f64> {
366        let mut max = f64::NEG_INFINITY;
367        let mut found = false;
368        for &x in self.iter() {
369            if x.to_bits() == NA_REAL.to_bits() {
370                if !na_rm {
371                    return Some(NA_REAL);
372                }
373            } else if x.is_nan() {
374                if !na_rm {
375                    return Some(f64::NAN);
376                }
377            } else {
378                found = true;
379                max = max.max(x);
380            }
381        }
382        if found { Some(max) } else { None }
383    }
384}
385
386impl_dataptr_vec!(f64);
387
388impl_len_vec!(u8);
389
390impl AltRawData for Vec<u8> {
391    fn elt(&self, i: usize) -> u8 {
392        self[i]
393    }
394
395    fn as_slice(&self) -> Option<&[u8]> {
396        Some(self.as_slice())
397    }
398
399    fn get_region(&self, start: usize, len: usize, buf: &mut [u8]) -> usize {
400        let end = (start + len).min(self.len());
401        let actual_len = end.saturating_sub(start);
402        if actual_len > 0 {
403            buf[..actual_len].copy_from_slice(&self[start..end]);
404        }
405        actual_len
406    }
407}
408
409impl_dataptr_vec!(u8);
410
411impl_len_vec!(String);
412
413impl AltStringData for Vec<String> {
414    fn elt(&self, i: usize) -> Option<&str> {
415        Some(self[i].as_str())
416    }
417
418    fn no_na(&self) -> Option<bool> {
419        Some(true) // String vectors don't have NA
420    }
421}
422
423impl_len_vec!(Option<String>);
424
425impl AltStringData for Vec<Option<String>> {
426    fn elt(&self, i: usize) -> Option<&str> {
427        self[i].as_deref()
428    }
429
430    fn no_na(&self) -> Option<bool> {
431        Some(!self.iter().any(|x| x.is_none()))
432    }
433}
434
435impl AltrepLen for Vec<std::borrow::Cow<'static, str>> {
436    fn len(&self) -> usize {
437        self.len()
438    }
439}
440
441impl AltStringData for Vec<std::borrow::Cow<'static, str>> {
442    fn elt(&self, i: usize) -> Option<&str> {
443        Some(self[i].as_ref())
444    }
445
446    fn no_na(&self) -> Option<bool> {
447        Some(true) // Cow vectors don't have NA
448    }
449}
450
451impl AltrepLen for Vec<Option<std::borrow::Cow<'static, str>>> {
452    fn len(&self) -> usize {
453        self.len()
454    }
455}
456
457impl AltStringData for Vec<Option<std::borrow::Cow<'static, str>>> {
458    fn elt(&self, i: usize) -> Option<&str> {
459        self[i].as_deref()
460    }
461
462    fn no_na(&self) -> Option<bool> {
463        Some(!self.iter().any(|x| x.is_none()))
464    }
465}
466
467impl_len_vec!(bool);
468
469impl AltLogicalData for Vec<bool> {
470    fn elt(&self, i: usize) -> Logical {
471        if self[i] {
472            Logical::True
473        } else {
474            Logical::False
475        }
476    }
477
478    fn no_na(&self) -> Option<bool> {
479        Some(true) // bool can't be NA
480    }
481
482    fn sum(&self, _na_rm: bool) -> Option<i64> {
483        Some(self.iter().filter(|&&x| x).count() as i64)
484    }
485}
486// endregion
487
488// region: Built-in implementations for Box<[T]> (owned slices)
489// Box<[T]> is a fat pointer (Sized) that wraps a DST slice.
490// Unlike Vec<T>, it has no capacity field - just ptr + len (2 words).
491// This makes it more memory-efficient for fixed-size data.
492//
493// Box<[T]> CAN be used directly with ALTREP via the proc-macro:
494// ```
495// #[miniextendr(class = "BoxedInts")]
496// pub struct BoxedIntsClass(Box<[i32]>);
497// ```
498//
499// Or use these trait implementations in custom wrapper structs.
500
501impl_len_boxed!(i32);
502
503impl AltIntegerData for Box<[i32]> {
504    fn elt(&self, i: usize) -> i32 {
505        self[i]
506    }
507
508    fn as_slice(&self) -> Option<&[i32]> {
509        Some(self)
510    }
511
512    fn get_region(&self, start: usize, len: usize, buf: &mut [i32]) -> usize {
513        let end = (start + len).min(<[i32]>::len(self));
514        let actual_len = end.saturating_sub(start);
515        if actual_len > 0 {
516            buf[..actual_len].copy_from_slice(&self[start..end]);
517        }
518        actual_len
519    }
520
521    fn no_na(&self) -> Option<bool> {
522        Some(!self.contains(&i32::MIN))
523    }
524
525    fn sum(&self, na_rm: bool) -> Option<i64> {
526        let mut sum: i64 = 0;
527        for &x in self.iter() {
528            if x == i32::MIN {
529                if !na_rm {
530                    return None;
531                }
532            } else {
533                sum += x as i64;
534            }
535        }
536        Some(sum)
537    }
538
539    fn min(&self, na_rm: bool) -> Option<i32> {
540        let mut min = i32::MAX;
541        let mut found = false;
542        for &x in self.iter() {
543            if x == i32::MIN {
544                if !na_rm {
545                    return None;
546                }
547            } else {
548                found = true;
549                min = min.min(x);
550            }
551        }
552        if found { Some(min) } else { None }
553    }
554
555    fn max(&self, na_rm: bool) -> Option<i32> {
556        let mut max = i32::MIN + 1; // i32::MIN is NA
557        let mut found = false;
558        for &x in self.iter() {
559            if x == i32::MIN {
560                if !na_rm {
561                    return None;
562                }
563            } else {
564                found = true;
565                max = max.max(x);
566            }
567        }
568        if found { Some(max) } else { None }
569    }
570}
571
572impl_dataptr_boxed!(i32);
573
574impl_len_boxed!(f64);
575
576impl AltRealData for Box<[f64]> {
577    fn elt(&self, i: usize) -> f64 {
578        self[i]
579    }
580
581    fn as_slice(&self) -> Option<&[f64]> {
582        Some(self)
583    }
584
585    fn get_region(&self, start: usize, len: usize, buf: &mut [f64]) -> usize {
586        let end = (start + len).min(<[f64]>::len(self));
587        let actual_len = end.saturating_sub(start);
588        if actual_len > 0 {
589            buf[..actual_len].copy_from_slice(&self[start..end]);
590        }
591        actual_len
592    }
593
594    fn no_na(&self) -> Option<bool> {
595        Some(!self.iter().any(|x| x.to_bits() == NA_REAL.to_bits()))
596    }
597
598    fn sum(&self, na_rm: bool) -> Option<f64> {
599        let mut sum = 0.0;
600        for &x in self.iter() {
601            if x.to_bits() == NA_REAL.to_bits() {
602                if !na_rm {
603                    return Some(NA_REAL);
604                }
605            } else if x.is_nan() {
606                if !na_rm {
607                    return Some(f64::NAN);
608                }
609            } else {
610                sum += x;
611            }
612        }
613        Some(sum)
614    }
615
616    fn min(&self, na_rm: bool) -> Option<f64> {
617        let mut min = f64::INFINITY;
618        let mut found = false;
619        for &x in self.iter() {
620            if x.to_bits() == NA_REAL.to_bits() {
621                if !na_rm {
622                    return Some(NA_REAL);
623                }
624            } else if x.is_nan() {
625                if !na_rm {
626                    return Some(f64::NAN);
627                }
628            } else {
629                found = true;
630                min = min.min(x);
631            }
632        }
633        if found { Some(min) } else { None }
634    }
635
636    fn max(&self, na_rm: bool) -> Option<f64> {
637        let mut max = f64::NEG_INFINITY;
638        let mut found = false;
639        for &x in self.iter() {
640            if x.to_bits() == NA_REAL.to_bits() {
641                if !na_rm {
642                    return Some(NA_REAL);
643                }
644            } else if x.is_nan() {
645                if !na_rm {
646                    return Some(f64::NAN);
647                }
648            } else {
649                found = true;
650                max = max.max(x);
651            }
652        }
653        if found { Some(max) } else { None }
654    }
655}
656
657impl_dataptr_boxed!(f64);
658
659impl_len_boxed!(u8);
660
661impl AltRawData for Box<[u8]> {
662    fn elt(&self, i: usize) -> u8 {
663        self[i]
664    }
665
666    fn as_slice(&self) -> Option<&[u8]> {
667        Some(self)
668    }
669
670    fn get_region(&self, start: usize, len: usize, buf: &mut [u8]) -> usize {
671        let end = (start + len).min(<[u8]>::len(self));
672        let actual_len = end.saturating_sub(start);
673        if actual_len > 0 {
674            buf[..actual_len].copy_from_slice(&self[start..end]);
675        }
676        actual_len
677    }
678}
679
680impl_dataptr_boxed!(u8);
681
682impl_len_boxed!(bool);
683
684impl AltLogicalData for Box<[bool]> {
685    fn elt(&self, i: usize) -> Logical {
686        if self[i] {
687            Logical::True
688        } else {
689            Logical::False
690        }
691    }
692
693    fn no_na(&self) -> Option<bool> {
694        Some(true) // bool can't be NA
695    }
696
697    fn sum(&self, _na_rm: bool) -> Option<i64> {
698        Some(self.iter().filter(|&&x| x).count() as i64)
699    }
700}
701
702impl_len_boxed!(String);
703
704impl AltStringData for Box<[String]> {
705    fn elt(&self, i: usize) -> Option<&str> {
706        Some(self[i].as_str())
707    }
708
709    fn no_na(&self) -> Option<bool> {
710        Some(true) // String can't be NA
711    }
712}
713// endregion
714
715// region: AltrepSerialize implementations for Range types
716// Ranges serialize to a 2-element integer/real vector [start, end].
717
718impl AltrepSerialize for Range<i32> {
719    fn serialized_state(&self) -> SEXP {
720        use crate::into_r::IntoR;
721        vec![self.start, self.end].into_sexp()
722    }
723
724    fn unserialize(state: SEXP) -> Option<Self> {
725        use crate::from_r::TryFromSexp;
726        let v = Vec::<i32>::try_from_sexp(state).ok()?;
727        if v.len() == 2 { Some(v[0]..v[1]) } else { None }
728    }
729}
730
731impl AltrepSerialize for Range<i64> {
732    fn serialized_state(&self) -> SEXP {
733        use crate::into_r::IntoR;
734        // Store i64 bit patterns in f64 slots for lossless round-trip.
735        // Plain `as f64` loses precision for values > 2^53.
736        vec![
737            f64::from_bits(self.start as u64),
738            f64::from_bits(self.end as u64),
739        ]
740        .into_sexp()
741    }
742
743    fn unserialize(state: SEXP) -> Option<Self> {
744        use crate::from_r::TryFromSexp;
745        let v = Vec::<f64>::try_from_sexp(state).ok()?;
746        if v.len() == 2 {
747            Some((v[0].to_bits() as i64)..(v[1].to_bits() as i64))
748        } else {
749            None
750        }
751    }
752}
753
754impl AltrepSerialize for Range<f64> {
755    fn serialized_state(&self) -> SEXP {
756        use crate::into_r::IntoR;
757        vec![self.start, self.end].into_sexp()
758    }
759
760    fn unserialize(state: SEXP) -> Option<Self> {
761        use crate::from_r::TryFromSexp;
762        let v = Vec::<f64>::try_from_sexp(state).ok()?;
763        if v.len() == 2 { Some(v[0]..v[1]) } else { None }
764    }
765}
766// endregion
767
768// region: Built-in implementations for Range types
769
770impl AltrepLen for Range<i32> {
771    fn len(&self) -> usize {
772        if self.end > self.start {
773            (self.end - self.start) as usize
774        } else {
775            0
776        }
777    }
778}
779
780impl AltIntegerData for Range<i32> {
781    fn elt(&self, i: usize) -> i32 {
782        self.start + i as i32
783    }
784
785    fn is_sorted(&self) -> Option<Sortedness> {
786        Some(Sortedness::Increasing)
787    }
788
789    fn no_na(&self) -> Option<bool> {
790        // i32::MIN is NA_INTEGER in R. Check if the range contains it.
791        // Range is [start, end), so i32::MIN is included iff start <= i32::MIN < end
792        // Since start is the smallest value, we just check if start == i32::MIN
793        let contains_na = self.start == i32::MIN && self.end > i32::MIN;
794        Some(!contains_na)
795    }
796
797    fn sum(&self, na_rm: bool) -> Option<i64> {
798        let n = AltrepLen::len(self) as i64;
799        if n == 0 {
800            return Some(0);
801        }
802
803        // Check if range contains NA (i32::MIN)
804        let contains_na = self.start == i32::MIN && self.end > i32::MIN;
805        if contains_na && !na_rm {
806            return None; // NA propagates
807        }
808
809        // Sum of arithmetic sequence: n/2 * (first + last)
810        // If na_rm and contains NA, exclude the first element (i32::MIN)
811        if contains_na {
812            // Exclude first element (NA), sum from start+1 to end-1
813            let n_valid = n - 1;
814            if n_valid == 0 {
815                return Some(0);
816            }
817            let first = (self.start + 1) as i64;
818            let last = (self.end - 1) as i64;
819            Some(n_valid * (first + last) / 2)
820        } else {
821            let first = self.start as i64;
822            let last = (self.end - 1) as i64;
823            Some(n * (first + last) / 2)
824        }
825    }
826
827    fn min(&self, na_rm: bool) -> Option<i32> {
828        if AltrepLen::len(self) == 0 {
829            return None;
830        }
831
832        // Check if first element is NA
833        if self.start == i32::MIN {
834            if na_rm {
835                // Skip NA, return second element if it exists
836                if self.end > self.start + 1 {
837                    Some(self.start + 1)
838                } else {
839                    None // Only element was NA
840                }
841            } else {
842                None // NA propagates
843            }
844        } else {
845            Some(self.start)
846        }
847    }
848
849    fn max(&self, na_rm: bool) -> Option<i32> {
850        if AltrepLen::len(self) == 0 {
851            return None;
852        }
853
854        // For increasing range, max is end-1 (last element)
855        // Check if range contains NA (first element)
856        let contains_na = self.start == i32::MIN && self.end > i32::MIN;
857        if contains_na && !na_rm {
858            return None; // NA propagates
859        }
860
861        // Max is always end-1 (last element), which is not NA
862        // (NA would only be first element if start == i32::MIN)
863        Some(self.end - 1)
864    }
865}
866
867impl AltrepLen for Range<i64> {
868    fn len(&self) -> usize {
869        if self.end > self.start {
870            (self.end - self.start) as usize
871        } else {
872            0
873        }
874    }
875}
876
877impl AltIntegerData for Range<i64> {
878    fn elt(&self, i: usize) -> i32 {
879        let val = self.start.saturating_add(i as i64);
880        // Bounds check: return NA_INTEGER for values outside i32 range
881        // Also, i32::MIN is the NA sentinel, so values equal to it are NA
882        if val > i32::MAX as i64 || val <= i32::MIN as i64 {
883            crate::altrep_traits::NA_INTEGER
884        } else {
885            val as i32
886        }
887    }
888
889    fn is_sorted(&self) -> Option<Sortedness> {
890        Some(Sortedness::Increasing)
891    }
892
893    fn no_na(&self) -> Option<bool> {
894        // An element is NA if:
895        // 1. It's outside valid i32 range (< i32::MIN or > i32::MAX)
896        // 2. It equals i32::MIN (NA sentinel)
897        //
898        // For increasing range [start, end), elements range from start to end-1.
899        // Range contains NA if:
900        // - start <= i32::MIN as i64 (NA sentinel could be in range)
901        // - OR end > i32::MAX as i64 + 1 (values exceed i32::MAX)
902        // - OR start < i32::MIN as i64 (values below i32 range)
903        let na_sentinel = i32::MIN as i64;
904        let i32_max = i32::MAX as i64;
905
906        // Check if NA sentinel is in [start, end)
907        let contains_na_sentinel = self.start <= na_sentinel && self.end > na_sentinel;
908
909        // Check if any values are outside valid i32 range
910        // Valid range for ALTREP integers: (i32::MIN, i32::MAX] (excluding NA sentinel)
911        let has_underflow = self.start < na_sentinel;
912        let has_overflow = (self.end - 1) > i32_max;
913
914        Some(!contains_na_sentinel && !has_underflow && !has_overflow)
915    }
916
917    fn sum(&self, na_rm: bool) -> Option<i64> {
918        let n = AltrepLen::len(self) as i64;
919        if n == 0 {
920            return Some(0);
921        }
922
923        // Check if range contains any NA values
924        let na_sentinel = i32::MIN as i64;
925        let i32_max = i32::MAX as i64;
926        let contains_na_sentinel = self.start <= na_sentinel && self.end > na_sentinel;
927        let has_underflow = self.start < na_sentinel;
928        let has_overflow = (self.end - 1) > i32_max;
929        let has_na = contains_na_sentinel || has_underflow || has_overflow;
930
931        if has_na && !na_rm {
932            return None; // NA propagates
933        }
934
935        if has_na {
936            // When na_rm=true, we need to exclude NA values
937            // This is complex for ranges with out-of-bounds values, so let R compute
938            return None;
939        }
940
941        let first = self.start;
942        let last = self.end - 1;
943
944        // Use checked arithmetic to detect overflow
945        // Formula: n * (first + last) / 2
946        let sum_endpoints = first.checked_add(last)?;
947        let product = n.checked_mul(sum_endpoints)?;
948        Some(product / 2)
949    }
950
951    fn min(&self, na_rm: bool) -> Option<i32> {
952        if AltrepLen::len(self) == 0 {
953            return None;
954        }
955
956        let na_sentinel = i32::MIN as i64;
957        let i32_max = i32::MAX as i64;
958
959        // Check for NA conditions
960        let contains_na_sentinel = self.start <= na_sentinel && self.end > na_sentinel;
961        let has_underflow = self.start < na_sentinel;
962        let has_overflow = (self.end - 1) > i32_max;
963        let has_na = contains_na_sentinel || has_underflow || has_overflow;
964
965        if has_na && !na_rm {
966            return None; // NA propagates
967        }
968
969        if has_na {
970            // Complex case: need to find first non-NA value
971            // Let R compute this
972            return None;
973        }
974
975        // No NA, return start (which is within valid i32 range)
976        Some(self.start as i32)
977    }
978
979    fn max(&self, na_rm: bool) -> Option<i32> {
980        if AltrepLen::len(self) == 0 {
981            return None;
982        }
983
984        let na_sentinel = i32::MIN as i64;
985        let i32_max = i32::MAX as i64;
986
987        // Check for NA conditions
988        let contains_na_sentinel = self.start <= na_sentinel && self.end > na_sentinel;
989        let has_underflow = self.start < na_sentinel;
990        let has_overflow = (self.end - 1) > i32_max;
991        let has_na = contains_na_sentinel || has_underflow || has_overflow;
992
993        if has_na && !na_rm {
994            return None; // NA propagates
995        }
996
997        if has_na {
998            // Complex case: need to find last non-NA value
999            // Let R compute this
1000            return None;
1001        }
1002
1003        // No NA, return end-1 (which is within valid i32 range)
1004        Some((self.end - 1) as i32)
1005    }
1006}
1007
1008impl AltrepLen for Range<f64> {
1009    fn len(&self) -> usize {
1010        // For f64 ranges, assume step of 1.0
1011        if self.end > self.start {
1012            (self.end - self.start).ceil() as usize
1013        } else {
1014            0
1015        }
1016    }
1017}
1018
1019impl AltRealData for Range<f64> {
1020    fn elt(&self, i: usize) -> f64 {
1021        self.start + i as f64
1022    }
1023
1024    fn is_sorted(&self) -> Option<Sortedness> {
1025        Some(Sortedness::Increasing)
1026    }
1027
1028    fn no_na(&self) -> Option<bool> {
1029        Some(true)
1030    }
1031
1032    fn sum(&self, _na_rm: bool) -> Option<f64> {
1033        let n = AltrepLen::len(self) as f64;
1034        if n == 0.0 {
1035            return Some(0.0);
1036        }
1037        let first = self.start;
1038        let last = self.start + (n - 1.0);
1039        Some(n * (first + last) / 2.0)
1040    }
1041
1042    fn min(&self, _na_rm: bool) -> Option<f64> {
1043        if AltrepLen::len(self) > 0 {
1044            Some(self.start)
1045        } else {
1046            None
1047        }
1048    }
1049
1050    fn max(&self, _na_rm: bool) -> Option<f64> {
1051        if AltrepLen::len(self) > 0 {
1052            Some(self.start + (AltrepLen::len(self) - 1) as f64)
1053        } else {
1054            None
1055        }
1056    }
1057}
1058// endregion
1059
1060// region: Built-in implementations for slices (read-only)
1061
1062impl_len_slice!(i32);
1063
1064impl AltIntegerData for &[i32] {
1065    fn elt(&self, i: usize) -> i32 {
1066        self[i]
1067    }
1068
1069    fn as_slice(&self) -> Option<&[i32]> {
1070        Some(self)
1071    }
1072
1073    fn get_region(&self, start: usize, len: usize, buf: &mut [i32]) -> usize {
1074        let end = (start + len).min(<[i32]>::len(self));
1075        let actual_len = end.saturating_sub(start);
1076        if actual_len > 0 {
1077            buf[..actual_len].copy_from_slice(&self[start..end]);
1078        }
1079        actual_len
1080    }
1081
1082    fn no_na(&self) -> Option<bool> {
1083        // i32 slices have NA as i32::MIN
1084        Some(!self.contains(&i32::MIN))
1085    }
1086
1087    fn sum(&self, _na_rm: bool) -> Option<i64> {
1088        // Check for NA (i32::MIN)
1089        if self.contains(&i32::MIN) {
1090            if _na_rm {
1091                Some(
1092                    self.iter()
1093                        .filter(|&&x| x != i32::MIN)
1094                        .map(|&x| x as i64)
1095                        .sum(),
1096                )
1097            } else {
1098                None // Return NA
1099            }
1100        } else {
1101            Some(self.iter().map(|&x| x as i64).sum())
1102        }
1103    }
1104
1105    fn min(&self, _na_rm: bool) -> Option<i32> {
1106        if self.is_empty() {
1107            return None;
1108        }
1109        if _na_rm {
1110            self.iter().filter(|&&x| x != i32::MIN).copied().min()
1111        } else if self.contains(&i32::MIN) {
1112            None // NA present
1113        } else {
1114            self.iter().copied().min()
1115        }
1116    }
1117
1118    fn max(&self, _na_rm: bool) -> Option<i32> {
1119        if self.is_empty() {
1120            return None;
1121        }
1122        if _na_rm {
1123            self.iter().filter(|&&x| x != i32::MIN).copied().max()
1124        } else if self.contains(&i32::MIN) {
1125            None // NA present
1126        } else {
1127            self.iter().copied().max()
1128        }
1129    }
1130}
1131
1132impl_len_slice!(f64);
1133
1134impl AltRealData for &[f64] {
1135    fn elt(&self, i: usize) -> f64 {
1136        self[i]
1137    }
1138
1139    fn as_slice(&self) -> Option<&[f64]> {
1140        Some(self)
1141    }
1142
1143    fn no_na(&self) -> Option<bool> {
1144        Some(!self.iter().any(|x| x.to_bits() == NA_REAL.to_bits()))
1145    }
1146
1147    fn sum(&self, na_rm: bool) -> Option<f64> {
1148        let mut sum = 0.0;
1149        for &x in self.iter() {
1150            if x.to_bits() == NA_REAL.to_bits() {
1151                if !na_rm {
1152                    return Some(NA_REAL);
1153                }
1154            } else if x.is_nan() {
1155                if !na_rm {
1156                    return Some(f64::NAN);
1157                }
1158            } else {
1159                sum += x;
1160            }
1161        }
1162        Some(sum)
1163    }
1164
1165    fn min(&self, na_rm: bool) -> Option<f64> {
1166        let mut min = f64::INFINITY;
1167        let mut found = false;
1168        for &x in self.iter() {
1169            if x.to_bits() == NA_REAL.to_bits() {
1170                if !na_rm {
1171                    return Some(NA_REAL);
1172                }
1173            } else if x.is_nan() {
1174                if !na_rm {
1175                    return Some(f64::NAN);
1176                }
1177            } else {
1178                found = true;
1179                min = min.min(x);
1180            }
1181        }
1182        if found { Some(min) } else { None }
1183    }
1184
1185    fn max(&self, na_rm: bool) -> Option<f64> {
1186        let mut max = f64::NEG_INFINITY;
1187        let mut found = false;
1188        for &x in self.iter() {
1189            if x.to_bits() == NA_REAL.to_bits() {
1190                if !na_rm {
1191                    return Some(NA_REAL);
1192                }
1193            } else if x.is_nan() {
1194                if !na_rm {
1195                    return Some(f64::NAN);
1196                }
1197            } else {
1198                found = true;
1199                max = max.max(x);
1200            }
1201        }
1202        if found { Some(max) } else { None }
1203    }
1204}
1205
1206impl_len_slice!(u8);
1207
1208impl AltRawData for &[u8] {
1209    fn elt(&self, i: usize) -> u8 {
1210        self[i]
1211    }
1212
1213    fn as_slice(&self) -> Option<&[u8]> {
1214        Some(self)
1215    }
1216}
1217
1218impl_len_slice!(bool);
1219
1220impl AltLogicalData for &[bool] {
1221    fn elt(&self, i: usize) -> Logical {
1222        Logical::from_bool(self[i])
1223    }
1224
1225    fn no_na(&self) -> Option<bool> {
1226        Some(true) // bool can't be NA
1227    }
1228
1229    fn sum(&self, _na_rm: bool) -> Option<i64> {
1230        Some(self.iter().filter(|&&x| x).count() as i64)
1231    }
1232}
1233
1234impl_len_slice!(String);
1235
1236impl AltStringData for &[String] {
1237    fn elt(&self, i: usize) -> Option<&str> {
1238        Some(self[i].as_str())
1239    }
1240}
1241
1242impl_len_slice!(&str);
1243
1244impl AltStringData for &[&str] {
1245    fn elt(&self, i: usize) -> Option<&str> {
1246        Some(self[i])
1247    }
1248}
1249// endregion
1250
1251// region: NOTE on &'static [T] (static slices)
1252//
1253// `&'static [T]` is Sized (fat pointer: ptr + len) and satisfies 'static,
1254// so it can be used DIRECTLY with ALTREP via ExternalPtr.
1255//
1256// The data trait implementations above for `&[T]` already cover `&'static [T]`
1257// since `&'static [T]` is a subtype of `&[T]`. The ALTREP trait implementations
1258// (Altrep, AltVec, AltInteger, etc.) are provided separately in altrep_impl.rs.
1259//
1260// Use cases:
1261// - Const arrays: `static DATA: [i32; 5] = [1, 2, 3, 4, 5]; create_altrep(&DATA[..])`
1262// - Leaked data: `let s: &'static [i32] = Box::leak(vec.into_boxed_slice());`
1263// - Memory-mapped files with 'static lifetime
1264// endregion
1265
1266// region: Built-in implementations for arrays (owned, fixed-size)
1267
1268impl_len_array!(i32);
1269
1270impl<const N: usize> AltIntegerData for [i32; N] {
1271    fn elt(&self, i: usize) -> i32 {
1272        self[i]
1273    }
1274
1275    fn as_slice(&self) -> Option<&[i32]> {
1276        Some(self.as_slice())
1277    }
1278
1279    fn get_region(&self, start: usize, len: usize, buf: &mut [i32]) -> usize {
1280        let end = (start + len).min(N);
1281        let actual_len = end.saturating_sub(start);
1282        if actual_len > 0 {
1283            buf[..actual_len].copy_from_slice(&self[start..end]);
1284        }
1285        actual_len
1286    }
1287
1288    fn no_na(&self) -> Option<bool> {
1289        Some(!self.contains(&i32::MIN))
1290    }
1291}
1292
1293impl_len_array!(f64);
1294
1295impl<const N: usize> AltRealData for [f64; N] {
1296    fn elt(&self, i: usize) -> f64 {
1297        self[i]
1298    }
1299
1300    fn as_slice(&self) -> Option<&[f64]> {
1301        Some(self.as_slice())
1302    }
1303
1304    fn get_region(&self, start: usize, len: usize, buf: &mut [f64]) -> usize {
1305        let end = (start + len).min(N);
1306        let actual_len = end.saturating_sub(start);
1307        if actual_len > 0 {
1308            buf[..actual_len].copy_from_slice(&self[start..end]);
1309        }
1310        actual_len
1311    }
1312
1313    fn no_na(&self) -> Option<bool> {
1314        Some(!self.iter().any(|x| x.to_bits() == NA_REAL.to_bits()))
1315    }
1316}
1317
1318impl_len_array!(bool);
1319
1320impl<const N: usize> AltLogicalData for [bool; N] {
1321    fn elt(&self, i: usize) -> Logical {
1322        Logical::from_bool(self[i])
1323    }
1324
1325    fn no_na(&self) -> Option<bool> {
1326        Some(true) // bool arrays can't have NA
1327    }
1328}
1329
1330impl_len_array!(u8);
1331
1332impl<const N: usize> AltRawData for [u8; N] {
1333    fn elt(&self, i: usize) -> u8 {
1334        self[i]
1335    }
1336
1337    fn as_slice(&self) -> Option<&[u8]> {
1338        Some(self.as_slice())
1339    }
1340
1341    fn get_region(&self, start: usize, len: usize, buf: &mut [u8]) -> usize {
1342        let end = (start + len).min(N);
1343        let actual_len = end.saturating_sub(start);
1344        if actual_len > 0 {
1345            buf[..actual_len].copy_from_slice(&self[start..end]);
1346        }
1347        actual_len
1348    }
1349}
1350
1351impl_len_array!(String);
1352
1353impl<const N: usize> AltStringData for [String; N] {
1354    fn elt(&self, i: usize) -> Option<&str> {
1355        Some(self[i].as_str())
1356    }
1357}
1358// endregion
1359
1360// region: Built-in implementations for Vec<Rcomplex> (complex numbers)
1361
1362impl_len_vec!(Rcomplex);
1363
1364impl AltComplexData for Vec<Rcomplex> {
1365    fn elt(&self, i: usize) -> Rcomplex {
1366        self[i]
1367    }
1368
1369    fn as_slice(&self) -> Option<&[Rcomplex]> {
1370        Some(self.as_slice())
1371    }
1372
1373    fn get_region(&self, start: usize, len: usize, buf: &mut [Rcomplex]) -> usize {
1374        let end = (start + len).min(self.len());
1375        let actual_len = end.saturating_sub(start);
1376        if actual_len > 0 {
1377            buf[..actual_len].copy_from_slice(&self[start..end]);
1378        }
1379        actual_len
1380    }
1381}
1382
1383impl_dataptr_vec!(Rcomplex);
1384// endregion
1385
1386// region: Built-in implementations for Box<[Rcomplex]>
1387
1388impl_len_boxed!(Rcomplex);
1389
1390impl AltComplexData for Box<[Rcomplex]> {
1391    fn elt(&self, i: usize) -> Rcomplex {
1392        self[i]
1393    }
1394
1395    fn as_slice(&self) -> Option<&[Rcomplex]> {
1396        Some(self.as_ref())
1397    }
1398
1399    fn get_region(&self, start: usize, len: usize, buf: &mut [Rcomplex]) -> usize {
1400        let end = (start + len).min(self.len());
1401        let actual_len = end.saturating_sub(start);
1402        if actual_len > 0 {
1403            buf[..actual_len].copy_from_slice(&self[start..end]);
1404        }
1405        actual_len
1406    }
1407}
1408
1409impl_dataptr_boxed!(Rcomplex);
1410// endregion
1411
1412// region: Built-in implementations for [Rcomplex; N] (complex arrays)
1413
1414impl_len_array!(Rcomplex);
1415
1416impl<const N: usize> AltComplexData for [Rcomplex; N] {
1417    fn elt(&self, i: usize) -> Rcomplex {
1418        self[i]
1419    }
1420
1421    fn as_slice(&self) -> Option<&[Rcomplex]> {
1422        Some(self.as_slice())
1423    }
1424
1425    fn get_region(&self, start: usize, len: usize, buf: &mut [Rcomplex]) -> usize {
1426        let end = (start + len).min(N);
1427        let actual_len = end.saturating_sub(start);
1428        if actual_len > 0 {
1429            buf[..actual_len].copy_from_slice(&self[start..end]);
1430        }
1431        actual_len
1432    }
1433}
1434// endregion
1435
1436// region: Built-in implementations for Cow<'static, [T]>
1437//
1438// Cow<'static, [T]> enables zero-copy borrows from R vectors while retaining
1439// ownership semantics. Borrowed variants expose the underlying R data directly
1440// via dataptr; Owned variants behave like Vec<T>. Copy-on-write happens
1441// automatically when R requests a writable dataptr on a Borrowed variant.
1442
1443macro_rules! impl_len_cow {
1444    ($elem:ty) => {
1445        impl AltrepLen for Cow<'static, [$elem]> {
1446            fn len(&self) -> usize {
1447                <[$elem]>::len(self)
1448            }
1449        }
1450    };
1451}
1452
1453macro_rules! impl_dataptr_cow {
1454    ($elem:ty) => {
1455        impl AltrepDataptr<$elem> for Cow<'static, [$elem]> {
1456            fn dataptr(&mut self, writable: bool) -> Option<*mut $elem> {
1457                if writable {
1458                    // to_mut() triggers copy-on-write for Borrowed variants.
1459                    // Only do this when R intends to write through the pointer.
1460                    Some(self.to_mut().as_mut_ptr())
1461                } else {
1462                    // Read-only access: return the existing pointer without
1463                    // forcing a copy for Borrowed variants.
1464                    Some(self.as_ptr().cast_mut())
1465                }
1466            }
1467
1468            fn dataptr_or_null(&self) -> Option<*const $elem> {
1469                Some(self.as_ptr())
1470            }
1471        }
1472    };
1473}
1474
1475macro_rules! impl_serialize_cow {
1476    ($elem:ty) => {
1477        impl AltrepSerialize for Cow<'static, [$elem]> {
1478            fn serialized_state(&self) -> SEXP {
1479                use crate::into_r::IntoR;
1480                self.clone().into_sexp()
1481            }
1482
1483            fn unserialize(state: SEXP) -> Option<Self> {
1484                use crate::from_r::TryFromSexp;
1485                Cow::<'static, [$elem]>::try_from_sexp(state).ok()
1486            }
1487        }
1488    };
1489}
1490
1491impl_len_cow!(i32);
1492
1493impl AltIntegerData for Cow<'static, [i32]> {
1494    fn elt(&self, i: usize) -> i32 {
1495        self[i]
1496    }
1497
1498    fn as_slice(&self) -> Option<&[i32]> {
1499        Some(self.as_ref())
1500    }
1501
1502    fn get_region(&self, start: usize, len: usize, buf: &mut [i32]) -> usize {
1503        let end = (start + len).min(<[i32]>::len(self));
1504        let actual_len = end.saturating_sub(start);
1505        if actual_len > 0 {
1506            buf[..actual_len].copy_from_slice(&self[start..end]);
1507        }
1508        actual_len
1509    }
1510
1511    fn no_na(&self) -> Option<bool> {
1512        Some(!self.contains(&i32::MIN))
1513    }
1514
1515    fn sum(&self, na_rm: bool) -> Option<i64> {
1516        let mut sum: i64 = 0;
1517        for &x in self.iter() {
1518            if x == i32::MIN {
1519                if !na_rm {
1520                    return None;
1521                }
1522            } else {
1523                sum += x as i64;
1524            }
1525        }
1526        Some(sum)
1527    }
1528
1529    fn min(&self, na_rm: bool) -> Option<i32> {
1530        let mut min = i32::MAX;
1531        let mut found = false;
1532        for &x in self.iter() {
1533            if x == i32::MIN {
1534                if !na_rm {
1535                    return None;
1536                }
1537            } else {
1538                found = true;
1539                min = min.min(x);
1540            }
1541        }
1542        if found { Some(min) } else { None }
1543    }
1544
1545    fn max(&self, na_rm: bool) -> Option<i32> {
1546        let mut max = i32::MIN + 1;
1547        let mut found = false;
1548        for &x in self.iter() {
1549            if x == i32::MIN {
1550                if !na_rm {
1551                    return None;
1552                }
1553            } else {
1554                found = true;
1555                max = max.max(x);
1556            }
1557        }
1558        if found { Some(max) } else { None }
1559    }
1560}
1561
1562impl_dataptr_cow!(i32);
1563impl_serialize_cow!(i32);
1564
1565impl_len_cow!(f64);
1566
1567impl AltRealData for Cow<'static, [f64]> {
1568    fn elt(&self, i: usize) -> f64 {
1569        self[i]
1570    }
1571
1572    fn as_slice(&self) -> Option<&[f64]> {
1573        Some(self.as_ref())
1574    }
1575
1576    fn get_region(&self, start: usize, len: usize, buf: &mut [f64]) -> usize {
1577        let end = (start + len).min(<[f64]>::len(self));
1578        let actual_len = end.saturating_sub(start);
1579        if actual_len > 0 {
1580            buf[..actual_len].copy_from_slice(&self[start..end]);
1581        }
1582        actual_len
1583    }
1584
1585    fn no_na(&self) -> Option<bool> {
1586        Some(!self.iter().any(|x| x.to_bits() == NA_REAL.to_bits()))
1587    }
1588
1589    fn sum(&self, na_rm: bool) -> Option<f64> {
1590        let mut sum = 0.0;
1591        for &x in self.iter() {
1592            if x.to_bits() == NA_REAL.to_bits() {
1593                if !na_rm {
1594                    return Some(NA_REAL);
1595                }
1596            } else if x.is_nan() {
1597                if !na_rm {
1598                    return Some(f64::NAN);
1599                }
1600            } else {
1601                sum += x;
1602            }
1603        }
1604        Some(sum)
1605    }
1606
1607    fn min(&self, na_rm: bool) -> Option<f64> {
1608        let mut min = f64::INFINITY;
1609        let mut found = false;
1610        for &x in self.iter() {
1611            if x.to_bits() == NA_REAL.to_bits() {
1612                if !na_rm {
1613                    return Some(NA_REAL);
1614                }
1615            } else if x.is_nan() {
1616                if !na_rm {
1617                    return Some(f64::NAN);
1618                }
1619            } else {
1620                found = true;
1621                min = min.min(x);
1622            }
1623        }
1624        if found { Some(min) } else { None }
1625    }
1626
1627    fn max(&self, na_rm: bool) -> Option<f64> {
1628        let mut max = f64::NEG_INFINITY;
1629        let mut found = false;
1630        for &x in self.iter() {
1631            if x.to_bits() == NA_REAL.to_bits() {
1632                if !na_rm {
1633                    return Some(NA_REAL);
1634                }
1635            } else if x.is_nan() {
1636                if !na_rm {
1637                    return Some(f64::NAN);
1638                }
1639            } else {
1640                found = true;
1641                max = max.max(x);
1642            }
1643        }
1644        if found { Some(max) } else { None }
1645    }
1646}
1647
1648impl_dataptr_cow!(f64);
1649impl_serialize_cow!(f64);
1650
1651impl_len_cow!(u8);
1652
1653impl AltRawData for Cow<'static, [u8]> {
1654    fn elt(&self, i: usize) -> u8 {
1655        self[i]
1656    }
1657
1658    fn as_slice(&self) -> Option<&[u8]> {
1659        Some(self.as_ref())
1660    }
1661
1662    fn get_region(&self, start: usize, len: usize, buf: &mut [u8]) -> usize {
1663        let end = (start + len).min(<[u8]>::len(self));
1664        let actual_len = end.saturating_sub(start);
1665        if actual_len > 0 {
1666            buf[..actual_len].copy_from_slice(&self[start..end]);
1667        }
1668        actual_len
1669    }
1670}
1671
1672impl_dataptr_cow!(u8);
1673impl_serialize_cow!(u8);
1674
1675impl_len_cow!(Rcomplex);
1676
1677impl AltComplexData for Cow<'static, [Rcomplex]> {
1678    fn elt(&self, i: usize) -> Rcomplex {
1679        self[i]
1680    }
1681
1682    fn as_slice(&self) -> Option<&[Rcomplex]> {
1683        Some(self.as_ref())
1684    }
1685
1686    fn get_region(&self, start: usize, len: usize, buf: &mut [Rcomplex]) -> usize {
1687        let end = (start + len).min(<[Rcomplex]>::len(self));
1688        let actual_len = end.saturating_sub(start);
1689        if actual_len > 0 {
1690            buf[..actual_len].copy_from_slice(&self[start..end]);
1691        }
1692        actual_len
1693    }
1694}
1695
1696impl_dataptr_cow!(Rcomplex);
1697impl_serialize_cow!(Rcomplex);
1698// endregion
1699
1700// region: Low-level ALTREP trait implementations
1701//
1702// The low-level trait impls (Altrep, AltVec, Alt*, InferBase) for builtin types
1703// are located in altrep_impl.rs. This is because the impl_alt*_from_data! macros
1704// are defined there and need to be in the same module.
1705//
1706// See altrep_impl.rs for:
1707// - Vec<i32>, Vec<f64>, Vec<bool>, Vec<u8>, Vec<String>, Vec<Rcomplex>
1708// - Box<[i32]>, Box<[f64]>, Box<[bool]>, Box<[u8]>, Box<[String]>, Box<[Rcomplex]>
1709// - Cow<'static, [i32]>, Cow<'static, [f64]>, Cow<'static, [u8]>, Cow<'static, [Rcomplex]>
1710// - Range<i32>, Range<i64>, Range<f64>
1711// endregion
1712
1713#[cfg(test)]
1714mod tests {
1715    use super::*;
1716    use crate::altrep_data::AltRealData;
1717
1718    // region: NA_REAL bit pattern tests
1719
1720    /// Verify NA_REAL has the expected R bit pattern.
1721    #[test]
1722    fn na_real_bit_pattern() {
1723        assert_eq!(NA_REAL.to_bits(), 0x7FF0_0000_0000_07A2);
1724    }
1725
1726    /// Regular NaN is NOT the same bit pattern as NA_real_.
1727    #[test]
1728    fn nan_is_not_na_real() {
1729        let nan = f64::NAN;
1730        assert!(nan.is_nan());
1731        assert_ne!(nan.to_bits(), NA_REAL.to_bits());
1732    }
1733
1734    // endregion
1735
1736    // region: Vec<f64> no_na — NA vs NaN
1737
1738    /// A vector with only regular (non-NA) NaN should report no_na = Some(true).
1739    /// Regular NaN is a valid floating-point value, not R's NA.
1740    #[test]
1741    fn vec_f64_no_na_with_regular_nan_is_true() {
1742        let v: Vec<f64> = vec![1.0, f64::NAN, 3.0];
1743        assert_eq!(AltRealData::no_na(&v), Some(true));
1744    }
1745
1746    /// A vector containing NA_real_ should report no_na = Some(false).
1747    #[test]
1748    fn vec_f64_no_na_with_na_real_is_false() {
1749        let v: Vec<f64> = vec![1.0, NA_REAL, 3.0];
1750        assert_eq!(AltRealData::no_na(&v), Some(false));
1751    }
1752
1753    /// A vector with no NaN and no NA should report no_na = Some(true).
1754    #[test]
1755    fn vec_f64_no_na_all_finite_is_true() {
1756        let v: Vec<f64> = vec![1.0, 2.0, 3.0];
1757        assert_eq!(AltRealData::no_na(&v), Some(true));
1758    }
1759
1760    // endregion
1761
1762    // region: Vec<f64> sum — NA vs NaN
1763
1764    /// sum with na_rm=false and NA_real_ present should return Some(NA_REAL).
1765    #[test]
1766    fn vec_f64_sum_na_propagates() {
1767        let v: Vec<f64> = vec![1.0, NA_REAL, 3.0];
1768        let result = AltRealData::sum(&v, false);
1769        assert!(result.is_some());
1770        let bits = result.unwrap().to_bits();
1771        assert_eq!(
1772            bits,
1773            NA_REAL.to_bits(),
1774            "sum with NA should return NA_real_"
1775        );
1776    }
1777
1778    /// sum with na_rm=true and NA_real_ present should skip the NA and sum the rest.
1779    #[test]
1780    fn vec_f64_sum_na_rm_skips_na() {
1781        let v: Vec<f64> = vec![1.0, NA_REAL, 3.0];
1782        let result = AltRealData::sum(&v, true);
1783        assert_eq!(result, Some(4.0));
1784    }
1785
1786    /// sum with na_rm=false and regular NaN present should return Some(NaN) — NOT NA.
1787    #[test]
1788    fn vec_f64_sum_nan_propagates_as_nan_not_na() {
1789        let v: Vec<f64> = vec![1.0, f64::NAN, 3.0];
1790        let result = AltRealData::sum(&v, false);
1791        assert!(result.is_some());
1792        let val = result.unwrap();
1793        assert!(val.is_nan(), "sum with NaN (not NA) should return NaN");
1794        assert_ne!(
1795            val.to_bits(),
1796            NA_REAL.to_bits(),
1797            "sum with regular NaN should NOT return NA_real_"
1798        );
1799    }
1800
1801    /// sum with na_rm=true and regular NaN present should skip the NaN.
1802    #[test]
1803    fn vec_f64_sum_nan_rm_skips_nan() {
1804        let v: Vec<f64> = vec![1.0, f64::NAN, 3.0];
1805        let result = AltRealData::sum(&v, true);
1806        assert_eq!(result, Some(4.0));
1807    }
1808
1809    // endregion
1810
1811    // region: Box<[f64]> no_na — NA vs NaN
1812
1813    #[test]
1814    fn box_f64_no_na_with_regular_nan_is_true() {
1815        let v: Box<[f64]> = vec![1.0, f64::NAN, 3.0].into_boxed_slice();
1816        assert_eq!(AltRealData::no_na(&v), Some(true));
1817    }
1818
1819    #[test]
1820    fn box_f64_no_na_with_na_real_is_false() {
1821        let v: Box<[f64]> = vec![1.0, NA_REAL, 3.0].into_boxed_slice();
1822        assert_eq!(AltRealData::no_na(&v), Some(false));
1823    }
1824
1825    // endregion
1826
1827    // region: &[f64] no_na — NA vs NaN
1828
1829    #[test]
1830    fn slice_f64_no_na_with_regular_nan_is_true() {
1831        let data: &[f64] = &[1.0, f64::NAN, 3.0];
1832        assert_eq!(AltRealData::no_na(&data), Some(true));
1833    }
1834
1835    #[test]
1836    fn slice_f64_no_na_with_na_real_is_false() {
1837        let data: &[f64] = &[1.0, NA_REAL, 3.0];
1838        assert_eq!(AltRealData::no_na(&data), Some(false));
1839    }
1840
1841    // endregion
1842
1843    // region: [f64; N] no_na — NA vs NaN
1844
1845    #[test]
1846    fn array_f64_no_na_with_regular_nan_is_true() {
1847        let arr: [f64; 3] = [1.0, f64::NAN, 3.0];
1848        assert_eq!(AltRealData::no_na(&arr), Some(true));
1849    }
1850
1851    #[test]
1852    fn array_f64_no_na_with_na_real_is_false() {
1853        let arr: [f64; 3] = [1.0, NA_REAL, 3.0];
1854        assert_eq!(AltRealData::no_na(&arr), Some(false));
1855    }
1856
1857    // endregion
1858
1859    // region: Cow<'static, [f64]> no_na — NA vs NaN
1860
1861    #[test]
1862    fn cow_f64_no_na_with_regular_nan_is_true() {
1863        let v: Cow<'static, [f64]> = Cow::Owned(vec![1.0, f64::NAN, 3.0]);
1864        assert_eq!(AltRealData::no_na(&v), Some(true));
1865    }
1866
1867    #[test]
1868    fn cow_f64_no_na_with_na_real_is_false() {
1869        let v: Cow<'static, [f64]> = Cow::Owned(vec![1.0, NA_REAL, 3.0]);
1870        assert_eq!(AltRealData::no_na(&v), Some(false));
1871    }
1872
1873    // endregion
1874}