1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
use libm::atan2;

use necsim_core::{
    cogs::{Backup, Habitat, MathsCore},
    landscape::Location,
};
use necsim_partitioning_core::partition::Partition;

use crate::decomposition::Decomposition;

#[allow(clippy::module_name_repetitions)]
#[derive(Debug)]
pub struct RadialDecomposition {
    subdomain: Partition,
}

impl RadialDecomposition {
    #[must_use]
    pub fn new(subdomain: Partition) -> Self {
        Self { subdomain }
    }
}

#[contract_trait]
impl Backup for RadialDecomposition {
    unsafe fn backup_unchecked(&self) -> Self {
        Self {
            subdomain: self.subdomain,
        }
    }
}

#[contract_trait]
impl<M: MathsCore, H: Habitat<M>> Decomposition<M, H> for RadialDecomposition {
    fn get_subdomain(&self) -> Partition {
        self.subdomain
    }

    fn map_location_to_subdomain_rank(&self, location: &Location, habitat: &H) -> u32 {
        let extent = habitat.get_extent();

        let neutral_x = location.x().wrapping_sub(extent.origin().x());
        let neutral_y = location.y().wrapping_sub(extent.origin().y());

        #[allow(clippy::cast_precision_loss)]
        let fraction = (atan2(
            (i64::from(neutral_y) - i64::from(extent.height()) / 2) as f64,
            (i64::from(neutral_x) - i64::from(extent.width()) / 2) as f64,
        ) * core::f64::consts::FRAC_1_PI
            * 0.5_f64)
            + 0.5_f64;

        #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
        {
            (M::floor(f64::from(self.subdomain.size().get()) * fraction) as u32)
                .min(self.subdomain.size().get() - 1)
        }
    }
}