pub trait NetworkTopology: Send + Sync + std::fmt::Debug {
fn kind(&self) -> &'static str;
fn node_count(&self) -> usize;
fn neighbours(&self, node: usize) -> Vec<usize>;
fn next_hop(&self, from: usize, to: usize) -> usize;
fn diameter(&self) -> usize;
}
#[derive(Clone, Copy, Debug)]
pub struct Ring {
nodes: usize,
}
impl Ring {
#[must_use]
pub fn new(nodes: usize) -> Self {
Self { nodes: nodes.max(2) }
}
}
impl NetworkTopology for Ring {
fn kind(&self) -> &'static str {
"ring"
}
fn node_count(&self) -> usize {
self.nodes
}
fn neighbours(&self, node: usize) -> Vec<usize> {
vec![(node + self.nodes - 1) % self.nodes, (node + 1) % self.nodes]
}
fn next_hop(&self, from: usize, to: usize) -> usize {
let forward = (to + self.nodes - from) % self.nodes;
if forward <= self.nodes - forward {
(from + 1) % self.nodes
} else {
(from + self.nodes - 1) % self.nodes
}
}
fn diameter(&self) -> usize {
self.nodes / 2
}
}
#[derive(Clone, Copy, Debug)]
pub struct Mesh2D {
k: usize,
wraparound: bool,
}
impl Mesh2D {
#[must_use]
pub fn for_endpoints(endpoints: usize, wraparound: bool) -> Self {
let mut k = 1;
while k * k < endpoints.max(1) {
k += 1;
}
Self { k: k.max(2), wraparound }
}
const fn coords(&self, node: usize) -> (usize, usize) {
(node % self.k, node / self.k)
}
const fn node(&self, x: usize, y: usize) -> usize {
y * self.k + x
}
const fn step(&self, from: usize, to: usize) -> usize {
if from == to {
return from;
}
if !self.wraparound {
return if to > from { from + 1 } else { from - 1 };
}
let forward = (to + self.k - from) % self.k;
if forward <= self.k - forward { (from + 1) % self.k } else { (from + self.k - 1) % self.k }
}
}
impl NetworkTopology for Mesh2D {
fn kind(&self) -> &'static str {
if self.wraparound { "torus" } else { "mesh" }
}
fn node_count(&self) -> usize {
self.k * self.k
}
fn neighbours(&self, node: usize) -> Vec<usize> {
let (x, y) = self.coords(node);
let mut out = Vec::with_capacity(4);
if self.wraparound {
out.push(self.node((x + self.k - 1) % self.k, y));
out.push(self.node((x + 1) % self.k, y));
out.push(self.node(x, (y + self.k - 1) % self.k));
out.push(self.node(x, (y + 1) % self.k));
} else {
if x > 0 {
out.push(self.node(x - 1, y));
}
if x + 1 < self.k {
out.push(self.node(x + 1, y));
}
if y > 0 {
out.push(self.node(x, y - 1));
}
if y + 1 < self.k {
out.push(self.node(x, y + 1));
}
}
out.sort_unstable();
out.dedup();
out
}
fn next_hop(&self, from: usize, to: usize) -> usize {
let (fx, fy) = self.coords(from);
let (tx, ty) = self.coords(to);
if fx == tx { self.node(fx, self.step(fy, ty)) } else { self.node(self.step(fx, tx), fy) }
}
fn diameter(&self) -> usize {
if self.wraparound { 2 * (self.k / 2) } else { 2 * (self.k - 1) }
}
}
#[derive(Clone, Copy, Debug)]
pub struct Hypercube {
dims: u32,
}
impl Hypercube {
#[must_use]
pub fn for_endpoints(endpoints: usize) -> Self {
let mut dims = 1;
while (1usize << dims) < endpoints.max(2) {
dims += 1;
}
Self { dims }
}
}
impl NetworkTopology for Hypercube {
fn kind(&self) -> &'static str {
"hypercube"
}
fn node_count(&self) -> usize {
1 << self.dims
}
fn neighbours(&self, node: usize) -> Vec<usize> {
(0..self.dims).map(|d| node ^ (1 << d)).collect()
}
fn next_hop(&self, from: usize, to: usize) -> usize {
let differing = from ^ to;
from ^ (1 << differing.trailing_zeros())
}
fn diameter(&self) -> usize {
self.dims as usize
}
}