use crate::primes::lcm;
use crate::rational::RnsRational;
use crate::rns::Channels;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DispatchPlan {
pub active_channels: Vec<usize>,
pub natural_base: u64,
pub channel_mask: u64,
}
pub struct Dispatcher {
channels: Channels,
}
impl Dispatcher {
pub fn new(channels: Channels) -> Self {
Dispatcher { channels }
}
pub fn channels(&self) -> &Channels {
&self.channels
}
fn plan_for(&self, natural_base: u64) -> DispatchPlan {
let mut active_channels = Vec::new();
let mut channel_mask = 0u64;
for c in 0..self.channels.len() {
let prime = self.channels.modulus(c);
if natural_base % prime == 0 {
active_channels.push(c);
if c < 64 {
channel_mask |= 1u64 << c;
}
}
}
DispatchPlan {
active_channels,
natural_base,
channel_mask,
}
}
pub fn plan_add(&self, a: &RnsRational, b: &RnsRational) -> DispatchPlan {
self.plan_for(lcm(a.natural_base(), b.natural_base()))
}
pub fn plan_mul(&self, a: &RnsRational, b: &RnsRational) -> DispatchPlan {
self.plan_for(lcm(a.natural_base(), b.natural_base()))
}
pub fn execute_add(&self, a: &RnsRational, b: &RnsRational) -> RnsRational {
a.add(b)
}
pub fn execute_mul(&self, a: &RnsRational, b: &RnsRational) -> RnsRational {
a.mul(b)
}
pub fn channel_efficiency(&self, plan: &DispatchPlan) -> f64 {
plan.active_channels.len() as f64 / self.channels.len() as f64
}
pub fn channel_idle_fraction(&self, plan: &DispatchPlan) -> f64 {
1.0 - self.channel_efficiency(plan)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn sixth_plus_quarter_uses_two_channels() {
let channels = Channels::standard(16);
let dispatcher = Dispatcher::new(channels.clone());
let sixth = RnsRational::from_fraction(1, 6, channels.clone());
let quarter = RnsRational::from_fraction(1, 4, channels);
let plan = dispatcher.plan_add(&sixth, &quarter);
assert_eq!(plan.active_channels.len(), 2);
assert_eq!(plan.active_channels, vec![0, 1]); assert!((dispatcher.channel_efficiency(&plan) - 2.0 / 16.0).abs() < 1e-12);
}
#[test]
fn integers_use_no_channels() {
let channels = Channels::standard(16);
let dispatcher = Dispatcher::new(channels.clone());
let a = RnsRational::from_int(3, channels.clone());
let b = RnsRational::from_int(5, channels);
let plan = dispatcher.plan_add(&a, &b);
assert!(plan.active_channels.is_empty());
assert_eq!(dispatcher.channel_efficiency(&plan), 0.0);
}
#[test]
fn execution_is_exact() {
let channels = Channels::standard(16);
let dispatcher = Dispatcher::new(channels.clone());
let sixth = RnsRational::from_fraction(1, 6, channels.clone());
let quarter = RnsRational::from_fraction(1, 4, channels.clone());
assert_eq!(
dispatcher.execute_add(&sixth, &quarter),
RnsRational::from_fraction(5, 12, channels)
);
}
}