#![crate_name = "xbar"]
use std::cmp::min;
#[derive(Debug)]
pub struct Position {
pub block_idx: usize,
pub row_idx: usize,
pub abs_idx: usize,
}
#[derive(Debug)]
pub struct Connection {
pub start: Position,
pub end: Position,
pub col_idx: usize,
}
#[derive(Debug)]
pub struct Crossbar {
pub count: usize,
inner_idx: usize,
outer_idx: usize,
}
impl Position {
fn new(block_idx: usize, row_idx: usize, count: usize) -> Self {
Self {
block_idx,
row_idx,
abs_idx: row_idx + block_idx * count,
}
}
}
impl Crossbar {
pub fn new(count: usize) -> Self {
Self {
count,
inner_idx: 0,
outer_idx: 1,
}
}
#[inline]
pub fn blocks(count: usize) -> usize {
count - 1
}
#[inline]
pub fn rows(count: usize) -> usize {
count * (count - 1)
}
#[inline]
pub fn columns(count: usize) -> usize {
count / 2
}
#[inline]
fn b2i(b: bool) -> usize {
if b {
1
} else {
0
}
}
#[inline]
fn full_block_reverse(n: usize, i: usize, j: usize) -> Connection {
let block = 2 * i - 1;
let col = if j < 3 * i {
j % i
} else {
i + min(j % i, j + i - n)
};
Connection {
start: Position::new(block, i + j - n, n),
end: Position::new(block, j, n),
col_idx: col,
}
}
#[inline]
fn full_block_forward(n: usize, i: usize, j: usize, is_odd: bool, is_wrap: bool) -> Connection {
let block = 2 * i - 2;
Connection {
start: Position::new(block + Self::b2i(is_odd), j, n),
end: Position::new(block + Self::b2i(is_odd || is_wrap), (i + j) % n, n),
col_idx: j % i,
}
}
#[inline]
fn full_block(n: usize, i: usize, j: usize) -> Connection {
let is_odd = ((j / i) & 1) == 1;
let is_wrap = i + j >= n;
if is_odd && is_wrap {
Self::full_block_reverse(n, i, j)
} else {
Self::full_block_forward(n, i, j, is_odd, is_wrap)
}
}
#[inline]
fn half_block(n: usize, i: usize, j: usize) -> Connection {
let block = 2 * i - 2;
Connection {
start: Position::new(block, j, n),
end: Position::new(block, i + j, n),
col_idx: j,
}
}
#[inline]
fn step(&mut self, inner_lim: usize) {
self.inner_idx += 1;
if self.inner_idx >= inner_lim {
self.inner_idx = 0;
self.outer_idx += 1;
}
}
}
impl Iterator for Crossbar {
type Item = Connection;
fn next(&mut self) -> Option<Self::Item> {
let rem = 2 * self.outer_idx;
if rem > self.count {
None
} else if rem == self.count {
let o = self.outer_idx;
let conn = Self::half_block(self.count, o, self.inner_idx);
self.step(o);
Some(conn)
} else {
let c = self.count;
let conn = Self::full_block(c, self.outer_idx, self.inner_idx);
self.step(c);
Some(conn)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_iter() {
for n in 1..100 {
println!("testing n = {}", n);
let w = n * (n - 1);
let mut buf: Vec<Vec<i32>> = vec![vec![0; w]; n / 2];
let mut mat: Vec<Vec<i32>> = vec![vec![0; n]; n];
for (i, row) in mat.iter_mut().enumerate().take(n) {
row[i] = 1;
}
for val in Crossbar::new(n) {
assert_eq!(
val.start.abs_idx,
val.start.row_idx + n * val.start.block_idx
);
assert_eq!(val.end.abs_idx, val.end.row_idx + n * val.end.block_idx);
mat[val.start.row_idx][val.end.row_idx] += 1;
mat[val.end.row_idx][val.start.row_idx] += 1;
for k in val.start.abs_idx..val.end.abs_idx {
assert_eq!(buf[val.col_idx][k], 0);
buf[val.col_idx][k] += 1;
}
for row in buf.iter().take(val.col_idx) {
let mut empty_line = true;
for elem in row.iter().take(val.end.abs_idx).skip(val.start.abs_idx) {
if elem != &0 {
empty_line = false;
break;
}
}
assert_eq!(empty_line, false);
}
}
for row in mat.iter().take(n) {
for elem in row.iter().take(n) {
assert_eq!(elem, &1);
}
}
}
}
}