pharmsol/data/
structs.rs

1use crate::{
2    data::*,
3    simulator::{Fa, Lag},
4    Censor,
5};
6use serde::{Deserialize, Serialize};
7use std::fmt;
8
9/// The main data container for pharmacokinetic/pharmacodynamic data
10///
11/// [Data] is a collection of [Subject] instances, which themselves contain [Occasion] instances with [Event]s.
12/// This structure represents the complete dataset for pharmacometric analysis.
13///
14/// # Examples
15///
16/// ```
17/// use pharmsol::*;
18///
19/// // Create subjects
20/// let subject1 = Subject::builder("patient_001")
21///     .bolus(0.0, 100.0, 0)
22///     .observation(1.0, 5.0, 0)
23///     .build();
24///     
25/// let subject2 = Subject::builder("patient_002")
26///     .bolus(0.0, 120.0, 0)
27///     .observation(1.0, 6.0, 0)
28///     .build();
29///     
30/// // Create dataset with multiple subjects
31/// let mut data = Data::new(vec![subject1]);
32/// data.add_subject(subject2);
33///
34/// // Filter data
35/// let filtered = data.filter_include(&["patient_001".to_string()]);
36/// ```
37#[derive(Serialize, Deserialize, Debug, Clone, Default)]
38pub struct Data {
39    subjects: Vec<Subject>,
40}
41impl Data {
42    /// Constructs a new [Data] object from a vector of [Subject]s
43    ///
44    /// It is recommended to construct subjects using the [SubjectBuilder] to ensure proper data formatting.
45    ///
46    /// # Arguments
47    ///
48    /// * `subjects` - Vector of [Subject]s to include in the dataset
49    pub fn new(subjects: Vec<Subject>) -> Self {
50        Data { subjects }
51    }
52
53    /// Get a vector of references to all subjects in the dataset
54    ///
55    /// # Returns
56    ///
57    /// Vector of references to all subjects
58    pub fn subjects(&self) -> Vec<&Subject> {
59        self.subjects.iter().collect()
60    }
61
62    /// Add a subject to the dataset
63    ///
64    /// # Arguments
65    ///
66    /// * `subject` - Subject to add to the dataset
67    pub fn add_subject(&mut self, subject: Subject) {
68        self.subjects.push(subject);
69    }
70
71    /// Get a specific subject by ID
72    ///
73    /// # Arguments
74    ///
75    /// * `id` - The ID of the subject to retrieve
76    ///
77    /// # Returns
78    ///
79    /// An `Option` containing a reference to the subject if found, or `None` if not found
80    pub fn get_subject(&self, id: &str) -> Option<&Subject> {
81        self.subjects.iter().find(|subject| subject.id() == id)
82    }
83
84    /// Get a mutable reference to a specific subject by ID
85    ///
86    /// # Arguments
87    ///
88    /// * `id` - The ID of the subject to retrieve
89    ///
90    /// # Returns
91    ///
92    /// An `Option` containing a reference to the subject if found, or `None` if not found
93    pub fn get_subject_mut(&mut self, id: &str) -> Option<&mut Subject> {
94        self.subjects.iter_mut().find(|subject| subject.id() == id)
95    }
96
97    /// Filter the dataset to include only subjects with specific IDs
98    ///
99    /// # Arguments
100    ///
101    /// * `include` - Vector of subject IDs to include
102    ///
103    /// # Returns
104    ///
105    /// A new `Data` object containing only the specified subjects
106    pub fn filter_include(&self, include: &[String]) -> Data {
107        let subjects = self
108            .subjects
109            .iter()
110            .filter(|subject| include.iter().any(|id| id == subject.id()))
111            .cloned()
112            .collect();
113        Data::new(subjects)
114    }
115
116    /// Filter the dataset to exclude subjects with specific IDs
117    ///
118    /// # Arguments
119    ///
120    /// * `exclude` - Vector of subject IDs to exclude
121    ///
122    /// # Returns
123    ///
124    /// A new `Data` object with the specified subjects excluded
125    pub fn filter_exclude(&self, exclude: Vec<String>) -> Data {
126        let subjects = self
127            .subjects
128            .iter()
129            .filter(|subject| !exclude.iter().any(|id| id == subject.id()))
130            .cloned()
131            .collect();
132        Data::new(subjects)
133    }
134
135    /// Expand the dataset by adding observations at regular time intervals
136    ///
137    /// This is useful for creating a dense grid of time points for simulations.
138    /// Observations are only added if they don't already exist at that time/outeq combination.
139    ///
140    /// # Arguments
141    ///
142    /// * `idelta` - Time interval between added observations
143    /// * `tad` - Additional time to add after the last observation
144    ///
145    /// # Returns
146    ///
147    /// A new `Data` object with expanded observations
148    pub fn expand(&self, idelta: f64, tad: f64) -> Data {
149        if idelta <= 0.0 {
150            return self.clone();
151        }
152
153        // Determine the last time across all subjects and occasions
154        let last_time = self
155            .subjects
156            .iter()
157            .flat_map(|subject| &subject.occasions)
158            .flat_map(|occasion| &occasion.events)
159            .filter_map(|event| match event {
160                Event::Observation(observation) => Some(observation.time()),
161                Event::Infusion(infusion) => Some(infusion.time() + infusion.duration()),
162                _ => None,
163            })
164            .max_by(|a, b| a.partial_cmp(b).unwrap())
165            .unwrap_or(0.0)
166            + tad;
167
168        // Collect unique output equations more efficiently
169        let outeq_values = self.get_output_equations();
170
171        // Create new data structure with expanded observations
172        let new_subjects = self
173            .subjects
174            .iter()
175            .map(|subject| {
176                let new_occasions = subject
177                    .occasions
178                    .iter()
179                    .map(|occasion| {
180                        let old_events = occasion.process_events(None, true);
181
182                        // Create a set of existing (time, outeq) pairs for fast lookup
183                        let existing_obs: std::collections::HashSet<(u64, usize)> = old_events
184                            .iter()
185                            .filter_map(|event| match event {
186                                Event::Observation(obs) => {
187                                    // Convert to microseconds for consistent comparison
188                                    let time_key = (obs.time() * 1e6).round() as u64;
189                                    Some((time_key, obs.outeq()))
190                                }
191                                _ => None,
192                            })
193                            .collect();
194
195                        // Generate new observation times
196                        let mut new_events = Vec::new();
197                        let mut time = 0.0;
198                        while time < last_time {
199                            let time_key = (time * 1e6).round() as u64;
200
201                            for &outeq in &outeq_values {
202                                // Only add if this (time, outeq) combination doesn't exist
203                                if !existing_obs.contains(&(time_key, outeq)) {
204                                    let obs = Observation::new(
205                                        time,
206                                        None,
207                                        outeq,
208                                        None,
209                                        occasion.index,
210                                        Censor::None,
211                                    );
212                                    new_events.push(Event::Observation(obs));
213                                }
214                            }
215
216                            time += idelta;
217                            time = (time * 1e6).round() / 1e6;
218                        }
219
220                        // Add original events
221                        new_events.extend(old_events);
222
223                        // Create new occasion and sort events
224                        let mut new_occasion = Occasion::new(occasion.index);
225                        new_occasion.events = new_events;
226                        new_occasion.covariates = occasion.covariates.clone();
227
228                        new_occasion.sort();
229                        new_occasion
230                    })
231                    .collect();
232
233                Subject::new(subject.id.clone(), new_occasions)
234            })
235            .collect();
236
237        Data::new(new_subjects)
238    }
239
240    /// Get an iterator over all subjects
241    ///
242    /// # Returns
243    ///
244    /// An iterator yielding references to subjects
245    pub fn iter(&'_ self) -> std::slice::Iter<'_, Subject> {
246        self.subjects.iter()
247    }
248
249    /// Get a mutable iterator over all subjects
250    ///
251    /// # Returns
252    ///
253    /// A mutable iterator yielding references to subjects
254    pub fn iter_mut(&'_ mut self) -> std::slice::IterMut<'_, Subject> {
255        self.subjects.iter_mut()
256    }
257
258    /// Get the number of subjects in the dataset
259    ///
260    /// # Returns
261    ///
262    /// The number of subjects
263    pub fn len(&self) -> usize {
264        self.subjects.len()
265    }
266
267    /// Check if the dataset is empty
268    ///
269    /// # Returns
270    ///
271    /// `true` if there are no subjects, `false` otherwise
272    pub fn is_empty(&self) -> bool {
273        self.subjects.is_empty()
274    }
275
276    /// Get a vector of all unique output equations (outeq) across all subjects
277    pub fn get_output_equations(&self) -> Vec<usize> {
278        // Collect all unique outeq values in order of occurrence
279        let mut outeq_values: Vec<usize> = self
280            .subjects
281            .iter()
282            .flat_map(|subject| subject.get_output_equations())
283            .collect();
284        outeq_values.sort_unstable();
285        outeq_values.dedup();
286        outeq_values
287    }
288}
289
290impl IntoIterator for Data {
291    type Item = Subject;
292    type IntoIter = std::vec::IntoIter<Subject>;
293    /// Consumes the data and yields owned subjects
294    fn into_iter(self) -> Self::IntoIter {
295        self.subjects.into_iter()
296    }
297}
298
299impl<'a> IntoIterator for &'a Data {
300    type Item = &'a Subject;
301    type IntoIter = std::slice::Iter<'a, Subject>;
302    /// Iterate immutably over all subjects in the dataset
303    fn into_iter(self) -> Self::IntoIter {
304        self.subjects.iter()
305    }
306}
307impl<'a> IntoIterator for &'a mut Data {
308    type Item = &'a mut Subject;
309    type IntoIter = std::slice::IterMut<'a, Subject>;
310    /// Iterate mutably over all subjects in the dataset
311    fn into_iter(self) -> Self::IntoIter {
312        self.subjects.iter_mut()
313    }
314}
315
316impl Into<Data> for Vec<Subject> {
317    /// Convert a vector of subjects into a Data object
318    fn into(self) -> Data {
319        Data::new(self)
320    }
321}
322
323impl Into<Data> for Subject {
324    /// Convert a subject into a Data object
325    fn into(self) -> Data {
326        Data::new(vec![self])
327    }
328}
329
330/// A subject in a pharmacometric dataset
331///
332/// A [Subject] represents a single individual with one or more occasions of data,
333/// each containing events (doses, observations) and covariates.
334#[derive(Serialize, Debug, Deserialize, Clone)]
335pub struct Subject {
336    id: String,
337    occasions: Vec<Occasion>,
338}
339impl Subject {
340    /// Create a new subject with the given ID and occasions
341    ///
342    /// # Arguments
343    ///
344    /// * `id` - The subject identifier
345    /// * `occasions` - Vector of occasions for this subject
346    pub(crate) fn new(id: String, occasions: Vec<Occasion>) -> Self {
347        let mut subject = Subject { id, occasions };
348        for occasion in subject.occasions.iter_mut() {
349            occasion.sort();
350        }
351        subject
352    }
353
354    /// Get a vector of references to all occasions for this subject
355    ///
356    /// # Returns
357    ///
358    /// Vector of references to all occasions
359    pub fn occasions(&self) -> Vec<&Occasion> {
360        self.occasions.iter().collect()
361    }
362
363    /// Get the ID of the subject
364    ///
365    /// # Returns
366    ///
367    /// The subject's identifier
368    pub fn id(&self) -> &String {
369        &self.id
370    }
371
372    /// Create a new subject from one or more occasions
373    ///
374    /// This is useful when you want to create a subject from specific occasions
375    /// rather than building it completely from scratch.
376    ///
377    /// # Arguments
378    ///
379    /// * `id` - The subject identifier
380    /// * `occasions` - Vector of occasions to include in this subject
381    ///
382    /// # Returns
383    ///
384    /// A new subject containing the specified occasions
385    pub fn from_occasions(id: String, occasions: Vec<Occasion>) -> Self {
386        Subject { id, occasions }
387    }
388
389    /// Iterate over a mutable reference to the occasions
390    pub fn occasions_mut(&mut self) -> &mut Vec<Occasion> {
391        &mut self.occasions
392    }
393
394    /// Get a mutable iterator to the occasions
395    pub fn occasions_iter_mut(&'_ mut self) -> std::slice::IterMut<'_, Occasion> {
396        self.occasions.iter_mut()
397    }
398
399    pub fn get_output_equations(&self) -> Vec<usize> {
400        // Collect all unique outeq values in order of occurrence
401        let outeq_values: Vec<usize> = self
402            .occasions
403            .iter()
404            .flat_map(|occasion| {
405                occasion.events.iter().filter_map(|event| match event {
406                    Event::Observation(obs) => Some(obs.outeq()),
407                    _ => None,
408                })
409            })
410            .collect();
411        outeq_values
412    }
413
414    /// Get a mutable reference to an occasion by index
415    ///
416    /// # Arguments
417    ///
418    /// * `index` - The index of the occasion to retrieve
419    ///
420    /// # Returns
421    ///
422    /// An `Option` containing a mutable reference to the occasion if found, or `None` if not found
423    pub fn get_occasion_mut(&mut self, index: usize) -> Option<&mut Occasion> {
424        self.occasions.iter_mut().find(|occ| occ.index() == index)
425    }
426
427    /// Get an iterator over all occasions
428    ///
429    /// # Returns
430    ///
431    /// An iterator yielding references to occasions
432    pub fn iter(&'_ self) -> std::slice::Iter<'_, Occasion> {
433        self.occasions.iter()
434    }
435
436    /// Get a mutable iterator over all occasions
437    ///
438    /// # Returns
439    ///
440    /// A mutable iterator yielding references to occasions
441    pub fn iter_mut(&'_ mut self) -> std::slice::IterMut<'_, Occasion> {
442        self.occasions.iter_mut()
443    }
444
445    /// Get the number of occasions for this subject
446    ///
447    /// # Returns
448    ///
449    /// The number of occasions
450    pub fn len(&self) -> usize {
451        self.occasions.len()
452    }
453
454    /// Check if the subject has any occasions
455    ///
456    /// # Returns
457    ///
458    /// `true` if there are no occasions, `false` otherwise
459    pub fn is_empty(&self) -> bool {
460        self.occasions.is_empty()
461    }
462}
463
464impl IntoIterator for Subject {
465    type Item = Occasion;
466    type IntoIter = std::vec::IntoIter<Occasion>;
467    /// Consumes the subject and yields owned occasions
468    fn into_iter(self) -> Self::IntoIter {
469        self.occasions.into_iter()
470    }
471}
472impl<'a> IntoIterator for &'a Subject {
473    type Item = &'a Occasion;
474    type IntoIter = std::slice::Iter<'a, Occasion>;
475    /// Iterate immutably over all occasions in the subject
476    fn into_iter(self) -> Self::IntoIter {
477        self.occasions.iter()
478    }
479}
480impl<'a> IntoIterator for &'a mut Subject {
481    type Item = &'a mut Occasion;
482    type IntoIter = std::slice::IterMut<'a, Occasion>;
483    /// Iterate mutably over all occasions in the subject
484    fn into_iter(self) -> Self::IntoIter {
485        self.occasions.iter_mut()
486    }
487}
488
489/// An occasion within a subject's dataset
490///
491/// An [Occasion] represents a distinct period of data collection for a subject,
492/// such as a hospital visit or dosing regimen. It contains events (doses, observations)
493/// and time-varying covariates.
494#[derive(Serialize, Debug, Deserialize, Clone)]
495pub struct Occasion {
496    pub(crate) events: Vec<Event>,
497    pub(crate) covariates: Covariates,
498    pub(crate) index: usize,
499}
500
501impl Occasion {
502    /// Create a new occasion
503    ///
504    /// # Arguments
505    ///
506    /// * `events` - Vector of events for this occasion
507    /// * `covariates` - Covariates for this occasion
508    /// * `index` - The occasion index (0-based)
509    pub(crate) fn new(index: usize) -> Self {
510        Occasion {
511            events: Vec::new(),
512            covariates: Covariates::new(),
513            index,
514        }
515    }
516
517    /// Get a vector of references to all events in this occasion
518    ///
519    /// # Returns
520    ///
521    /// Vector of references to all events
522    pub fn events(&self) -> Vec<&Event> {
523        self.events.iter().collect()
524    }
525
526    /// Get the index of the occasion
527    ///
528    /// # Returns
529    ///
530    /// The occasion index (0-based)
531    pub fn index(&self) -> usize {
532        self.index
533    }
534
535    /// Add a covariate to this occasion
536    ///
537    /// # Arguments
538    ///
539    /// * `name` - Name of the covariate
540    /// * `covariate` - The covariate to add
541    pub fn add_covariate(&mut self, name: String, covariate: Covariate) {
542        self.covariates.add_covariate(name, covariate);
543    }
544
545    /// Set covariates for this occasion
546    pub(crate) fn set_covariates(&mut self, covariates: Covariates) {
547        self.covariates = covariates;
548    }
549
550    fn add_lagtime(&mut self, reorder: Option<(&Fa, &Lag, &Vec<f64>, &Covariates)>) {
551        if let Some((_, fn_lag, spp, covariates)) = reorder {
552            let spp = nalgebra::DVector::from_vec(spp.to_vec());
553            for event in self.events.iter_mut() {
554                let time = event.time();
555                if let Event::Bolus(bolus) = event {
556                    let lagtime = fn_lag(&spp.clone().into(), time, covariates);
557                    if let Some(l) = lagtime.get(&bolus.input()) {
558                        *bolus.mut_time() += l;
559                    }
560                }
561            }
562        }
563        self.sort();
564    }
565
566    fn add_bioavailability(&mut self, reorder: Option<(&Fa, &Lag, &Vec<f64>, &Covariates)>) {
567        // If lagtime is empty, return early
568        if let Some((fn_fa, _, spp, covariates)) = reorder {
569            let spp = nalgebra::DVector::from_vec(spp.to_vec());
570            for event in self.events.iter_mut() {
571                let time = event.time();
572                if let Event::Bolus(bolus) = event {
573                    let fa = fn_fa(&spp.clone().into(), time, covariates);
574                    if let Some(f) = fa.get(&bolus.input()) {
575                        bolus.set_amount(bolus.amount() * f);
576                    }
577                }
578            }
579        }
580    }
581
582    /// Sort events by time, then by [Event] type so that [Bolus] and [Infusion] come before [Observation]
583    pub(crate) fn sort(&mut self) {
584        self.events.sort_by(|a, b| {
585            // Helper function to get event type order
586            #[inline]
587            fn event_type_order(event: &Event) -> u8 {
588                match event {
589                    Event::Bolus(_) => 1,
590                    Event::Infusion(_) => 2,
591                    Event::Observation(_) => 3,
592                }
593            }
594
595            // Compare times first using the existing time() method
596            let time_cmp = a.time().partial_cmp(&b.time());
597
598            match time_cmp {
599                Some(std::cmp::Ordering::Equal) => {
600                    // If times are equal, sort by event type
601                    event_type_order(a).cmp(&event_type_order(b))
602                }
603                Some(ordering) => ordering,
604                None => std::cmp::Ordering::Equal, // Handle NaN cases
605            }
606        });
607    }
608
609    /// Process the events with modifications for lag time, bioavailability and input remapping.
610    ///
611    /// # Arguments
612    ///
613    /// * `reorder` - Optional tuple containing references to (Fa, Lag, support point, covariates) for adjustments
614    /// * `ignore` - If true, filter out events marked as ignore
615    /// * `mappings` - Optional reference to an [equation::Mapper] for input remapping
616    ///
617    /// # Returns
618    ///
619    /// Vector of events, potentially filtered and with times adjusted for lag and bioavailability
620    pub(crate) fn process_events(
621        &self,
622        reorder: Option<(&Fa, &Lag, &Vec<f64>, &Covariates)>,
623        ignore: bool,
624    ) -> Vec<Event> {
625        let mut occ = self.clone();
626        occ.add_lagtime(reorder);
627        occ.add_bioavailability(reorder);
628
629        // Filter out events that are marked as ignore
630        if ignore {
631            occ.events.iter().cloned().collect()
632        } else {
633            occ.events.clone()
634        }
635    }
636
637    /// Get a reference to the  covariates for this occasion
638    ///
639    /// # Returns
640    ///
641    /// Reference to the occasion's covariates, if any
642    pub fn covariates(&self) -> &Covariates {
643        &self.covariates
644    }
645
646    /// Get a mutable refernce to the covariates for this occasion
647    ///
648    /// # Returns
649    ///
650    /// Reference to the occasion's covariates, if any
651    pub fn covariates_mut(&mut self) -> &mut Covariates {
652        &mut self.covariates
653    }
654
655    /// Add an event to the [Occasion]
656    ///
657    /// Note that this will sort the events automatically, ensuring events are sorted by time, then by [Event] type so that [Bolus] and [Infusion] come before [Observation]
658    pub(crate) fn add_event(&mut self, event: Event) {
659        self.events.push(event);
660        self.sort();
661    }
662
663    /// Add an [Observation] event to the [Occasion]
664    pub fn add_observation(
665        &mut self,
666        time: f64,
667        value: f64,
668        outeq: usize,
669        errorpoly: Option<ErrorPoly>,
670        censored: Censor,
671    ) {
672        let observation =
673            Observation::new(time, Some(value), outeq, errorpoly, self.index, censored);
674        self.add_event(Event::Observation(observation));
675    }
676
677    /// Add a missing [Observation] event to the [Occasion]
678    pub fn add_missing_observation(&mut self, time: f64, outeq: usize) {
679        let observation = Observation::new(time, None, outeq, None, self.index, Censor::None);
680        self.add_event(Event::Observation(observation));
681    }
682
683    /// Add a missing [Observation] with a custom [ErrorPoly] to the [Occasion]
684    ///
685    /// This is useful if you want a different weight for the observation
686    pub fn add_observation_with_error(
687        &mut self,
688        time: f64,
689        value: f64,
690        outeq: usize,
691        errorpoly: ErrorPoly,
692        censored: Censor,
693    ) {
694        let observation = Observation::new(
695            time,
696            Some(value),
697            outeq,
698            Some(errorpoly),
699            self.index,
700            censored,
701        );
702        self.add_event(Event::Observation(observation));
703    }
704
705    /// Add a [Bolus] event to the [Occasion]
706    pub fn add_bolus(&mut self, time: f64, amount: f64, input: usize) {
707        let bolus = Bolus::new(time, amount, input, self.index);
708        self.add_event(Event::Bolus(bolus));
709    }
710
711    /// Add an [Infusion] event to the [Occasion]
712    pub fn add_infusion(&mut self, time: f64, amount: f64, input: usize, duration: f64) {
713        let infusion = Infusion::new(time, amount, input, duration, self.index);
714        self.add_event(Event::Infusion(infusion));
715    }
716
717    /// Get a mutable reference to the events
718    pub fn events_mut(&mut self) -> &mut Vec<Event> {
719        &mut self.events
720    }
721
722    /// Get a mutable iterator to the events
723    pub fn events_iter_mut(&'_ mut self) -> std::slice::IterMut<'_, Event> {
724        self.events.iter_mut()
725    }
726
727    pub(crate) fn initial_time(&self) -> f64 {
728        //TODO this can be pre-computed when the struct is initially created
729        self.events
730            .iter()
731            .filter_map(|event| match event {
732                Event::Observation(observation) => Some(observation.time()),
733                Event::Bolus(bolus) => Some(bolus.time()),
734                Event::Infusion(infusion) => Some(infusion.time()),
735            })
736            .min_by(|a, b| a.partial_cmp(b).unwrap())
737            .unwrap_or(0.0)
738    }
739
740    pub(crate) fn infusions_ref(&self) -> Vec<&Infusion> {
741        //TODO this can be pre-computed when the struct is initially created
742        self.events
743            .iter()
744            .filter_map(|event| match event {
745                Event::Infusion(infusion) => Some(infusion),
746                _ => None,
747            })
748            .collect()
749    }
750
751    /// Get an iterator over all events
752    ///
753    /// # Returns
754    ///
755    /// An iterator yielding references to events
756    pub fn iter(&'_ self) -> std::slice::Iter<'_, Event> {
757        self.events.iter()
758    }
759
760    /// Get a mutable iterator over all events
761    ///
762    /// # Returns
763    ///
764    /// A mutable iterator yielding references to events
765    pub fn iter_mut(&'_ mut self) -> std::slice::IterMut<'_, Event> {
766        self.events.iter_mut()
767    }
768
769    /// Get the number of events in this occasion
770    ///
771    /// # Returns
772    ///
773    /// The number of events
774    pub fn len(&self) -> usize {
775        self.events.len()
776    }
777
778    /// Check if the occasion has any events
779    ///
780    /// # Returns
781    ///
782    /// `true` if there are no events, `false` otherwise
783    pub fn is_empty(&self) -> bool {
784        self.events.is_empty()
785    }
786}
787
788impl IntoIterator for Occasion {
789    type Item = Event;
790    type IntoIter = std::vec::IntoIter<Event>;
791    /// Consumes the occasion and yields owned events
792    fn into_iter(self) -> Self::IntoIter {
793        self.events.into_iter()
794    }
795}
796impl<'a> IntoIterator for &'a Occasion {
797    type Item = &'a Event;
798    type IntoIter = std::slice::Iter<'a, Event>;
799    /// Iterate immutably over all events in the occasion
800    fn into_iter(self) -> Self::IntoIter {
801        self.events.iter()
802    }
803}
804impl<'a> IntoIterator for &'a mut Occasion {
805    type Item = &'a mut Event;
806    type IntoIter = std::slice::IterMut<'a, Event>;
807    /// Iterate mutably over all events in the occasion
808    fn into_iter(self) -> Self::IntoIter {
809        self.events.iter_mut()
810    }
811}
812
813// For Event, IntoIterator yields a single reference to self (for & and &mut)
814impl<'a> IntoIterator for &'a Event {
815    type Item = &'a Event;
816    type IntoIter = std::option::IntoIter<&'a Event>;
817    /// Yields a single reference to the event
818    fn into_iter(self) -> Self::IntoIter {
819        Some(self).into_iter()
820    }
821}
822impl<'a> IntoIterator for &'a mut Event {
823    type Item = &'a mut Event;
824    type IntoIter = std::option::IntoIter<&'a mut Event>;
825    /// Yields a single mutable reference to the event
826    fn into_iter(self) -> Self::IntoIter {
827        Some(self).into_iter()
828    }
829}
830
831impl fmt::Display for Data {
832    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
833        writeln!(f, "Data Overview: {} subjects", self.subjects.len())?;
834        for subject in &self.subjects {
835            writeln!(f, "{}", subject)?;
836        }
837        Ok(())
838    }
839}
840impl fmt::Display for Subject {
841    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
842        writeln!(f, "Subject ID: {}", self.id)?;
843        for occasion in &self.occasions {
844            writeln!(f, "{}", occasion)?;
845        }
846        Ok(())
847    }
848}
849impl fmt::Display for Occasion {
850    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
851        writeln!(f, "Occasion {}:", self.index)?;
852        for event in &self.events {
853            writeln!(f, "  {}", event)?;
854        }
855
856        writeln!(f, "  Covariates:\n{}", self.covariates)?;
857        Ok(())
858    }
859}
860
861#[cfg(test)]
862mod tests {
863    use super::*;
864    use crate::prelude::*;
865
866    fn create_sample_data() -> Data {
867        let subject1 = Subject::builder("subject1")
868            .observation(1.0, 10.0, 1)
869            .bolus(2.0, 50.0, 1)
870            .infusion(3.0, 100.0, 1, 1.0)
871            .covariate("age", 0.0, 30.0)
872            .covariate("weight", 0.0, 70.0)
873            .reset()
874            .observation(4.0, 20.0, 2)
875            .bolus(5.0, 60.0, 2)
876            .infusion(6.0, 120.0, 2, 2.0)
877            .covariate("age", 0.0, 31.0)
878            .covariate("weight", 0.0, 75.0)
879            .build();
880
881        let subject2 = Subject::builder("subject2")
882            .observation(1.5, 15.0, 1)
883            .bolus(2.5, 55.0, 1)
884            .infusion(3.5, 110.0, 1, 1.5)
885            .covariate("age", 0.0, 25.0)
886            .covariate("weight", 0.0, 65.0)
887            .reset()
888            .observation(4.5, 25.0, 2)
889            .bolus(5.5, 65.0, 2)
890            .infusion(6.5, 130.0, 2, 2.5)
891            .covariate("age", 0.0, 26.0)
892            .covariate("weight", 0.0, 68.0)
893            .build();
894
895        Data::new(vec![subject1, subject2])
896    }
897
898    #[test]
899    fn test_new_data() {
900        let data = create_sample_data();
901        assert_eq!(data.len(), 2);
902    }
903
904    #[test]
905    fn test_get_subjects() {
906        let data = create_sample_data();
907        let subjects = data.subjects();
908        assert_eq!(subjects.len(), 2);
909        assert_eq!(subjects[0].id(), "subject1");
910        assert_eq!(subjects[1].id(), "subject2");
911    }
912
913    #[test]
914    fn test_add_subject() {
915        let mut data = create_sample_data();
916        let new_subject = Subject::builder("subject3")
917            .observation(1.0, 10.0, 1)
918            .bolus(2.0, 50.0, 1)
919            .infusion(3.0, 100.0, 1, 1.0)
920            .covariate("age", 0.0, 30.0)
921            .covariate("weight", 0.0, 70.0)
922            .build();
923        data.add_subject(new_subject);
924        assert_eq!(data.len(), 3);
925        assert_eq!(data.subjects()[2].id(), "subject3");
926    }
927
928    #[test]
929    fn test_filter_include() {
930        let data = create_sample_data();
931        let include = vec!["subject1".to_string()];
932        let filtered_data = data.filter_include(&include);
933        assert_eq!(filtered_data.subjects().len(), 1);
934        assert_eq!(filtered_data.subjects()[0].id(), "subject1");
935    }
936
937    #[test]
938    fn test_filter_exclude() {
939        let data = create_sample_data();
940        let filtered_data = data.filter_exclude(vec!["subject1".to_string()]);
941        assert_eq!(filtered_data.len(), 1);
942        assert_eq!(filtered_data.subjects()[0].id(), "subject2");
943    }
944
945    #[test]
946    fn test_occasion_sort() {
947        let mut occasion = Occasion::new(0);
948        occasion.add_observation(2.0, 1.0, 1, None, Censor::None);
949        occasion.add_bolus(1.0, 100.0, 1);
950        occasion.sort();
951        let events = occasion.process_events(None, false);
952        match &events[0] {
953            Event::Bolus(b) => assert_eq!(b.time(), 1.0),
954            _ => panic!("First event should be a Bolus"),
955        }
956        match &events[1] {
957            Event::Observation(o) => assert_eq!(o.time(), 2.0),
958            _ => panic!("Second event should be an Observation"),
959        }
960    }
961
962    #[test]
963    fn test_data_iterators() {
964        let data = create_sample_data();
965        let mut count = 0;
966        for subject in data.iter() {
967            assert!(subject.id().starts_with("subject"));
968            count += 1;
969        }
970        assert_eq!(count, 2);
971
972        let mut data = create_sample_data();
973        for subject in data.iter_mut() {
974            subject.occasions_mut().push(Occasion::new(2));
975        }
976        assert_eq!(data.subjects()[0].occasions().len(), 3);
977    }
978
979    #[test]
980    fn test_subject_iterators() {
981        let subject = Subject::builder("test")
982            .observation(1.0, 10.0, 1)
983            .bolus(2.0, 50.0, 1)
984            .reset()
985            .observation(3.0, 20.0, 1)
986            .build();
987
988        let mut count = 0;
989        for occasion in subject.iter() {
990            assert!(occasion.index() < 2);
991            count += 1;
992        }
993        assert_eq!(count, 2);
994
995        let mut subject = subject;
996        for occasion in subject.iter_mut() {
997            occasion.add_observation(12.0, 100.0, 0, None, Censor::None);
998        }
999        assert_eq!(subject.occasions()[0].events().len(), 3);
1000    }
1001
1002    #[test]
1003    fn test_occasion_iterators() {
1004        let mut occasion = Occasion::new(0);
1005        occasion.add_observation(1.0, 10.0, 1, None, Censor::None);
1006        occasion.add_bolus(2.0, 50.0, 1);
1007        occasion.sort();
1008
1009        let mut count = 0;
1010        for event in occasion.iter() {
1011            match event {
1012                Event::Observation(_) => count += 1,
1013                Event::Bolus(_) => count += 2,
1014                _ => panic!("Unexpected event type"),
1015            }
1016        }
1017        assert_eq!(count, 3);
1018
1019        let mut occasion = occasion;
1020        for event in occasion.iter_mut() {
1021            event.inc_time(1.0);
1022        }
1023        assert_eq!(occasion.events()[0].time(), 2.0);
1024    }
1025
1026    #[test]
1027    fn test_data_intoiterator_refs() {
1028        let data = create_sample_data();
1029        let mut count = 0;
1030        for subject in &data {
1031            assert!(subject.id().starts_with("subject"));
1032            count += 1;
1033        }
1034        assert_eq!(count, 2);
1035        let mut data = create_sample_data();
1036        for subject in &mut data {
1037            subject.occasions_mut().push(Occasion::new(2));
1038        }
1039        assert_eq!(data.subjects()[0].occasions().len(), 3);
1040    }
1041    #[test]
1042    fn test_subject_intoiterator_all_forms() {
1043        let data = create_sample_data();
1044        let subject = data.get_subject("subject1").unwrap().clone();
1045
1046        // Test owned iterator - consumes the subject
1047        let mut occasion_count = 0;
1048        let mut total_events = 0;
1049        for occasion in subject.clone() {
1050            assert_eq!(occasion.index(), occasion_count);
1051            total_events += occasion.events().len();
1052            occasion_count += 1;
1053        }
1054        assert_eq!(occasion_count, 2); // subject1 has 2 occasions
1055        assert_eq!(total_events, 6); // 3 events per occasion (obs + bolus + infusion)
1056
1057        // Test immutable reference iterator
1058        let mut covariate_ages = Vec::new();
1059        for occasion in &subject {
1060            if let Some(age_cov) = occasion.covariates().get_covariate("age") {
1061                if let Ok(age_value) = age_cov.interpolate(0.1) {
1062                    covariate_ages.push(age_value);
1063                }
1064            }
1065        }
1066        assert_eq!(covariate_ages, vec![30.0, 31.0]); // Ages from sample data
1067
1068        // Test mutable reference iterator - add missing observations
1069        let mut subject_mut = subject;
1070        for occasion in &mut subject_mut {
1071            occasion.add_missing_observation(10.0, 1); // Add observation at t=10 for outeq 1
1072        }
1073
1074        // Verify we added observations to both occasions
1075        assert_eq!(subject_mut.occasions()[0].events().len(), 4);
1076        assert_eq!(subject_mut.occasions()[1].events().len(), 4);
1077    }
1078    #[test]
1079    fn test_occasion_intoiterator_all_forms() {
1080        let data = create_sample_data();
1081        let subject = data.get_subject("subject2").unwrap();
1082        let occasion = subject.occasions()[0].clone(); // Clone to get owned occasion
1083
1084        // Test owned iterator - verify event types and ordering
1085        let mut event_types = Vec::new();
1086        let mut event_times = Vec::new();
1087        for event in occasion.clone() {
1088            event_times.push(event.time());
1089            match event {
1090                Event::Observation(_) => event_types.push("obs"),
1091                Event::Bolus(_) => event_types.push("bolus"),
1092                Event::Infusion(_) => event_types.push("infusion"),
1093            }
1094        }
1095        // Should be sorted by time, then by event type priority
1096        assert_eq!(event_times, vec![1.5, 2.5, 3.5]);
1097        assert_eq!(event_types, vec!["obs", "bolus", "infusion"]);
1098
1099        // Test immutable reference iterator - calculate total dose
1100        let mut total_dose = 0.0;
1101        for event in &occasion {
1102            match event {
1103                Event::Bolus(bolus) => total_dose += bolus.amount(),
1104                Event::Infusion(infusion) => total_dose += infusion.amount(),
1105                _ => {}
1106            }
1107        }
1108        assert_eq!(total_dose, 165.0); // 55.0 (bolus) + 110.0 (infusion)
1109
1110        // Test mutable reference iterator - shift all event times by 1 hour
1111        let mut occasion_mut = occasion;
1112        for event in &mut occasion_mut {
1113            event.inc_time(1.0);
1114        }
1115
1116        // Verify all times were shifted
1117        let shifted_times: Vec<f64> = occasion_mut.events().iter().map(|e| e.time()).collect();
1118        assert_eq!(shifted_times, vec![2.5, 3.5, 4.5]);
1119    }
1120    #[test]
1121    fn test_event_intoiterator_refs() {
1122        let data = create_sample_data();
1123        let subject = data.get_subject("subject1").unwrap();
1124        let occasion = &subject.occasions()[0];
1125
1126        // Get a bolus event from sample data and test immutable reference iterator
1127        if let Some(bolus_event) = occasion
1128            .events()
1129            .iter()
1130            .find(|e| matches!(e, Event::Bolus(_)))
1131        {
1132            let mut event_count = 0;
1133            for event in *bolus_event {
1134                assert_eq!(event.time(), 2.0); // Bolus time from sample data
1135                assert!(matches!(event, Event::Bolus(_)));
1136                if let Event::Bolus(bolus) = event {
1137                    assert_eq!(bolus.amount(), 50.0); // Amount from sample data
1138                    assert_eq!(bolus.input(), 1); // Input compartment 1
1139                }
1140                event_count += 1;
1141            }
1142            assert_eq!(event_count, 1);
1143        }
1144
1145        // Test mutable reference iterator on a new event
1146        let mut infusion_event = Event::Infusion(Infusion::new(5.0, 200.0, 1, 2.0, 2));
1147        let original_time = infusion_event.time();
1148
1149        for event in &mut infusion_event {
1150            event.inc_time(3.0); // Increase time by 3 hours
1151        }
1152
1153        assert_eq!(infusion_event.time(), original_time + 3.0);
1154
1155        // Test with observation event from sample data
1156        if let Some(obs_event) = occasion
1157            .events()
1158            .iter()
1159            .find(|e| matches!(e, Event::Observation(_)))
1160        {
1161            for event in *obs_event {
1162                assert_eq!(event.time(), 1.0); // Observation time from sample data
1163                if let Event::Observation(observation) = event {
1164                    assert_eq!(observation.value(), Some(10.0)); // Value from sample data
1165                    assert_eq!(observation.outeq(), 1); // Output equation 1
1166                }
1167            }
1168        }
1169    }
1170
1171    #[test]
1172    fn test_subject_builder_and_data_modification() {
1173        // Create a subject with one bolus and three observations using the builder
1174        let mut subject = Subject::builder("test_subject")
1175            .bolus(0.0, 100.0, 1)
1176            .missing_observation(0.0, 1)
1177            .missing_observation(1.0, 1)
1178            .missing_observation(3.0, 1)
1179            .build();
1180
1181        // Verify initial setup
1182        assert_eq!(subject.id(), "test_subject");
1183        assert_eq!(subject.occasions().len(), 1);
1184
1185        let occasions = subject.occasions();
1186        let occasion = &occasions.first().unwrap();
1187        assert_eq!(occasion.events().len(), 4); // 1 bolus + 3 observations
1188
1189        // Find the bolus and verify initial dose
1190        if let Some(Event::Bolus(bolus)) = occasion
1191            .events()
1192            .iter()
1193            .find(|e| matches!(e, Event::Bolus(_)))
1194        {
1195            assert_eq!(bolus.amount(), 100.0);
1196            assert_eq!(bolus.time(), 0.0);
1197            assert_eq!(bolus.input(), 1);
1198        } else {
1199            panic!("Bolus event not found");
1200        }
1201
1202        // Count observations with None values
1203        let none_obs_count = occasion
1204            .events()
1205            .iter()
1206            .filter(|e| matches!(e, Event::Observation(obs) if obs.value().is_none()))
1207            .count();
1208        assert_eq!(none_obs_count, 3);
1209
1210        // Edit the data: double the dose
1211        let occasion_mut = subject.get_occasion_mut(0).unwrap();
1212        for event in occasion_mut.events_iter_mut() {
1213            if let Event::Bolus(bolus) = event {
1214                let dose = bolus.mut_amount();
1215                *dose *= 2.0; // Double the dose
1216            }
1217        }
1218
1219        // Add an observation at time 12 with value None
1220        occasion_mut.add_missing_observation(12.0, 1);
1221
1222        // Verify the modifications
1223        let occasion = &subject.occasions()[0];
1224        assert_eq!(occasion.events().len(), 5); // 1 bolus + 4 observations
1225
1226        // Verify doubled dose
1227        if let Some(Event::Bolus(bolus)) = occasion
1228            .events()
1229            .iter()
1230            .find(|e| matches!(e, Event::Bolus(_)))
1231        {
1232            assert_eq!(bolus.amount(), 200.0); // Should be doubled
1233        } else {
1234            panic!("Bolus event not found after modification");
1235        }
1236
1237        // Verify the new observation at time 12
1238        if let Some(Event::Observation(obs)) = occasion
1239            .events()
1240            .iter()
1241            .find(|e| matches!(e, Event::Observation(obs) if obs.time() == 12.0))
1242        {
1243            assert_eq!(obs.time(), 12.0);
1244            assert_eq!(obs.value(), None);
1245            assert_eq!(obs.outeq(), 1);
1246        } else {
1247            panic!("Observation at time 12 not found");
1248        }
1249
1250        // Verify all observations still have None values
1251        let none_obs_count = occasion
1252            .events()
1253            .iter()
1254            .filter(|e| matches!(e, Event::Observation(obs) if obs.value().is_none()))
1255            .count();
1256        assert_eq!(none_obs_count, 4); // Should now be 4 observations with None values
1257    }
1258}