Skip to main content

miniextendr_api/altrep_data/iter/
coerce.rs

1//! Iterator-backed ALTREP with coercion support.
2//!
3//! Provides `IterIntCoerceData`, `IterRealCoerceData`, and `IterIntFromBoolData`
4//! for iterators that produce values coercible to the target R type.
5
6use super::IterState;
7use crate::altrep_data::{
8    AltComplexData, AltIntegerData, AltListData, AltRealData, AltStringData, AltrepLen, InferBase,
9    fill_region,
10};
11use crate::ffi::SEXP;
12
13/// Iterator-backed integer vector with coercion from any integer-like type.
14///
15/// Wraps an iterator producing values that coerce to `i32` (e.g., `u16`, `i8`, etc.).
16///
17/// # Example
18///
19/// ```ignore
20/// use miniextendr_api::altrep_data::IterIntCoerceData;
21///
22/// // Create from an iterator of u16 values
23/// let iter = (0..10u16).map(|x| x * 100);
24/// let data = IterIntCoerceData::from_iter(iter, 10);
25/// // Values are coerced from u16 to i32 when accessed
26/// ```
27pub struct IterIntCoerceData<I, T>
28where
29    I: Iterator<Item = T>,
30    T: crate::coerce::Coerce<i32> + Copy,
31{
32    state: IterState<I, T>,
33}
34
35impl<I, T> IterIntCoerceData<I, T>
36where
37    I: Iterator<Item = T>,
38    T: crate::coerce::Coerce<i32> + Copy,
39{
40    /// Create from an iterator with explicit length.
41    pub fn from_iter(iter: I, len: usize) -> Self {
42        Self {
43            state: IterState::new(iter, len),
44        }
45    }
46}
47
48impl<I, T> IterIntCoerceData<I, T>
49where
50    I: ExactSizeIterator<Item = T>,
51    T: crate::coerce::Coerce<i32> + Copy,
52{
53    /// Create from an ExactSizeIterator (length auto-detected).
54    pub fn from_exact_iter(iter: I) -> Self {
55        Self {
56            state: IterState::from_exact_size(iter),
57        }
58    }
59}
60
61impl<I, T> AltrepLen for IterIntCoerceData<I, T>
62where
63    I: Iterator<Item = T>,
64    T: crate::coerce::Coerce<i32> + Copy,
65{
66    fn len(&self) -> usize {
67        self.state.len()
68    }
69}
70
71impl<I, T> AltIntegerData for IterIntCoerceData<I, T>
72where
73    I: Iterator<Item = T>,
74    T: crate::coerce::Coerce<i32> + Copy,
75{
76    fn elt(&self, i: usize) -> i32 {
77        self.state
78            .get_element(i)
79            .map(|val| val.coerce())
80            .unwrap_or(crate::altrep_traits::NA_INTEGER)
81    }
82
83    fn as_slice(&self) -> Option<&[i32]> {
84        // Can't return slice of i32 when cached values are type T
85        // Would need a separate coerced cache
86        None
87    }
88
89    fn get_region(&self, start: usize, len: usize, buf: &mut [i32]) -> usize {
90        fill_region(start, len, self.len(), buf, |idx| self.elt(idx))
91    }
92}
93
94impl<I, T> crate::externalptr::TypedExternal for IterIntCoerceData<I, T>
95where
96    I: Iterator<Item = T> + 'static,
97    T: crate::coerce::Coerce<i32> + Copy + 'static,
98{
99    const TYPE_NAME: &'static str = "IterIntCoerceData";
100    const TYPE_NAME_CSTR: &'static [u8] = b"IterIntCoerceData\0";
101    const TYPE_ID_CSTR: &'static [u8] = b"miniextendr_api::altrep::IterIntCoerceData\0";
102}
103
104impl<I, T> InferBase for IterIntCoerceData<I, T>
105where
106    I: Iterator<Item = T> + 'static,
107    T: crate::coerce::Coerce<i32> + Copy + 'static,
108{
109    const BASE: crate::altrep::RBase = crate::altrep::RBase::Int;
110
111    unsafe fn make_class(
112        class_name: *const i8,
113        pkg_name: *const i8,
114    ) -> crate::ffi::altrep::R_altrep_class_t {
115        unsafe {
116            crate::ffi::altrep::R_make_altinteger_class(class_name, pkg_name, core::ptr::null_mut())
117        }
118    }
119
120    unsafe fn install_methods(cls: crate::ffi::altrep::R_altrep_class_t) {
121        unsafe { crate::altrep_bridge::install_base::<Self>(cls) };
122        unsafe { crate::altrep_bridge::install_vec::<Self>(cls) };
123        unsafe { crate::altrep_bridge::install_int::<Self>(cls) };
124    }
125}
126
127impl<I, T> crate::altrep_traits::Altrep for IterIntCoerceData<I, T>
128where
129    I: Iterator<Item = T> + 'static,
130    T: crate::coerce::Coerce<i32> + Copy + 'static,
131{
132    fn length(x: crate::ffi::SEXP) -> crate::ffi::R_xlen_t {
133        let data = unsafe { <Self as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
134        data.len() as crate::ffi::R_xlen_t
135    }
136}
137
138impl<I, T> crate::altrep_traits::AltVec for IterIntCoerceData<I, T>
139where
140    I: Iterator<Item = T> + 'static,
141    T: crate::coerce::Coerce<i32> + Copy + 'static,
142{
143}
144
145impl<I, T> crate::altrep_traits::AltInteger for IterIntCoerceData<I, T>
146where
147    I: Iterator<Item = T> + 'static,
148    T: crate::coerce::Coerce<i32> + Copy + 'static,
149{
150    const HAS_ELT: bool = true;
151
152    fn elt(x: crate::ffi::SEXP, i: crate::ffi::R_xlen_t) -> i32 {
153        let data = unsafe { <Self as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
154        AltIntegerData::elt(data, i as usize)
155    }
156
157    const HAS_GET_REGION: bool = true;
158
159    fn get_region(
160        x: crate::ffi::SEXP,
161        start: crate::ffi::R_xlen_t,
162        len: crate::ffi::R_xlen_t,
163        buf: &mut [i32],
164    ) -> crate::ffi::R_xlen_t {
165        let data = unsafe { <Self as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
166        AltIntegerData::get_region(data, start as usize, len as usize, buf) as crate::ffi::R_xlen_t
167    }
168}
169
170/// Iterator-backed real vector with coercion from any float-like type.
171///
172/// Wraps an iterator producing values that coerce to `f64` (e.g., `f32`, integer types).
173///
174/// # Example
175///
176/// ```ignore
177/// use miniextendr_api::altrep_data::IterRealCoerceData;
178///
179/// // Create from an iterator of f32 values
180/// let iter = (0..5).map(|x| x as f32 * 1.5);
181/// let data = IterRealCoerceData::from_iter(iter, 5);
182/// // Values are coerced from f32 to f64 when accessed
183/// ```
184pub struct IterRealCoerceData<I, T>
185where
186    I: Iterator<Item = T>,
187    T: crate::coerce::Coerce<f64> + Copy,
188{
189    state: IterState<I, T>,
190}
191
192impl<I, T> IterRealCoerceData<I, T>
193where
194    I: Iterator<Item = T>,
195    T: crate::coerce::Coerce<f64> + Copy,
196{
197    /// Create from an iterator with explicit length.
198    pub fn from_iter(iter: I, len: usize) -> Self {
199        Self {
200            state: IterState::new(iter, len),
201        }
202    }
203}
204
205impl<I, T> IterRealCoerceData<I, T>
206where
207    I: ExactSizeIterator<Item = T>,
208    T: crate::coerce::Coerce<f64> + Copy,
209{
210    /// Create from an ExactSizeIterator (length auto-detected).
211    pub fn from_exact_iter(iter: I) -> Self {
212        Self {
213            state: IterState::from_exact_size(iter),
214        }
215    }
216}
217
218impl<I, T> AltrepLen for IterRealCoerceData<I, T>
219where
220    I: Iterator<Item = T>,
221    T: crate::coerce::Coerce<f64> + Copy,
222{
223    fn len(&self) -> usize {
224        self.state.len()
225    }
226}
227
228impl<I, T> AltRealData for IterRealCoerceData<I, T>
229where
230    I: Iterator<Item = T>,
231    T: crate::coerce::Coerce<f64> + Copy,
232{
233    fn elt(&self, i: usize) -> f64 {
234        self.state
235            .get_element(i)
236            .map(|val| val.coerce())
237            .unwrap_or(f64::NAN)
238    }
239
240    fn as_slice(&self) -> Option<&[f64]> {
241        // Can't return slice of f64 when cached values are type T
242        None
243    }
244
245    fn get_region(&self, start: usize, len: usize, buf: &mut [f64]) -> usize {
246        fill_region(start, len, self.len(), buf, |idx| self.elt(idx))
247    }
248}
249
250impl<I, T> crate::externalptr::TypedExternal for IterRealCoerceData<I, T>
251where
252    I: Iterator<Item = T> + 'static,
253    T: crate::coerce::Coerce<f64> + Copy + 'static,
254{
255    const TYPE_NAME: &'static str = "IterRealCoerceData";
256    const TYPE_NAME_CSTR: &'static [u8] = b"IterRealCoerceData\0";
257    const TYPE_ID_CSTR: &'static [u8] = b"miniextendr_api::altrep::IterRealCoerceData\0";
258}
259
260impl<I, T> InferBase for IterRealCoerceData<I, T>
261where
262    I: Iterator<Item = T> + 'static,
263    T: crate::coerce::Coerce<f64> + Copy + 'static,
264{
265    const BASE: crate::altrep::RBase = crate::altrep::RBase::Real;
266
267    unsafe fn make_class(
268        class_name: *const i8,
269        pkg_name: *const i8,
270    ) -> crate::ffi::altrep::R_altrep_class_t {
271        unsafe {
272            crate::ffi::altrep::R_make_altreal_class(class_name, pkg_name, core::ptr::null_mut())
273        }
274    }
275
276    unsafe fn install_methods(cls: crate::ffi::altrep::R_altrep_class_t) {
277        unsafe { crate::altrep_bridge::install_base::<Self>(cls) };
278        unsafe { crate::altrep_bridge::install_vec::<Self>(cls) };
279        unsafe { crate::altrep_bridge::install_real::<Self>(cls) };
280    }
281}
282
283impl<I, T> crate::altrep_traits::Altrep for IterRealCoerceData<I, T>
284where
285    I: Iterator<Item = T> + 'static,
286    T: crate::coerce::Coerce<f64> + Copy + 'static,
287{
288    fn length(x: crate::ffi::SEXP) -> crate::ffi::R_xlen_t {
289        let data = unsafe { <Self as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
290        data.len() as crate::ffi::R_xlen_t
291    }
292}
293
294impl<I, T> crate::altrep_traits::AltVec for IterRealCoerceData<I, T>
295where
296    I: Iterator<Item = T> + 'static,
297    T: crate::coerce::Coerce<f64> + Copy + 'static,
298{
299}
300
301impl<I, T> crate::altrep_traits::AltReal for IterRealCoerceData<I, T>
302where
303    I: Iterator<Item = T> + 'static,
304    T: crate::coerce::Coerce<f64> + Copy + 'static,
305{
306    const HAS_ELT: bool = true;
307
308    fn elt(x: crate::ffi::SEXP, i: crate::ffi::R_xlen_t) -> f64 {
309        let data = unsafe { <Self as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
310        AltRealData::elt(data, i as usize)
311    }
312
313    const HAS_GET_REGION: bool = true;
314
315    fn get_region(
316        x: crate::ffi::SEXP,
317        start: crate::ffi::R_xlen_t,
318        len: crate::ffi::R_xlen_t,
319        buf: &mut [f64],
320    ) -> crate::ffi::R_xlen_t {
321        let data = unsafe { <Self as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
322        AltRealData::get_region(data, start as usize, len as usize, buf) as crate::ffi::R_xlen_t
323    }
324}
325
326/// Iterator-backed integer vector with coercion from bool.
327///
328/// Wraps an iterator producing `bool` values that coerce to `i32`.
329/// Useful for converting boolean iterators to integer vectors.
330pub struct IterIntFromBoolData<I>
331where
332    I: Iterator<Item = bool>,
333{
334    state: IterState<I, bool>,
335}
336
337impl<I> IterIntFromBoolData<I>
338where
339    I: Iterator<Item = bool>,
340{
341    /// Create from an iterator with explicit length.
342    pub fn from_iter(iter: I, len: usize) -> Self {
343        Self {
344            state: IterState::new(iter, len),
345        }
346    }
347}
348
349impl<I> IterIntFromBoolData<I>
350where
351    I: ExactSizeIterator<Item = bool>,
352{
353    /// Create from an ExactSizeIterator (length auto-detected).
354    pub fn from_exact_iter(iter: I) -> Self {
355        Self {
356            state: IterState::from_exact_size(iter),
357        }
358    }
359}
360
361impl<I> AltrepLen for IterIntFromBoolData<I>
362where
363    I: Iterator<Item = bool>,
364{
365    fn len(&self) -> usize {
366        self.state.len()
367    }
368}
369
370impl<I> AltIntegerData for IterIntFromBoolData<I>
371where
372    I: Iterator<Item = bool>,
373{
374    fn elt(&self, i: usize) -> i32 {
375        use crate::coerce::Coerce;
376        self.state
377            .get_element(i)
378            .map(|val| val.coerce())
379            .unwrap_or(crate::altrep_traits::NA_INTEGER)
380    }
381
382    fn as_slice(&self) -> Option<&[i32]> {
383        None
384    }
385
386    fn get_region(&self, start: usize, len: usize, buf: &mut [i32]) -> usize {
387        fill_region(start, len, self.len(), buf, |idx| self.elt(idx))
388    }
389}
390
391impl<I: Iterator<Item = bool> + 'static> crate::externalptr::TypedExternal
392    for IterIntFromBoolData<I>
393{
394    const TYPE_NAME: &'static str = "IterIntFromBoolData";
395    const TYPE_NAME_CSTR: &'static [u8] = b"IterIntFromBoolData\0";
396    const TYPE_ID_CSTR: &'static [u8] = b"miniextendr_api::altrep::IterIntFromBoolData\0";
397}
398
399impl<I: Iterator<Item = bool> + 'static> InferBase for IterIntFromBoolData<I> {
400    const BASE: crate::altrep::RBase = crate::altrep::RBase::Int;
401
402    unsafe fn make_class(
403        class_name: *const i8,
404        pkg_name: *const i8,
405    ) -> crate::ffi::altrep::R_altrep_class_t {
406        unsafe {
407            crate::ffi::altrep::R_make_altinteger_class(class_name, pkg_name, core::ptr::null_mut())
408        }
409    }
410
411    unsafe fn install_methods(cls: crate::ffi::altrep::R_altrep_class_t) {
412        unsafe { crate::altrep_bridge::install_base::<Self>(cls) };
413        unsafe { crate::altrep_bridge::install_vec::<Self>(cls) };
414        unsafe { crate::altrep_bridge::install_int::<Self>(cls) };
415    }
416}
417
418impl<I: Iterator<Item = bool> + 'static> crate::altrep_traits::Altrep for IterIntFromBoolData<I> {
419    fn length(x: crate::ffi::SEXP) -> crate::ffi::R_xlen_t {
420        let data = unsafe { <Self as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
421        data.len() as crate::ffi::R_xlen_t
422    }
423}
424
425impl<I: Iterator<Item = bool> + 'static> crate::altrep_traits::AltVec for IterIntFromBoolData<I> {}
426
427impl<I: Iterator<Item = bool> + 'static> crate::altrep_traits::AltInteger
428    for IterIntFromBoolData<I>
429{
430    const HAS_ELT: bool = true;
431
432    fn elt(x: crate::ffi::SEXP, i: crate::ffi::R_xlen_t) -> i32 {
433        let data = unsafe { <Self as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
434        AltIntegerData::elt(data, i as usize)
435    }
436
437    const HAS_GET_REGION: bool = true;
438
439    fn get_region(
440        x: crate::ffi::SEXP,
441        start: crate::ffi::R_xlen_t,
442        len: crate::ffi::R_xlen_t,
443        buf: &mut [i32],
444    ) -> crate::ffi::R_xlen_t {
445        let data = unsafe { <Self as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
446        AltIntegerData::get_region(data, start as usize, len as usize, buf) as crate::ffi::R_xlen_t
447    }
448}
449
450/// Iterator-backed string vector.
451///
452/// Wraps an iterator producing `String` values and exposes it as an ALTREP character vector.
453///
454/// # Performance Warning
455///
456/// Unlike other `Iter*Data` types, **accessing ANY element forces full materialization
457/// of the entire iterator**. This is because R's `AltStringData::elt()` returns a borrowed
458/// `&str`, which requires stable storage. The internal `RefCell` cannot provide the required
459/// lifetime, so all strings must be materialized upfront.
460///
461/// This means:
462/// - `elt(0)` on a million-element iterator will generate ALL million strings
463/// - There is no lazy evaluation benefit for string iterators
464/// - Memory usage equals the full vector regardless of access patterns
465///
466/// For truly lazy string ALTREP, consider implementing a custom type that stores
467/// strings in a way that allows borrowing without full materialization (e.g., arena
468/// allocation or caching generated strings incrementally).
469///
470/// # Example
471///
472/// ```ignore
473/// use miniextendr_api::altrep_data::IterStringData;
474///
475/// let iter = (0..5).map(|x| format!("item_{}", x));
476/// let data = IterStringData::from_iter(iter, 5);
477/// // First access to ANY element will materialize all 5 strings
478/// ```
479pub struct IterStringData<I>
480where
481    I: Iterator<Item = String>,
482{
483    state: IterState<I, String>,
484}
485
486impl<I> IterStringData<I>
487where
488    I: Iterator<Item = String>,
489{
490    /// Create from an iterator with explicit length.
491    pub fn from_iter(iter: I, len: usize) -> Self {
492        Self {
493            state: IterState::new(iter, len),
494        }
495    }
496}
497
498impl<I> IterStringData<I>
499where
500    I: ExactSizeIterator<Item = String>,
501{
502    /// Create from an ExactSizeIterator (length auto-detected).
503    pub fn from_exact_iter(iter: I) -> Self {
504        Self {
505            state: IterState::from_exact_size(iter),
506        }
507    }
508}
509
510impl<I> AltrepLen for IterStringData<I>
511where
512    I: Iterator<Item = String>,
513{
514    fn len(&self) -> usize {
515        self.state.len()
516    }
517}
518
519impl<I> AltStringData for IterStringData<I>
520where
521    I: Iterator<Item = String>,
522{
523    fn elt(&self, i: usize) -> Option<&str> {
524        // Materialize to get stable storage for &str references
525        // This is necessary because we can't return &str from RefCell borrows
526        let materialized = self.state.materialize_all();
527        materialized.get(i).map(|s| s.as_str())
528    }
529}
530
531impl<I: Iterator<Item = String> + 'static> crate::externalptr::TypedExternal for IterStringData<I> {
532    const TYPE_NAME: &'static str = "IterStringData";
533    const TYPE_NAME_CSTR: &'static [u8] = b"IterStringData\0";
534    const TYPE_ID_CSTR: &'static [u8] = b"miniextendr_api::altrep::IterStringData\0";
535}
536
537impl<I: Iterator<Item = String> + 'static> InferBase for IterStringData<I> {
538    const BASE: crate::altrep::RBase = crate::altrep::RBase::String;
539
540    unsafe fn make_class(
541        class_name: *const i8,
542        pkg_name: *const i8,
543    ) -> crate::ffi::altrep::R_altrep_class_t {
544        unsafe {
545            crate::ffi::altrep::R_make_altstring_class(class_name, pkg_name, core::ptr::null_mut())
546        }
547    }
548
549    unsafe fn install_methods(cls: crate::ffi::altrep::R_altrep_class_t) {
550        unsafe { crate::altrep_bridge::install_base::<Self>(cls) };
551        unsafe { crate::altrep_bridge::install_vec::<Self>(cls) };
552        unsafe { crate::altrep_bridge::install_str::<Self>(cls) };
553    }
554}
555
556impl<I: Iterator<Item = String> + 'static> crate::altrep_traits::Altrep for IterStringData<I> {
557    // String ALTREP elt calls Rf_mkCharLenCE (R API) — must use RUnwind to catch longjmps.
558    const GUARD: crate::altrep_traits::AltrepGuard = crate::altrep_traits::AltrepGuard::RUnwind;
559
560    fn length(x: crate::ffi::SEXP) -> crate::ffi::R_xlen_t {
561        let data = unsafe { <Self as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
562        data.len() as crate::ffi::R_xlen_t
563    }
564}
565
566impl<I: Iterator<Item = String> + 'static> crate::altrep_traits::AltVec for IterStringData<I> {}
567
568impl<I: Iterator<Item = String> + 'static> crate::altrep_traits::AltString for IterStringData<I> {
569    fn elt(x: crate::ffi::SEXP, i: crate::ffi::R_xlen_t) -> crate::ffi::SEXP {
570        let data = unsafe { <Self as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
571        AltStringData::elt(data, i as usize)
572            .map(|s| unsafe { crate::altrep_impl::checked_mkchar(s) })
573            .unwrap_or(SEXP::na_string())
574    }
575}
576
577/// Iterator-backed list vector.
578///
579/// Wraps an iterator producing R `SEXP` values and exposes it as an ALTREP list.
580///
581/// # Safety
582///
583/// The iterator must produce valid, protected SEXP values. Each SEXP must remain
584/// protected for the lifetime of the ALTREP object.
585///
586/// # Example
587///
588/// ```ignore
589/// use miniextendr_api::altrep_data::IterListData;
590/// use miniextendr_api::IntoR;
591///
592/// let iter = (0..5).map(|x| vec![x, x+1, x+2].into_sexp());
593/// let data = IterListData::from_iter(iter, 5);
594/// ```
595pub struct IterListData<I>
596where
597    I: Iterator<Item = SEXP>,
598{
599    state: IterState<I, SEXP>,
600}
601
602impl<I> IterListData<I>
603where
604    I: Iterator<Item = SEXP>,
605{
606    /// Create from an iterator with explicit length.
607    ///
608    /// # Safety
609    ///
610    /// The iterator must produce valid, protected SEXP values.
611    pub fn from_iter(iter: I, len: usize) -> Self {
612        Self {
613            state: IterState::new(iter, len),
614        }
615    }
616}
617
618impl<I> IterListData<I>
619where
620    I: ExactSizeIterator<Item = SEXP>,
621{
622    /// Create from an ExactSizeIterator (length auto-detected).
623    ///
624    /// # Safety
625    ///
626    /// The iterator must produce valid, protected SEXP values.
627    pub fn from_exact_iter(iter: I) -> Self {
628        Self {
629            state: IterState::from_exact_size(iter),
630        }
631    }
632}
633
634impl<I> AltrepLen for IterListData<I>
635where
636    I: Iterator<Item = SEXP>,
637{
638    fn len(&self) -> usize {
639        self.state.len()
640    }
641}
642
643impl<I> AltListData for IterListData<I>
644where
645    I: Iterator<Item = SEXP>,
646{
647    fn elt(&self, i: usize) -> SEXP {
648        use crate::ffi::SEXP;
649        self.state.get_element(i).unwrap_or(SEXP::nil())
650    }
651}
652
653impl<I: Iterator<Item = SEXP> + 'static> crate::externalptr::TypedExternal for IterListData<I> {
654    const TYPE_NAME: &'static str = "IterListData";
655    const TYPE_NAME_CSTR: &'static [u8] = b"IterListData\0";
656    const TYPE_ID_CSTR: &'static [u8] = b"miniextendr_api::altrep::IterListData\0";
657}
658
659impl<I: Iterator<Item = SEXP> + 'static> InferBase for IterListData<I> {
660    const BASE: crate::altrep::RBase = crate::altrep::RBase::List;
661
662    unsafe fn make_class(
663        class_name: *const i8,
664        pkg_name: *const i8,
665    ) -> crate::ffi::altrep::R_altrep_class_t {
666        unsafe {
667            crate::ffi::altrep::R_make_altlist_class(class_name, pkg_name, core::ptr::null_mut())
668        }
669    }
670
671    unsafe fn install_methods(cls: crate::ffi::altrep::R_altrep_class_t) {
672        unsafe { crate::altrep_bridge::install_base::<Self>(cls) };
673        unsafe { crate::altrep_bridge::install_vec::<Self>(cls) };
674        unsafe { crate::altrep_bridge::install_list::<Self>(cls) };
675    }
676}
677
678impl<I: Iterator<Item = SEXP> + 'static> crate::altrep_traits::Altrep for IterListData<I> {
679    fn length(x: crate::ffi::SEXP) -> crate::ffi::R_xlen_t {
680        let data = unsafe { <Self as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
681        data.len() as crate::ffi::R_xlen_t
682    }
683}
684
685impl<I: Iterator<Item = SEXP> + 'static> crate::altrep_traits::AltVec for IterListData<I> {}
686
687impl<I: Iterator<Item = SEXP> + 'static> crate::altrep_traits::AltList for IterListData<I> {
688    fn elt(x: crate::ffi::SEXP, i: crate::ffi::R_xlen_t) -> crate::ffi::SEXP {
689        let data = unsafe { <Self as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
690        AltListData::elt(data, i as usize)
691    }
692}
693
694/// Iterator-backed complex number vector.
695///
696/// Wraps an iterator producing `Rcomplex` values and exposes it as an ALTREP complex vector.
697///
698/// # Example
699///
700/// ```ignore
701/// use miniextendr_api::altrep_data::IterComplexData;
702/// use miniextendr_api::ffi::Rcomplex;
703///
704/// let iter = (0..5).map(|x| Rcomplex { r: x as f64, i: (x * 2) as f64 });
705/// let data = IterComplexData::from_iter(iter, 5);
706/// ```
707pub struct IterComplexData<I>
708where
709    I: Iterator<Item = crate::ffi::Rcomplex>,
710{
711    state: IterState<I, crate::ffi::Rcomplex>,
712}
713
714impl<I> IterComplexData<I>
715where
716    I: Iterator<Item = crate::ffi::Rcomplex>,
717{
718    /// Create from an iterator with explicit length.
719    pub fn from_iter(iter: I, len: usize) -> Self {
720        Self {
721            state: IterState::new(iter, len),
722        }
723    }
724}
725
726impl<I> IterComplexData<I>
727where
728    I: ExactSizeIterator<Item = crate::ffi::Rcomplex>,
729{
730    /// Create from an ExactSizeIterator (length auto-detected).
731    pub fn from_exact_iter(iter: I) -> Self {
732        Self {
733            state: IterState::from_exact_size(iter),
734        }
735    }
736}
737
738impl<I> AltrepLen for IterComplexData<I>
739where
740    I: Iterator<Item = crate::ffi::Rcomplex>,
741{
742    fn len(&self) -> usize {
743        self.state.len()
744    }
745}
746
747impl<I> AltComplexData for IterComplexData<I>
748where
749    I: Iterator<Item = crate::ffi::Rcomplex>,
750{
751    fn elt(&self, i: usize) -> crate::ffi::Rcomplex {
752        self.state.get_element(i).unwrap_or(crate::ffi::Rcomplex {
753            r: f64::NAN,
754            i: f64::NAN,
755        })
756    }
757
758    fn as_slice(&self) -> Option<&[crate::ffi::Rcomplex]> {
759        self.state.as_materialized()
760    }
761
762    fn get_region(&self, start: usize, len: usize, buf: &mut [crate::ffi::Rcomplex]) -> usize {
763        fill_region(start, len, self.len(), buf, |idx| self.elt(idx))
764    }
765}
766
767impl<I: Iterator<Item = crate::ffi::Rcomplex> + 'static> crate::externalptr::TypedExternal
768    for IterComplexData<I>
769{
770    const TYPE_NAME: &'static str = "IterComplexData";
771    const TYPE_NAME_CSTR: &'static [u8] = b"IterComplexData\0";
772    const TYPE_ID_CSTR: &'static [u8] = b"miniextendr_api::altrep::IterComplexData\0";
773}
774
775impl<I: Iterator<Item = crate::ffi::Rcomplex> + 'static> InferBase for IterComplexData<I> {
776    const BASE: crate::altrep::RBase = crate::altrep::RBase::Complex;
777
778    unsafe fn make_class(
779        class_name: *const i8,
780        pkg_name: *const i8,
781    ) -> crate::ffi::altrep::R_altrep_class_t {
782        unsafe {
783            crate::ffi::altrep::R_make_altcomplex_class(class_name, pkg_name, core::ptr::null_mut())
784        }
785    }
786
787    unsafe fn install_methods(cls: crate::ffi::altrep::R_altrep_class_t) {
788        unsafe { crate::altrep_bridge::install_base::<Self>(cls) };
789        unsafe { crate::altrep_bridge::install_vec::<Self>(cls) };
790        unsafe { crate::altrep_bridge::install_cplx::<Self>(cls) };
791    }
792}
793
794impl<I: Iterator<Item = crate::ffi::Rcomplex> + 'static> crate::altrep_traits::Altrep
795    for IterComplexData<I>
796{
797    fn length(x: crate::ffi::SEXP) -> crate::ffi::R_xlen_t {
798        let data = unsafe { <Self as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
799        data.len() as crate::ffi::R_xlen_t
800    }
801}
802
803impl<I: Iterator<Item = crate::ffi::Rcomplex> + 'static> crate::altrep_traits::AltVec
804    for IterComplexData<I>
805{
806}
807
808impl<I: Iterator<Item = crate::ffi::Rcomplex> + 'static> crate::altrep_traits::AltComplex
809    for IterComplexData<I>
810{
811    const HAS_ELT: bool = true;
812
813    fn elt(x: crate::ffi::SEXP, i: crate::ffi::R_xlen_t) -> crate::ffi::Rcomplex {
814        let data = unsafe { <Self as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
815        AltComplexData::elt(data, i as usize)
816    }
817
818    const HAS_GET_REGION: bool = true;
819
820    fn get_region(
821        x: crate::ffi::SEXP,
822        start: crate::ffi::R_xlen_t,
823        len: crate::ffi::R_xlen_t,
824        buf: &mut [crate::ffi::Rcomplex],
825    ) -> crate::ffi::R_xlen_t {
826        let data = unsafe { <Self as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
827        AltComplexData::get_region(data, start as usize, len as usize, buf) as crate::ffi::R_xlen_t
828    }
829}
830// endregion