1use crate::primes::lcm;
11use crate::rational::RnsRational;
12use crate::rns::Channels;
13
14#[derive(Debug, Clone, PartialEq, Eq)]
16pub struct DispatchPlan {
17 pub active_channels: Vec<usize>,
19 pub natural_base: u64,
21 pub channel_mask: u64,
23}
24
25pub struct Dispatcher {
27 channels: Channels,
28}
29
30impl Dispatcher {
31 pub fn new(channels: Channels) -> Self {
32 Dispatcher { channels }
33 }
34
35 pub fn channels(&self) -> &Channels {
37 &self.channels
38 }
39
40 fn plan_for(&self, natural_base: u64) -> DispatchPlan {
41 let mut active_channels = Vec::new();
42 let mut channel_mask = 0u64;
43 for c in 0..self.channels.len() {
44 let prime = self.channels.modulus(c);
45 if natural_base % prime == 0 {
46 active_channels.push(c);
47 if c < 64 {
48 channel_mask |= 1u64 << c;
49 }
50 }
51 }
52 DispatchPlan {
53 active_channels,
54 natural_base,
55 channel_mask,
56 }
57 }
58
59 pub fn plan_add(&self, a: &RnsRational, b: &RnsRational) -> DispatchPlan {
61 self.plan_for(lcm(a.natural_base(), b.natural_base()))
62 }
63
64 pub fn plan_mul(&self, a: &RnsRational, b: &RnsRational) -> DispatchPlan {
66 self.plan_for(lcm(a.natural_base(), b.natural_base()))
67 }
68
69 pub fn execute_add(&self, a: &RnsRational, b: &RnsRational) -> RnsRational {
72 a.add(b)
73 }
74
75 pub fn execute_mul(&self, a: &RnsRational, b: &RnsRational) -> RnsRational {
77 a.mul(b)
78 }
79
80 pub fn channel_efficiency(&self, plan: &DispatchPlan) -> f64 {
82 plan.active_channels.len() as f64 / self.channels.len() as f64
83 }
84
85 pub fn channel_idle_fraction(&self, plan: &DispatchPlan) -> f64 {
87 1.0 - self.channel_efficiency(plan)
88 }
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94
95 #[test]
96 fn sixth_plus_quarter_uses_two_channels() {
97 let channels = Channels::standard(16);
98 let dispatcher = Dispatcher::new(channels.clone());
99 let sixth = RnsRational::from_fraction(1, 6, channels.clone());
100 let quarter = RnsRational::from_fraction(1, 4, channels);
101 let plan = dispatcher.plan_add(&sixth, &quarter);
102 assert_eq!(plan.active_channels.len(), 2);
104 assert_eq!(plan.active_channels, vec![0, 1]); assert!((dispatcher.channel_efficiency(&plan) - 2.0 / 16.0).abs() < 1e-12);
106 }
107
108 #[test]
109 fn integers_use_no_channels() {
110 let channels = Channels::standard(16);
111 let dispatcher = Dispatcher::new(channels.clone());
112 let a = RnsRational::from_int(3, channels.clone());
113 let b = RnsRational::from_int(5, channels);
114 let plan = dispatcher.plan_add(&a, &b);
115 assert!(plan.active_channels.is_empty());
116 assert_eq!(dispatcher.channel_efficiency(&plan), 0.0);
117 }
118
119 #[test]
120 fn execution_is_exact() {
121 let channels = Channels::standard(16);
122 let dispatcher = Dispatcher::new(channels.clone());
123 let sixth = RnsRational::from_fraction(1, 6, channels.clone());
124 let quarter = RnsRational::from_fraction(1, 4, channels.clone());
125 assert_eq!(
127 dispatcher.execute_add(&sixth, &quarter),
128 RnsRational::from_fraction(5, 12, channels)
129 );
130 }
131}