use core::num::NonZeroU32;
use necsim_core::{
cogs::{Habitat, MathsCore, RngCore, UniformlySampleableHabitat},
landscape::{IndexedLocation, LandscapeExtent, Location},
};
use necsim_core_bond::{OffByOneU32, OffByOneU64};
use crate::cogs::habitat::non_spatial::NonSpatialHabitat;
const SPATIALLY_IMPLICIT_EXTENT: LandscapeExtent =
LandscapeExtent::new(Location::new(0, 0), OffByOneU32::max(), OffByOneU32::max());
#[allow(clippy::module_name_repetitions)]
#[derive(Debug)]
#[cfg_attr(feature = "cuda", derive(rust_cuda::lend::LendRustToCuda))]
#[cfg_attr(feature = "cuda", cuda(free = "M"))]
pub struct SpatiallyImplicitHabitat<M: MathsCore> {
#[cfg_attr(feature = "cuda", cuda(embed))]
local: NonSpatialHabitat<M>,
#[cfg_attr(feature = "cuda", cuda(embed))]
meta: NonSpatialHabitat<M>,
}
impl<M: MathsCore> SpatiallyImplicitHabitat<M> {
#[must_use]
#[debug_ensures(
ret.get_total_habitat() == old(
OffByOneU64::from(local_area.0)
* OffByOneU64::from(local_area.1)
* OffByOneU64::from(local_deme)
+ OffByOneU64::from(meta_area.0)
* OffByOneU64::from(meta_area.1)
* OffByOneU64::from(meta_deme)
),
"creates a habitat with a combined local and meta community size "
)]
pub fn new(
local_area: (OffByOneU32, OffByOneU32),
local_deme: NonZeroU32,
meta_area: (OffByOneU32, OffByOneU32),
meta_deme: NonZeroU32,
) -> Self {
let local = NonSpatialHabitat::new(local_area, local_deme);
let meta = NonSpatialHabitat::new_with_offset(
Location::new(meta_area.0.inv(), meta_area.1.inv()),
meta_area,
meta_deme,
);
Self { local, meta }
}
#[must_use]
pub fn local(&self) -> &NonSpatialHabitat<M> {
&self.local
}
#[must_use]
pub fn meta(&self) -> &NonSpatialHabitat<M> {
&self.meta
}
}
impl<M: MathsCore> Clone for SpatiallyImplicitHabitat<M> {
fn clone(&self) -> Self {
Self {
local: self.local.clone(),
meta: self.meta.clone(),
}
}
}
#[contract_trait]
impl<M: MathsCore> Habitat<M> for SpatiallyImplicitHabitat<M> {
type LocationIterator<'a> = impl Iterator<Item = Location> + 'a;
#[must_use]
fn is_finite(&self) -> bool {
true
}
#[must_use]
fn get_extent(&self) -> &LandscapeExtent {
&SPATIALLY_IMPLICIT_EXTENT
}
#[must_use]
fn get_total_habitat(&self) -> OffByOneU64 {
self.local.get_total_habitat() + self.meta().get_total_habitat()
}
#[must_use]
fn get_habitat_at_location(&self, location: &Location) -> u32 {
if self.local.get_extent().contains(location) {
self.local.get_habitat_at_location(location)
} else if self.meta().get_extent().contains(location) {
self.meta.get_habitat_at_location(location)
} else {
0_u32
}
}
#[must_use]
#[inline]
fn map_indexed_location_to_u64_injective(&self, indexed_location: &IndexedLocation) -> u64 {
if self
.local
.get_extent()
.contains(indexed_location.location())
{
self.local
.map_indexed_location_to_u64_injective(indexed_location)
} else {
self.meta
.map_indexed_location_to_u64_injective(indexed_location)
}
}
#[must_use]
fn iter_habitable_locations(&self) -> Self::LocationIterator<'_> {
self.local
.iter_habitable_locations()
.chain(self.meta.iter_habitable_locations())
}
}
#[contract_trait]
impl<M: MathsCore, G: RngCore<M>> UniformlySampleableHabitat<M, G> for SpatiallyImplicitHabitat<M> {
#[must_use]
#[inline]
fn sample_habitable_indexed_location(&self, rng: &mut G) -> IndexedLocation {
self.local.sample_habitable_indexed_location(rng)
}
}