1use crate::phenotype::{FamilyId, PhenotypeId};
2use std::collections::HashMap;
3#[derive(Clone, Debug)]
7pub enum LineageUpdate {
8 Mutate {
9 family: FamilyId,
10 parent: PhenotypeId,
11 child: PhenotypeId,
12 },
13 Crossover {
14 families: (FamilyId, FamilyId),
15 parents: (PhenotypeId, PhenotypeId),
16 child: PhenotypeId,
17 },
18 Replace {
19 reason: &'static str,
20 old: (FamilyId, PhenotypeId),
21 new: (FamilyId, PhenotypeId),
22 },
23 Invalid,
24}
25
26impl
27 From<(
28 (FamilyId, FamilyId),
29 (PhenotypeId, PhenotypeId),
30 PhenotypeId,
31 )> for LineageUpdate
32{
33 fn from(
34 (parent_lineages, parent_versions, child_id): (
35 (FamilyId, FamilyId),
36 (PhenotypeId, PhenotypeId),
37 PhenotypeId,
38 ),
39 ) -> Self {
40 if parent_versions.0 == child_id || parent_versions.1 == child_id {
41 return LineageUpdate::Invalid;
42 }
43
44 LineageUpdate::Crossover {
45 families: parent_lineages,
46 parents: parent_versions,
47 child: child_id,
48 }
49 }
50}
51
52impl From<((FamilyId, PhenotypeId), PhenotypeId)> for LineageUpdate {
53 fn from((parent_id, child_id): ((FamilyId, PhenotypeId), PhenotypeId)) -> Self {
54 if parent_id.1 == child_id {
55 return LineageUpdate::Invalid;
56 }
57
58 LineageUpdate::Mutate {
59 family: parent_id.0,
60 parent: parent_id.1,
61 child: child_id,
62 }
63 }
64}
65
66impl
67 From<(
68 &'static str,
69 (FamilyId, PhenotypeId),
70 (FamilyId, PhenotypeId),
71 )> for LineageUpdate
72{
73 fn from(
74 (reason, (old_family, old_id), (new_family, new_id)): (
75 &'static str,
76 (FamilyId, PhenotypeId),
77 (FamilyId, PhenotypeId),
78 ),
79 ) -> Self {
80 if old_id == new_id {
81 return LineageUpdate::Invalid;
82 }
83
84 LineageUpdate::Replace {
85 reason,
86 old: (old_family, old_id),
87 new: (new_family, new_id),
88 }
89 }
90}
91
92#[derive(Clone, Debug)]
93pub enum LineageEvent {
94 Mutate {
95 operator: &'static str,
96 family: FamilyId,
97 parent: PhenotypeId,
98 child: PhenotypeId,
99 },
100 Crossover {
101 operator: &'static str,
102 families: (FamilyId, FamilyId),
103 parents: (PhenotypeId, PhenotypeId),
104 child: PhenotypeId,
105 },
106 Replace {
107 reason: &'static str,
108 old: (FamilyId, PhenotypeId),
109 new: (FamilyId, PhenotypeId),
110 },
111}
112
113#[derive(Clone, Debug, Default)]
114pub struct LineageStats {
115 pub updates: usize,
116 pub parent_usage: HashMap<PhenotypeId, usize>,
117 pub family_usage: HashMap<FamilyId, usize>,
118 pub family_pairs: HashMap<(FamilyId, FamilyId), usize>,
119 pub cross_family_crossovers: usize,
120 pub within_family_crossovers: usize,
121}
122
123#[derive(Clone, Debug, Default)]
124pub struct Lineage {
125 stats: LineageStats,
126}
127
128impl Lineage {
129 pub fn rollover(&mut self) {
130 self.stats = LineageStats::default();
131 }
132
133 pub fn stats(&self) -> &LineageStats {
134 &self.stats
135 }
136
137 pub fn extend<I: IntoIterator<Item = impl Into<LineageUpdate>>>(
138 &mut self,
139 operation: &'static str,
140 events: I,
141 ) {
142 for event in events {
143 self.push(operation, event.into());
144 }
145 }
146
147 pub fn push(&mut self, operator: &'static str, update: LineageUpdate) {
148 match update {
149 LineageUpdate::Invalid => return,
150 LineageUpdate::Mutate {
151 family,
152 parent,
153 child,
154 } => {
155 *self.stats.parent_usage.entry(parent).or_insert(0) += 1;
156 *self.stats.family_usage.entry(family).or_insert(0) += 1;
157
158 LineageEvent::Mutate {
159 operator,
160 child,
161 family,
162 parent,
163 }
164 }
165 LineageUpdate::Crossover {
166 families,
167 parents,
168 child,
169 } => {
170 *self.stats.parent_usage.entry(parents.0).or_insert(0) += 1;
171 *self.stats.parent_usage.entry(parents.1).or_insert(0) += 1;
172 *self.stats.family_usage.entry(families.0).or_insert(0) += 1;
173 *self.stats.family_usage.entry(families.1).or_insert(0) += 1;
174
175 if families.0 == families.1 {
176 self.stats.within_family_crossovers += 1;
177 } else {
178 self.stats.cross_family_crossovers += 1;
179 };
180
181 let (a, b) = if families.0 <= families.1 {
182 (families.0, families.1)
183 } else {
184 (families.1, families.0)
185 };
186
187 *self.stats.family_pairs.entry((a, b)).or_insert(0) += 1;
188
189 LineageEvent::Crossover {
190 operator,
191 families,
192 parents,
193 child,
194 }
195 }
196 LineageUpdate::Replace { reason, old, new } => {
197 LineageEvent::Replace { reason, old, new }
198 }
199 };
200
201 self.stats.updates += 1;
202 }
203}