use necsim_core_bond::{NonNegativeF64, PositiveF64};
use serde::{Deserialize, Serialize};
use core::{
cmp::{Ord, Ordering},
hash::{Hash, Hasher},
};
use crate::{
landscape::IndexedLocation,
lineage::{GlobalLineageReference, LineageInteraction},
};
#[allow(clippy::module_name_repetitions)]
#[allow(clippy::unsafe_derive_deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize, TypeLayout)]
#[repr(C)]
pub struct PackedEvent {
global_lineage_reference: GlobalLineageReference,
prior_time: f64, event_time: f64, origin: IndexedLocation,
target: IndexedLocation,
coalescence: GlobalLineageReference,
}
impl PackedEvent {
#[must_use]
#[inline]
pub fn event_time(&self) -> PositiveF64 {
unsafe { PositiveF64::new_unchecked(self.event_time.make_positive()) }
}
}
#[allow(dead_code)]
const EXCESSIVE_PACKED_EVENT_ERROR: [(); 1 - {
const ASSERT: bool = core::mem::size_of::<PackedEvent>() == 56;
ASSERT
} as usize] = [];
#[allow(clippy::module_name_repetitions)]
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum EventType {
Speciation,
Dispersal(Dispersal),
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Dispersal {
pub target: IndexedLocation,
pub interaction: LineageInteraction,
}
#[allow(clippy::module_name_repetitions)]
#[derive(Debug, Clone, Serialize, Deserialize, TypeLayout)]
#[repr(C)]
pub struct SpeciationEvent {
pub global_lineage_reference: GlobalLineageReference,
pub prior_time: NonNegativeF64, pub event_time: PositiveF64, pub origin: IndexedLocation,
}
#[allow(dead_code)]
const EXCESSIVE_SPECIATION_EVENT_ERROR: [(); 1 - {
const ASSERT: bool = core::mem::size_of::<SpeciationEvent>() == 40;
ASSERT
} as usize] = [];
#[allow(clippy::module_name_repetitions)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DispersalEvent {
pub global_lineage_reference: GlobalLineageReference,
pub prior_time: NonNegativeF64, pub event_time: PositiveF64, pub origin: IndexedLocation,
pub target: IndexedLocation,
pub interaction: LineageInteraction,
}
#[allow(dead_code)]
const EXCESSIVE_DISPERSAL_EVENT_ERROR: [(); 1 - {
const ASSERT: bool = core::mem::size_of::<DispersalEvent>() == 64;
ASSERT
} as usize] = [];
#[allow(dead_code)]
const EXCESSIVE_OPTION_DISPERSAL_EVENT_ERROR: [(); 1 - {
const ASSERT: bool =
core::mem::size_of::<Option<DispersalEvent>>() == core::mem::size_of::<DispersalEvent>();
ASSERT
} as usize] = [];
#[allow(clippy::module_name_repetitions)]
pub enum TypedEvent {
Speciation(SpeciationEvent),
Dispersal(DispersalEvent),
}
impl From<SpeciationEvent> for PackedEvent {
fn from(event: SpeciationEvent) -> Self {
Self {
global_lineage_reference: event.global_lineage_reference.clone(),
prior_time: event.prior_time.get().make_negative(),
event_time: event.event_time.get(),
origin: event.origin.clone(),
target: event.origin,
coalescence: event.global_lineage_reference,
}
}
}
impl From<DispersalEvent> for PackedEvent {
fn from(
DispersalEvent {
global_lineage_reference,
prior_time,
event_time,
origin,
target,
interaction,
}: DispersalEvent,
) -> Self {
let prior_time = prior_time.get();
let event_time = event_time.get();
let (coalescence, prior_time, event_time) = match interaction {
LineageInteraction::None => {
(
global_lineage_reference.clone(),
prior_time.make_negative(),
event_time.make_negative(),
)
},
LineageInteraction::Maybe => {
(
global_lineage_reference.clone(),
prior_time,
event_time.make_negative(),
)
},
LineageInteraction::Coalescence(coalescence) => {
(coalescence, prior_time, event_time)
},
};
Self {
global_lineage_reference,
prior_time,
event_time,
origin,
target,
coalescence,
}
}
}
impl From<TypedEvent> for PackedEvent {
fn from(event: TypedEvent) -> Self {
match event {
TypedEvent::Speciation(event) => event.into(),
TypedEvent::Dispersal(event) => event.into(),
}
}
}
impl From<SpeciationEvent> for TypedEvent {
fn from(event: SpeciationEvent) -> Self {
Self::Speciation(event)
}
}
impl From<DispersalEvent> for TypedEvent {
fn from(event: DispersalEvent) -> Self {
Self::Dispersal(event)
}
}
impl From<PackedEvent> for TypedEvent {
fn from(
PackedEvent {
global_lineage_reference,
prior_time,
event_time,
origin,
target,
coalescence,
}: PackedEvent,
) -> Self {
let prior_pos = prior_time.is_sign_positive();
let event_pos = event_time.is_sign_positive();
let prior_time = unsafe { NonNegativeF64::new_unchecked(prior_time.make_positive()) };
let event_time = unsafe { PositiveF64::new_unchecked(event_time.make_positive()) };
match (prior_pos, event_pos) {
(false, false) => Self::Dispersal(DispersalEvent {
global_lineage_reference,
prior_time,
event_time,
origin,
target,
interaction: LineageInteraction::None,
}),
(false, true) => Self::Speciation(SpeciationEvent {
global_lineage_reference,
prior_time,
event_time,
origin,
}),
(true, false) => Self::Dispersal(DispersalEvent {
global_lineage_reference,
prior_time,
event_time,
origin,
target,
interaction: LineageInteraction::Maybe,
}),
(true, true) => Self::Dispersal(DispersalEvent {
global_lineage_reference,
prior_time,
event_time,
origin,
target,
interaction: LineageInteraction::Coalescence(coalescence),
}),
}
}
}
impl Eq for PackedEvent {}
impl PartialEq for PackedEvent {
fn eq(&self, other: &Self) -> bool {
self.origin == other.origin
&& unsafe { PositiveF64::new_unchecked(self.event_time) }
== unsafe { PositiveF64::new_unchecked(other.event_time) }
&& self.target == other.target
}
}
impl Ord for PackedEvent {
fn cmp(&self, other: &Self) -> Ordering {
(
&unsafe { PositiveF64::new_unchecked(self.event_time.make_positive()) },
&self.origin,
&self.target,
&unsafe { NonNegativeF64::new_unchecked(self.prior_time.make_positive()) },
&self.global_lineage_reference,
)
.cmp(&(
&unsafe { PositiveF64::new_unchecked(other.event_time.make_positive()) },
&other.origin,
&other.target,
&unsafe { NonNegativeF64::new_unchecked(other.prior_time.make_positive()) },
&other.global_lineage_reference,
))
}
}
impl PartialOrd for PackedEvent {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Hash for PackedEvent {
fn hash<S: Hasher>(&self, state: &mut S) {
self.origin.hash(state);
unsafe { PositiveF64::new_unchecked(self.event_time.make_positive()) }.hash(state);
self.target.hash(state);
}
}
impl Eq for SpeciationEvent {}
impl PartialEq for SpeciationEvent {
fn eq(&self, other: &Self) -> bool {
self.origin == other.origin && self.event_time == other.event_time
}
}
impl Eq for DispersalEvent {}
impl PartialEq for DispersalEvent {
fn eq(&self, other: &Self) -> bool {
self.origin == other.origin
&& self.event_time == other.event_time
&& self.target == other.target
}
}
trait F64 {
fn make_positive(self) -> Self;
fn make_negative(self) -> Self;
}
impl F64 for f64 {
fn make_positive(self) -> Self {
f64::from_bits(self.to_bits() & 0x7fff_ffff_ffff_ffff_u64)
}
fn make_negative(self) -> Self {
f64::from_bits(self.to_bits() | 0x8000_0000_0000_0000_u64)
}
}
#[cfg(test)]
mod tests {
use super::F64;
#[test]
fn test_make_positive() {
assert_eq!((-0.0_f64).make_positive().to_bits(), (0.0_f64).to_bits());
assert_eq!((-42.0_f64).make_positive().to_bits(), (42.0_f64).to_bits());
assert_eq!((24.0_f64).make_positive().to_bits(), (24.0_f64).to_bits());
}
#[test]
fn test_make_negative() {
assert_eq!((0.0_f64).make_negative().to_bits(), (-0.0_f64).to_bits());
assert_eq!((42.0_f64).make_negative().to_bits(), (-42.0_f64).to_bits());
assert_eq!((-24.0_f64).make_negative().to_bits(), (-24.0_f64).to_bits());
}
}