use std::fmt::Debug;
pub trait Indexer: Eq + Debug + Clone {
type Index: Copy;
fn to_num(&self, index: Self::Index) -> usize;
fn to_index(&self, n: usize) -> Self::Index;
fn is_valid(&self, index: Self::Index) -> bool;
fn range(&self) -> usize;
fn in_range(&self, n: usize) -> bool {
n < self.range()
}
}
impl Indexer for () {
type Index = ();
fn to_num(&self, _i: Self::Index) -> usize {
0
}
fn to_index(&self, n: usize) -> Self::Index {
assert!(self.in_range(n));
}
fn range(&self) -> usize {
1
}
fn is_valid(&self, _i: Self::Index) -> bool {
true
}
}
impl Indexer for usize {
type Index = usize;
fn to_num(&self, i: Self::Index) -> usize {
assert!(self.is_valid(i));
i
}
fn to_index(&self, n: usize) -> Self::Index {
assert!(self.in_range(n));
n
}
fn range(&self) -> usize {
*self
}
fn is_valid(&self, i: Self::Index) -> bool {
self.in_range(i)
}
}
#[derive(PartialEq, Eq, Debug, Clone)]
pub struct Rect {
height: usize,
width: usize,
}
impl Rect {
pub fn new(height: usize, width: usize) -> Rect {
Rect { height, width }
}
}
impl Indexer for Rect {
type Index = (usize, usize);
fn to_num(&self, i: Self::Index) -> usize {
assert!(self.is_valid(i));
let (j, k) = i;
j * self.width + k
}
fn to_index(&self, n: usize) -> Self::Index {
assert!(self.in_range(n));
let j = n / self.width;
let k = n - j * self.width;
(j, k)
}
fn range(&self) -> usize {
self.width * self.height
}
fn is_valid(&self, (j, k): Self::Index) -> bool {
(j < self.height) & (k < self.width)
}
}
#[cfg(test)]
mod tests {
use super::Indexer;
#[test]
fn unit() {
assert_eq!(().range(), 1);
assert_eq!(().to_index(0), ());
assert_eq!(().to_num(()), 0);
assert!(().is_valid(()));
}
#[test]
#[should_panic]
fn unit_num_oob() {
().to_index(1);
}
#[test]
fn integer() {
let n = 12;
assert_eq!(n.range(), n);
assert_eq!(n.to_index(6), 6);
assert_eq!(n.to_num(11), 11);
}
#[test]
#[should_panic]
fn integer_index_oob() {
let n = 19;
n.to_num(n);
}
#[test]
fn rect() {
use super::Rect;
let h = 7;
let w = 17;
let rect = Rect::new(h, w);
assert_eq!(rect.range(), h * w);
assert_eq!(rect.to_index(0), (0, 0));
assert_eq!(rect.to_index(w), (1, 0));
assert_eq!(rect.to_num((0, 0)), 0);
assert_eq!(rect.to_num((1, 0)), w);
}
#[test]
#[should_panic]
fn rect_index_oob() {
use super::Rect;
let h = 2;
let w = 3;
let rect = Rect::new(h, w);
rect.to_num((2, 2));
}
#[test]
#[should_panic]
fn rect_num_oob() {
use super::Rect;
let h = 2;
let w = 3;
let rect = Rect::new(h, w);
rect.to_index(rect.range());
} }