1pub fn natural_abundance(z: u32, a: u32) -> Option<f64> {
23 NATURAL_ABUNDANCES
24 .binary_search_by(|&(tz, ta, _)| (tz, ta).cmp(&(z, a)))
25 .ok()
26 .map(|idx| NATURAL_ABUNDANCES[idx].2)
27}
28
29pub fn natural_isotopes(z: u32) -> Vec<(u32, f64)> {
41 NATURAL_ABUNDANCES
42 .iter()
43 .filter(|&&(tz, _, _)| tz == z)
44 .map(|&(_, a, frac)| (a, frac))
45 .collect()
46}
47
48#[rustfmt::skip]
52static NATURAL_ABUNDANCES: &[(u32, u32, f64)] = &[
53 (1, 1, 0.999885),
55 (1, 2, 0.000115),
56
57 (2, 3, 0.00000134),
59 (2, 4, 0.999999),
60
61 (3, 6, 0.0759),
63 (3, 7, 0.9241),
64
65 (4, 9, 1.0),
67
68 (5, 10, 0.199),
70 (5, 11, 0.801),
71
72 (6, 12, 0.9893),
74 (6, 13, 0.0107),
75
76 (7, 14, 0.99636),
78 (7, 15, 0.00364),
79
80 (8, 16, 0.99757),
82 (8, 17, 0.00038),
83 (8, 18, 0.00205),
84
85 (9, 19, 1.0),
87
88 (10, 20, 0.9048),
90 (10, 21, 0.0027),
91 (10, 22, 0.0925),
92
93 (11, 23, 1.0),
95
96 (12, 24, 0.7899),
98 (12, 25, 0.1),
99 (12, 26, 0.1101),
100
101 (13, 27, 1.0),
103
104 (14, 28, 0.92223),
106 (14, 29, 0.04685),
107 (14, 30, 0.03092),
108
109 (15, 31, 1.0),
111
112 (16, 32, 0.9499),
114 (16, 33, 0.0075),
115 (16, 34, 0.0425),
116 (16, 36, 0.0001),
117
118 (17, 35, 0.7576),
120 (17, 37, 0.2424),
121
122 (18, 36, 0.003336),
124 (18, 38, 0.000629),
125 (18, 40, 0.996035),
126
127 (19, 39, 0.932581),
129 (19, 40, 0.000117),
130 (19, 41, 0.067302),
131
132 (20, 40, 0.96941),
134 (20, 42, 0.00647),
135 (20, 43, 0.00135),
136 (20, 44, 0.02086),
137 (20, 46, 0.00004),
138 (20, 48, 0.00187),
139
140 (21, 45, 1.0),
142
143 (22, 46, 0.0825),
145 (22, 47, 0.0744),
146 (22, 48, 0.7372),
147 (22, 49, 0.0541),
148 (22, 50, 0.0518),
149
150 (23, 50, 0.0025),
152 (23, 51, 0.9975),
153
154 (24, 50, 0.04345),
156 (24, 52, 0.83789),
157 (24, 53, 0.09501),
158 (24, 54, 0.02365),
159
160 (25, 55, 1.0),
162
163 (26, 54, 0.05845),
165 (26, 56, 0.91754),
166 (26, 57, 0.02119),
167 (26, 58, 0.00282),
168
169 (27, 59, 1.0),
171
172 (28, 58, 0.68077),
174 (28, 60, 0.26223),
175 (28, 61, 0.011399),
176 (28, 62, 0.036346),
177 (28, 64, 0.009255),
178
179 (29, 63, 0.6915),
181 (29, 65, 0.3085),
182
183 (30, 64, 0.4917),
185 (30, 66, 0.2773),
186 (30, 67, 0.0404),
187 (30, 68, 0.1845),
188 (30, 70, 0.0061),
189
190 (31, 69, 0.60108),
192 (31, 71, 0.39892),
193
194 (32, 70, 0.2057),
196 (32, 72, 0.2745),
197 (32, 73, 0.0775),
198 (32, 74, 0.365),
199 (32, 76, 0.0773),
200
201 (33, 75, 1.0),
203
204 (34, 74, 0.0089),
206 (34, 76, 0.0937),
207 (34, 77, 0.0763),
208 (34, 78, 0.2377),
209 (34, 80, 0.4961),
210 (34, 82, 0.0873),
211
212 (35, 79, 0.5069),
214 (35, 81, 0.4931),
215
216 (36, 78, 0.00355),
218 (36, 80, 0.02286),
219 (36, 82, 0.11593),
220 (36, 83, 0.115),
221 (36, 84, 0.56987),
222 (36, 86, 0.17279),
223
224 (37, 85, 0.7217),
226 (37, 87, 0.2783),
227
228 (38, 84, 0.0056),
230 (38, 86, 0.0986),
231 (38, 87, 0.07),
232 (38, 88, 0.8258),
233
234 (39, 89, 1.0),
236
237 (40, 90, 0.5145),
239 (40, 91, 0.1122),
240 (40, 92, 0.1715),
241 (40, 94, 0.1738),
242 (40, 96, 0.028),
243
244 (41, 93, 1.0),
246
247 (42, 92, 0.1453),
249 (42, 94, 0.0915),
250 (42, 95, 0.1584),
251 (42, 96, 0.1667),
252 (42, 97, 0.096),
253 (42, 98, 0.2439),
254 (42, 100, 0.0982),
255
256 (44, 96, 0.0554),
258 (44, 98, 0.0187),
259 (44, 99, 0.1276),
260 (44, 100, 0.126),
261 (44, 101, 0.1706),
262 (44, 102, 0.3155),
263 (44, 104, 0.1862),
264
265 (45, 103, 1.0),
267
268 (46, 102, 0.0102),
270 (46, 104, 0.1114),
271 (46, 105, 0.2233),
272 (46, 106, 0.2733),
273 (46, 108, 0.2646),
274 (46, 110, 0.1172),
275
276 (47, 107, 0.51839),
278 (47, 109, 0.48161),
279
280 (48, 106, 0.0125),
282 (48, 108, 0.0089),
283 (48, 110, 0.1249),
284 (48, 111, 0.128),
285 (48, 112, 0.2413),
286 (48, 113, 0.1222),
287 (48, 114, 0.2873),
288 (48, 116, 0.0749),
289
290 (49, 113, 0.0429),
292 (49, 115, 0.9571),
293
294 (50, 112, 0.0097),
296 (50, 114, 0.0066),
297 (50, 115, 0.0034),
298 (50, 116, 0.1454),
299 (50, 117, 0.0768),
300 (50, 118, 0.2422),
301 (50, 119, 0.0859),
302 (50, 120, 0.3258),
303 (50, 122, 0.0463),
304 (50, 124, 0.0579),
305
306 (51, 121, 0.5721),
308 (51, 123, 0.4279),
309
310 (52, 120, 0.0009),
312 (52, 122, 0.0255),
313 (52, 123, 0.0089),
314 (52, 124, 0.0474),
315 (52, 125, 0.0707),
316 (52, 126, 0.1884),
317 (52, 128, 0.3174),
318 (52, 130, 0.3408),
319
320 (53, 127, 1.0),
322
323 (54, 124, 0.000952),
325 (54, 126, 0.00089),
326 (54, 128, 0.019102),
327 (54, 129, 0.264006),
328 (54, 130, 0.04071),
329 (54, 131, 0.212324),
330 (54, 132, 0.269086),
331 (54, 134, 0.104357),
332 (54, 136, 0.088573),
333
334 (55, 133, 1.0),
336
337 (56, 130, 0.00106),
339 (56, 132, 0.00101),
340 (56, 134, 0.02417),
341 (56, 135, 0.06592),
342 (56, 136, 0.07854),
343 (56, 137, 0.11232),
344 (56, 138, 0.71698),
345
346 (57, 138, 0.0008881),
348 (57, 139, 0.999112),
349
350 (58, 136, 0.00185),
352 (58, 138, 0.00251),
353 (58, 140, 0.8845),
354 (58, 142, 0.11114),
355
356 (59, 141, 1.0),
358
359 (60, 142, 0.27152),
361 (60, 143, 0.12174),
362 (60, 144, 0.23798),
363 (60, 145, 0.08293),
364 (60, 146, 0.17189),
365 (60, 148, 0.05756),
366 (60, 150, 0.05638),
367
368 (62, 144, 0.0307),
370 (62, 147, 0.1499),
371 (62, 148, 0.1124),
372 (62, 149, 0.1382),
373 (62, 150, 0.0738),
374 (62, 152, 0.2675),
375 (62, 154, 0.2275),
376
377 (63, 151, 0.4781),
379 (63, 153, 0.5219),
380
381 (64, 152, 0.002),
383 (64, 154, 0.0218),
384 (64, 155, 0.148),
385 (64, 156, 0.2047),
386 (64, 157, 0.1565),
387 (64, 158, 0.2484),
388 (64, 160, 0.2186),
389
390 (65, 159, 1.0),
392
393 (66, 156, 0.00056),
395 (66, 158, 0.00095),
396 (66, 160, 0.02329),
397 (66, 161, 0.18889),
398 (66, 162, 0.25475),
399 (66, 163, 0.24896),
400 (66, 164, 0.2826),
401
402 (67, 165, 1.0),
404
405 (68, 162, 0.00139),
407 (68, 164, 0.01601),
408 (68, 166, 0.33503),
409 (68, 167, 0.22869),
410 (68, 168, 0.26978),
411 (68, 170, 0.1491),
412
413 (69, 169, 1.0),
415
416 (70, 168, 0.00123),
418 (70, 170, 0.02982),
419 (70, 171, 0.1409),
420 (70, 172, 0.2168),
421 (70, 173, 0.16103),
422 (70, 174, 0.32026),
423 (70, 176, 0.12996),
424
425 (71, 175, 0.97401),
427 (71, 176, 0.02599),
428
429 (72, 174, 0.0016),
431 (72, 176, 0.0526),
432 (72, 177, 0.186),
433 (72, 178, 0.2728),
434 (72, 179, 0.1362),
435 (72, 180, 0.3508),
436
437 (73, 180, 0.0001201),
439 (73, 181, 0.99988),
440
441 (74, 180, 0.0012),
443 (74, 182, 0.265),
444 (74, 183, 0.1431),
445 (74, 184, 0.3064),
446 (74, 186, 0.2843),
447
448 (75, 185, 0.374),
450 (75, 187, 0.626),
451
452 (76, 184, 0.0002),
454 (76, 186, 0.0159),
455 (76, 187, 0.0196),
456 (76, 188, 0.1324),
457 (76, 189, 0.1615),
458 (76, 190, 0.2626),
459 (76, 192, 0.4078),
460
461 (77, 191, 0.373),
463 (77, 193, 0.627),
464
465 (78, 190, 0.00012),
467 (78, 192, 0.00782),
468 (78, 194, 0.3286),
469 (78, 195, 0.3378),
470 (78, 196, 0.2521),
471 (78, 198, 0.07356),
472
473 (79, 197, 1.0),
475
476 (80, 196, 0.0015),
478 (80, 198, 0.0997),
479 (80, 199, 0.1687),
480 (80, 200, 0.231),
481 (80, 201, 0.1318),
482 (80, 202, 0.2986),
483 (80, 204, 0.0687),
484
485 (81, 203, 0.2952),
487 (81, 205, 0.7048),
488
489 (82, 204, 0.014),
491 (82, 206, 0.241),
492 (82, 207, 0.221),
493 (82, 208, 0.524),
494
495 (83, 209, 1.0),
497
498 (90, 232, 1.0),
500
501 (91, 231, 1.0),
503
504 (92, 234, 0.000054),
506 (92, 235, 0.007204),
507 (92, 238, 0.992742),
508
509];
510
511#[cfg(test)]
512mod tests {
513 use super::*;
514
515 #[test]
516 fn test_natural_abundance_uranium() {
517 assert!((natural_abundance(92, 238).unwrap() - 0.992742).abs() < 1e-6);
518 assert!((natural_abundance(92, 235).unwrap() - 0.007204).abs() < 1e-6);
519 assert!((natural_abundance(92, 234).unwrap() - 0.000054).abs() < 1e-6);
520 }
521
522 #[test]
523 fn test_natural_abundance_iron() {
524 assert!((natural_abundance(26, 56).unwrap() - 0.91754).abs() < 1e-5);
525 assert!((natural_abundance(26, 54).unwrap() - 0.05845).abs() < 1e-5);
526 }
527
528 #[test]
529 fn test_natural_abundance_synthetic() {
530 assert_eq!(natural_abundance(43, 99), None);
532 assert_eq!(natural_abundance(61, 147), None);
534 assert_eq!(natural_abundance(94, 239), None);
536 }
537
538 #[test]
539 fn test_natural_abundance_unknown() {
540 assert_eq!(natural_abundance(200, 400), None);
541 }
542
543 #[test]
544 fn test_natural_isotopes_iron() {
545 let fe = natural_isotopes(26);
546 assert_eq!(fe.len(), 4);
547 let total: f64 = fe.iter().map(|(_, f)| f).sum();
548 assert!((total - 1.0).abs() < 0.001);
549 }
550
551 #[test]
552 fn test_natural_isotopes_tin() {
553 let sn = natural_isotopes(50);
554 assert_eq!(sn.len(), 10); let total: f64 = sn.iter().map(|(_, f)| f).sum();
556 assert!((total - 1.0).abs() < 0.001);
557 }
558
559 #[test]
560 fn test_natural_isotopes_tungsten() {
561 let w = natural_isotopes(74);
562 assert_eq!(w.len(), 5); }
564
565 #[test]
566 fn test_natural_isotopes_synthetic() {
567 let tc = natural_isotopes(43);
568 assert!(tc.is_empty());
569 }
570
571 #[test]
572 fn test_natural_isotopes_mono_isotopic() {
573 let au = natural_isotopes(79);
575 assert_eq!(au.len(), 1);
576 assert_eq!(au[0].0, 197);
577 assert!((au[0].1 - 1.0).abs() < 1e-10);
578 }
579
580 #[test]
581 fn test_table_size() {
582 assert_eq!(NATURAL_ABUNDANCES.len(), 288);
583 }
584
585 #[test]
586 fn test_table_sorted() {
587 for i in 1..NATURAL_ABUNDANCES.len() {
588 let (z1, a1, _) = NATURAL_ABUNDANCES[i - 1];
589 let (z2, a2, _) = NATURAL_ABUNDANCES[i];
590 assert!(
591 (z1, a1) < (z2, a2),
592 "Table not sorted at index {}: ({}, {}) >= ({}, {})",
593 i,
594 z1,
595 a1,
596 z2,
597 a2
598 );
599 }
600 }
601}