Skip to content

Commit a316c64

Browse files
committed
Add newtypes for Time, Position, and for Location.
Closes #191
1 parent 4ba44ef commit a316c64

13 files changed

+375
-105
lines changed

examples/forward_simulation.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ fn crossover_and_record_edges_details(
228228
Err(e) => panic!("{}", e),
229229
}
230230
} else {
231-
let exp = match Exp::new(params.xovers / tables.sequence_length()) {
231+
let exp = match Exp::new(params.xovers / f64::from(tables.sequence_length())) {
232232
Ok(e) => e,
233233
Err(e) => panic!("{}", e),
234234
};

src/_macros.rs

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,140 @@ macro_rules! impl_size_type_comparisons_for_row_ids {
365365
};
366366
}
367367

368+
macro_rules! impl_f64_newtypes {
369+
($type: ty) => {
370+
impl std::fmt::Display for $type {
371+
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
372+
write!(f, "{}({})", stringify!($idtype), self.0)
373+
}
374+
}
375+
376+
impl PartialEq<f64> for $type {
377+
fn eq(&self, other: &f64) -> bool {
378+
self.0.eq(other)
379+
}
380+
}
381+
382+
impl PartialEq<$type> for f64 {
383+
fn eq(&self, other: &$type) -> bool {
384+
self.eq(&other.0)
385+
}
386+
}
387+
388+
impl PartialOrd<f64> for $type {
389+
fn partial_cmp(&self, other: &f64) -> Option<std::cmp::Ordering> {
390+
self.0.partial_cmp(other)
391+
}
392+
}
393+
394+
impl PartialOrd<$type> for f64 {
395+
fn partial_cmp(&self, other: &$type) -> Option<std::cmp::Ordering> {
396+
self.partial_cmp(&other.0)
397+
}
398+
}
399+
400+
impl From<f64> for $type {
401+
fn from(value: f64) -> Self {
402+
Self(value)
403+
}
404+
}
405+
406+
impl From<$type> for f64 {
407+
fn from(value: $type) -> Self {
408+
value.0
409+
}
410+
}
411+
412+
impl std::ops::Sub for $type {
413+
type Output = Self;
414+
415+
fn sub(self, rhs: Self) -> Self::Output {
416+
Self(self.0 - rhs.0)
417+
}
418+
}
419+
420+
impl std::ops::SubAssign for $type {
421+
fn sub_assign(&mut self, rhs: Self) {
422+
self.0 -= rhs.0
423+
}
424+
}
425+
426+
impl std::ops::Add for $type {
427+
type Output = Self;
428+
429+
fn add(self, rhs: Self) -> Self::Output {
430+
Self(self.0 + rhs.0)
431+
}
432+
}
433+
434+
impl std::ops::AddAssign for $type {
435+
fn add_assign(&mut self, rhs: Self) {
436+
self.0 += rhs.0
437+
}
438+
}
439+
440+
impl std::ops::Mul for $type {
441+
type Output = Self;
442+
443+
fn mul(self, rhs: Self) -> Self::Output {
444+
Self(self.0 * rhs.0)
445+
}
446+
}
447+
448+
impl std::ops::MulAssign for $type {
449+
fn mul_assign(&mut self, rhs: Self) {
450+
self.0.mul_assign(&rhs.0)
451+
}
452+
}
453+
454+
impl std::ops::Div for $type {
455+
type Output = Self;
456+
457+
fn div(self, rhs: Self) -> Self::Output {
458+
Self(self.0 / rhs.0)
459+
}
460+
}
461+
462+
impl std::ops::DivAssign for $type {
463+
fn div_assign(&mut self, rhs: Self) {
464+
self.0.div_assign(&rhs.0)
465+
}
466+
}
467+
};
468+
}
469+
470+
macro_rules! impl_time_position_arithmetic {
471+
($lhs: ty, $rhs:ty) => {
472+
impl std::ops::Mul<$rhs> for $lhs {
473+
type Output = $lhs;
474+
475+
fn mul(self, other: $rhs) -> Self {
476+
Self(self.0.mul(&other.0))
477+
}
478+
}
479+
480+
impl std::ops::MulAssign<$rhs> for $lhs {
481+
fn mul_assign(&mut self, other: $rhs) {
482+
self.0.mul_assign(&other.0)
483+
}
484+
}
485+
486+
impl std::ops::Div<$rhs> for $lhs {
487+
type Output = $lhs;
488+
489+
fn div(self, other: $rhs) -> Self {
490+
Self(self.0.div(&other.0))
491+
}
492+
}
493+
494+
impl std::ops::DivAssign<$rhs> for $lhs {
495+
fn div_assign(&mut self, other: $rhs) {
496+
self.0.div_assign(&other.0)
497+
}
498+
}
499+
};
500+
}
501+
368502
/// Convenience macro to handle implementing
369503
/// [`crate::metadata::MetadataRoundtrip`]
370504
#[macro_export]

src/edge_table.rs

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
use crate::bindings as ll_bindings;
22
use crate::metadata;
3+
use crate::Position;
34
use crate::{tsk_id_t, TskitError};
45
use crate::{EdgeId, NodeId};
56

67
/// Row of an [`EdgeTable`]
78
pub struct EdgeTableRow {
89
pub id: EdgeId,
9-
pub left: f64,
10-
pub right: f64,
10+
pub left: Position,
11+
pub right: Position,
1112
pub parent: NodeId,
1213
pub child: NodeId,
1314
pub metadata: Option<Vec<u8>>,
@@ -18,8 +19,8 @@ impl PartialEq for EdgeTableRow {
1819
self.id == other.id
1920
&& self.parent == other.parent
2021
&& self.child == other.child
21-
&& crate::util::f64_partial_cmp_equal(&self.left, &other.left)
22-
&& crate::util::f64_partial_cmp_equal(&self.right, &other.right)
22+
&& crate::util::partial_cmp_equal(&self.left, &other.left)
23+
&& crate::util::partial_cmp_equal(&self.right, &other.right)
2324
&& self.metadata == other.metadata
2425
}
2526
}
@@ -112,8 +113,11 @@ impl<'a> EdgeTable<'a> {
112113
///
113114
/// Will return [``IndexError``](crate::TskitError::IndexError)
114115
/// if ``row`` is out of range.
115-
pub fn left<E: Into<EdgeId> + Copy>(&'a self, row: E) -> Result<f64, TskitError> {
116-
unsafe_tsk_column_access!(row.into().0, 0, self.num_rows(), self.table_.left)
116+
pub fn left<E: Into<EdgeId> + Copy>(&'a self, row: E) -> Result<Position, TskitError> {
117+
match unsafe_tsk_column_access!(row.into().0, 0, self.num_rows(), self.table_.left) {
118+
Ok(p) => Ok(p.into()),
119+
Err(e) => Err(e),
120+
}
117121
}
118122

119123
/// Return the ``right`` value from row ``row`` of the table.
@@ -122,8 +126,11 @@ impl<'a> EdgeTable<'a> {
122126
///
123127
/// Will return [``IndexError``](crate::TskitError::IndexError)
124128
/// if ``row`` is out of range.
125-
pub fn right<E: Into<EdgeId> + Copy>(&'a self, row: E) -> Result<f64, TskitError> {
126-
unsafe_tsk_column_access!(row.into().0, 0, self.num_rows(), self.table_.right)
129+
pub fn right<E: Into<EdgeId> + Copy>(&'a self, row: E) -> Result<Position, TskitError> {
130+
match unsafe_tsk_column_access!(row.into().0, 0, self.num_rows(), self.table_.right) {
131+
Ok(p) => Ok(p.into()),
132+
Err(e) => Err(e),
133+
}
127134
}
128135

129136
pub fn metadata<T: metadata::MetadataRoundtrip>(

src/individual_table.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
use crate::bindings as ll_bindings;
22
use crate::metadata;
33
use crate::IndividualId;
4+
use crate::Location;
45
use crate::{tsk_flags_t, tsk_id_t, tsk_size_t, TskitError};
56

67
/// Row of a [`IndividualTable`]
78
pub struct IndividualTableRow {
89
pub id: IndividualId,
910
pub flags: tsk_flags_t,
10-
pub location: Option<Vec<f64>>,
11+
pub location: Option<Vec<Location>>,
1112
pub parents: Option<Vec<IndividualId>>,
1213
pub metadata: Option<Vec<u8>>,
1314
}
@@ -27,7 +28,7 @@ impl PartialEq for IndividualTableRow {
2728
false
2829
} else {
2930
for (i, j) in a.iter().enumerate() {
30-
if !crate::util::f64_partial_cmp_equal(j, &b[i]) {
31+
if !crate::util::partial_cmp_equal(&b[i], j) {
3132
return false;
3233
}
3334
}
@@ -121,14 +122,15 @@ impl<'a> IndividualTable<'a> {
121122
pub fn location<I: Into<IndividualId> + Copy>(
122123
&self,
123124
row: I,
124-
) -> Result<Option<Vec<f64>>, TskitError> {
125+
) -> Result<Option<Vec<Location>>, TskitError> {
125126
unsafe_tsk_ragged_column_access!(
126127
row.into().0,
127128
0,
128129
self.num_rows(),
129130
self.table_.location,
130131
self.table_.location_offset,
131-
self.table_.location_length
132+
self.table_.location_length,
133+
Location
132134
)
133135
}
134136

src/lib.rs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,77 @@ impl PartialOrd<SizeType> for tsk_size_t {
333333
}
334334
}
335335

336+
/// A newtype for the concept of time.
337+
/// A `Time` value can represent either a point in time
338+
/// or the output of arithmetic involving time.
339+
///
340+
/// Wraps [`f64`].
341+
///
342+
/// # Examples
343+
///
344+
/// ```
345+
/// let t0 = tskit::Time::from(2.0);
346+
/// let t1 = tskit::Time::from(10.0);
347+
///
348+
/// let mut sum = t0 + t1;
349+
///
350+
/// match sum.partial_cmp(&12.0) {
351+
/// Some(std::cmp::Ordering::Equal) => (),
352+
/// _ => assert!(false),
353+
/// };
354+
///
355+
/// sum /= tskit::Time::from(2.0);
356+
///
357+
/// match sum.partial_cmp(&6.0) {
358+
/// Some(std::cmp::Ordering::Equal) => (),
359+
/// _ => assert!(false),
360+
/// };
361+
/// ```
362+
///
363+
/// # Notes
364+
///
365+
/// The current implementation of [`PartialOrd`] is based on
366+
/// the underlying implementation for [`f64`].
367+
///
368+
/// A `Time` can be multiplied and divided by a [`Position`]
369+
///
370+
#[repr(transparent)]
371+
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
372+
pub struct Time(f64);
373+
374+
/// A newtype for the concept of "genomic position".
375+
/// A `Position` can represent either a locus or a
376+
/// distance between loci.
377+
///
378+
/// Wraps [`f64`].
379+
///
380+
/// For examples, see [`Time`].
381+
///
382+
/// This type can be multiplied and divided by [`Time`].
383+
#[repr(transparent)]
384+
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
385+
pub struct Position(f64);
386+
387+
/// A newtype for the concept of location.
388+
/// A `Location` may represent a location or the
389+
/// output of arithmetic involving locations.
390+
///
391+
/// Wraps [`f64`].
392+
///
393+
/// For examples, see [`Time`].
394+
///
395+
#[repr(transparent)]
396+
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
397+
pub struct Location(f64);
398+
399+
impl_f64_newtypes!(Time);
400+
impl_f64_newtypes!(Position);
401+
impl_f64_newtypes!(Location);
402+
403+
// It is natural to be able to * and / times and positions
404+
impl_time_position_arithmetic!(Time, Position);
405+
impl_time_position_arithmetic!(Position, Time);
406+
336407
// tskit defines this via a type cast
337408
// in a macro. bindgen thus misses it.
338409
// See bindgen issue 316.

src/metadata.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@
8888
//! let individual = IndividualMetadata {
8989
//! genetic_value: GeneticValue(0.0),
9090
//! };
91-
//! let id = tables.add_individual_with_metadata(0, &[], &[tskit::IndividualId::NULL], &individual).unwrap();
91+
//! let id = tables.add_individual_with_metadata(0, &[] as &[tskit::Location], &[tskit::IndividualId::NULL], &individual).unwrap();
9292
//! let decoded = tables.individuals().metadata::<IndividualMetadata>(id).unwrap().unwrap();
9393
//! assert_eq!(decoded.genetic_value.partial_cmp(&individual.genetic_value).unwrap(), std::cmp::Ordering::Equal);
9494
//! # }

0 commit comments

Comments
 (0)