use std::slice::{Chunks, ChunksMut};
use serde::{Serialize, Deserialize};
use crate::nc_node_info::NodeID;
use crate::nc_error::NCError;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Array2D<T> {
width: u64,
height: u64,
data: Vec<T>,
}
impl<T: Clone + Copy> Array2D<T> {
pub fn new(width: u64, height: u64, initial: T) -> Array2D<T> {
assert!(width > 0);
assert!(height > 0);
let data = vec![initial; (width * height) as usize];
Array2D {
width, height, data,
}
}
fn index(&self, x: u64, y: u64) -> usize {
((self.width * y) + x) as usize
}
pub fn get(&self, x: u64, y: u64) -> T {
self.data[self.index(x, y)]
}
pub fn set(&mut self, x: u64, y: u64, value: T) {
let index = self.index(x, y);
self.data[index] = value;
}
pub fn set_region(&mut self, dest_x: u64, dest_y: u64, source: &Array2D<T>) {
for x in 0..source.width {
for y in 0..source.height {
self.set(dest_x + x, dest_y + y, source.get(x, y))
}
}
}
pub fn split_rows(&self) -> Chunks<'_, T> {
self.data.chunks(self.width as usize)
}
pub fn split_row_mut(&mut self) -> ChunksMut<'_, T> {
self.data.chunks_mut(self.width as usize)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Array2DChunk<T> {
chunk_width: u64,
chunk_height: u64,
rest_width: u64,
rest_height: u64,
chunks_x: u64,
chunks_y: u64,
array2d: Array2D<T>,
}
impl<T: Clone + Copy> Array2DChunk<T> {
pub fn new(width: u64, height: u64, chunk_width: u64, chunk_height: u64, initial: T) -> Array2DChunk<T> {
assert!(width > 0);
assert!(height > 0);
assert!(chunk_width > 0);
assert!(chunk_height > 0);
assert!(chunk_width < width);
assert!(chunk_height < height);
let a = width / chunk_width;
let rest_width = width - (a * chunk_width);
let b = height / chunk_height;
let rest_height = height - (b * chunk_height);
let chunks_x = if rest_width == 0 {a} else {a + 1};
let chunks_y = if rest_height == 0 {b} else {b + 1};
let array2d = Array2D::new(width, height, initial);
Array2DChunk {
chunk_width, chunk_height,
rest_width, rest_height,
chunks_x, chunks_y, array2d,
}
}
pub fn num_of_chunks(&self) -> u64 {
self.chunks_x * self.chunks_y
}
pub fn get_chunk_property(&self, chunk_id: u64) -> (u64, u64, u64, u64) {
let cy = chunk_id / self.chunks_x;
let cx = chunk_id - (cy * self.chunks_x);
let width = if (cx == (self.chunks_x - 1)) && (self.rest_width > 0) { self.rest_width } else { self.chunk_width };
let height = if (cy == (self.chunks_y - 1)) && (self.rest_height > 0) { self.rest_height } else { self.chunk_height };
(cx * self.chunk_width, cy * self.chunk_height, width, height)
}
pub fn set_chunk(&mut self, chunk_id: u64, source: &Array2D<T>) -> Result<(), NCError> {
let (x, y, width, height) = self.get_chunk_property(chunk_id);
if (width == source.width) && (height == source.height) {
self.array2d.set_region(x, y, source);
Ok(())
} else {
Err(NCError::Array2DDimensionMismatch((width, height), (source.width, source.height)))
}
}
pub fn get(&self, x: u64, y: u64) -> T {
self.array2d.get(x, y)
}
pub fn dimensions(&self) -> (u64, u64) {
(self.array2d.width, self.array2d.height)
}
}
#[derive(Debug, Clone, PartialEq)]
enum ChunkStatus {
Empty,
Processing,
Finished,
}
#[derive(Debug, Clone)]
pub struct Chunk<T> {
pub data: T,
pub node_id: NodeID,
status: ChunkStatus,
}
impl<T> Chunk<T> {
pub fn set_empty(&mut self) {
self.status = ChunkStatus::Empty
}
pub fn is_empty(&self) -> bool {
self.status == ChunkStatus::Empty
}
pub fn set_processing(&mut self, node_id: NodeID) {
self.status = ChunkStatus::Processing;
self.node_id = node_id;
}
pub fn is_processing(&self, node_id: NodeID) -> bool {
self.status == ChunkStatus::Processing &&
self.node_id == node_id
}
pub fn set_finished(&mut self) {
self.status = ChunkStatus::Finished;
}
}
#[derive(Debug, Clone)]
pub struct ChunkList<T> {
chunks: Vec<Chunk<T>>
}
impl<T> ChunkList<T> {
pub fn new() -> Self {
ChunkList{ chunks: Vec::new() }
}
pub fn stats(&self) -> (u64, u64, u64) {
let mut empty: u64 = 0;
let mut processing: u64 = 0;
let mut finished: u64 = 0;
for chunk in self.chunks.iter() {
match chunk.status {
ChunkStatus::Empty => empty += 1,
ChunkStatus::Processing => processing += 1,
ChunkStatus::Finished => finished += 1,
}
}
(empty, processing, finished)
}
pub fn get_next_free_chunk(&mut self) -> Option<(usize, &mut Chunk<T>)> {
self.chunks.iter().position(|chunk| chunk.is_empty()).map(move |index| (index, &mut self.chunks[index]))
}
pub fn get(&mut self, index: usize) -> &mut Chunk<T> {
&mut self.chunks[index]
}
pub fn heartbeat_timeout(&mut self, nodes: &[NodeID]) {
for chunk in self.chunks.iter_mut() {
for node_id in nodes.iter() {
if chunk.is_processing(*node_id) {
chunk.set_empty()
}
}
}
}
pub fn push(&mut self, data: T) {
self.chunks.push(Chunk{ data, node_id: NodeID::random(), status: ChunkStatus::Empty});
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[should_panic]
fn test_a2d_new1() {
let _a2d = Array2D::new(0, 1, 99);
}
#[test]
#[should_panic]
fn test_a2d_new2() {
let _a2d = Array2D::new(1, 0, 99);
}
#[test]
fn test_a2d_index() {
let a2d = Array2D::new(10, 10, 0);
assert_eq!(a2d.index(0, 0), 0);
assert_eq!(a2d.index(1, 0), 1);
assert_eq!(a2d.index(0, 1), 10);
assert_eq!(a2d.index(1, 1), 11);
assert_eq!(a2d.index(9, 9), 99);
}
#[test]
fn test_a2d_set_and_get() {
let mut a2d = Array2D::new(10, 10, 0);
assert_eq!(a2d.get(0, 0), 0);
a2d.set(0, 0, 55);
assert_eq!(a2d.get(0, 0), 55);
assert_eq!(a2d.get(4, 8), 0);
a2d.set(4, 8, 123);
assert_eq!(a2d.get(4, 8), 123);
assert_eq!(a2d.get(9, 9), 0);
a2d.set(9, 9, 999);
assert_eq!(a2d.get(9, 9), 999);
}
#[test]
fn test_a2d_set_region1() {
let mut a2d = Array2D::new(10, 10, 0);
let region = Array2D::new(1, 1, 789);
assert_eq!(a2d.get(6, 5), 0);
a2d.set_region(6, 5, ®ion);
assert_eq!(a2d.get(6, 5), 789);
}
#[test]
fn test_a2d_set_region2() {
let mut a2d = Array2D::new(10, 10, 0);
let region = Array2D::new(2, 2, 2345);
assert_eq!(a2d.get(2, 7), 0);
assert_eq!(a2d.get(3, 7), 0);
assert_eq!(a2d.get(2, 8), 0);
assert_eq!(a2d.get(3, 8), 0);
a2d.set_region(2, 7, ®ion);
assert_eq!(a2d.get(2, 7), 2345);
assert_eq!(a2d.get(3, 7), 2345);
assert_eq!(a2d.get(2, 8), 2345);
assert_eq!(a2d.get(3, 8), 2345);
}
#[test]
fn test_a2d_set_region3() {
let mut a2d = Array2D::new(2, 2, 0);
let region = Array2D::new(2, 2, 8765);
assert_eq!(a2d.get(0, 0), 0);
assert_eq!(a2d.get(1, 0), 0);
assert_eq!(a2d.get(0, 1), 0);
assert_eq!(a2d.get(1, 1), 0);
a2d.set_region(0, 0, ®ion);
assert_eq!(a2d.get(0, 0), 8765);
assert_eq!(a2d.get(1, 0), 8765);
assert_eq!(a2d.get(0, 1), 8765);
assert_eq!(a2d.get(1, 1), 8765);
}
#[test]
fn test_a2d_chunk_new1() {
let a2d_chunk = Array2DChunk::new(100, 100, 20, 20, 0);
assert_eq!(a2d_chunk.chunk_width, 20);
assert_eq!(a2d_chunk.chunk_height, 20);
assert_eq!(a2d_chunk.rest_width, 0);
assert_eq!(a2d_chunk.rest_height, 0);
assert_eq!(a2d_chunk.chunks_x, 5);
assert_eq!(a2d_chunk.chunks_y, 5);
}
#[test]
fn test_a2d_chunk_new2() {
let a2d_chunk = Array2DChunk::new(100, 100, 21, 21, 0);
assert_eq!(a2d_chunk.chunk_width, 21);
assert_eq!(a2d_chunk.chunk_height, 21);
assert_eq!(a2d_chunk.rest_width, 16);
assert_eq!(a2d_chunk.rest_height, 16);
assert_eq!(a2d_chunk.chunks_x, 5);
assert_eq!(a2d_chunk.chunks_y, 5);
}
#[test]
#[should_panic]
fn test_a2d_chunk_new3() {
let _a2d_chunk = Array2DChunk::new(0, 100, 10, 10, 0);
}
#[test]
#[should_panic]
fn test_a2d_chunk_new4() {
let _a2d_chunk = Array2DChunk::new(100, 0, 10, 10, 0);
}
#[test]
#[should_panic]
fn test_a2d_chunk_new5() {
let _a2d_chunk = Array2DChunk::new(100, 100, 0, 10, 0);
}
#[test]
#[should_panic]
fn test_a2d_chunk_new6() {
let _a2d_chunk = Array2DChunk::new(100, 100, 10, 0, 0);
}
#[test]
#[should_panic]
fn test_a2d_chunk_new7() {
let _a2d_chunk = Array2DChunk::new(100, 100, 110, 10, 0);
}
#[test]
#[should_panic]
fn test_a2d_chunk_new8() {
let _a2d_chunk = Array2DChunk::new(100, 100, 10, 110, 0);
}
#[test]
fn test_a2d_chunk_num_of_chunks1() {
let a2d_chunk = Array2DChunk::new(100, 100, 20, 20, 0);
assert_eq!(a2d_chunk.num_of_chunks(), 25);
}
#[test]
fn test_a2d_chunk_num_of_chunks2() {
let a2d_chunk = Array2DChunk::new(100, 100, 21, 21, 0);
assert_eq!(a2d_chunk.num_of_chunks(), 25);
}
#[test]
fn test_a2d_chunk_get_chunk_property1() {
let a2d_chunk = Array2DChunk::new(120, 120, 40, 40, 0);
assert_eq!(a2d_chunk.num_of_chunks(), 9);
assert_eq!(a2d_chunk.get_chunk_property(0), (0, 0, 40, 40));
assert_eq!(a2d_chunk.get_chunk_property(1), (40, 0, 40, 40));
assert_eq!(a2d_chunk.get_chunk_property(2), (80, 0, 40, 40));
assert_eq!(a2d_chunk.get_chunk_property(3), (0, 40, 40, 40));
assert_eq!(a2d_chunk.get_chunk_property(4), (40, 40, 40, 40));
assert_eq!(a2d_chunk.get_chunk_property(5), (80, 40, 40, 40));
assert_eq!(a2d_chunk.get_chunk_property(6), (0, 80, 40, 40));
assert_eq!(a2d_chunk.get_chunk_property(7), (40, 80, 40, 40));
assert_eq!(a2d_chunk.get_chunk_property(8), (80, 80, 40, 40));
}
#[test]
fn test_a2d_chunk_get_chunk_property2() {
let a2d_chunk = Array2DChunk::new(120, 120, 41, 41, 0);
assert_eq!(a2d_chunk.num_of_chunks(), 9);
assert_eq!(a2d_chunk.get_chunk_property(0), (0, 0, 41, 41));
assert_eq!(a2d_chunk.get_chunk_property(1), (41, 0, 41, 41));
assert_eq!(a2d_chunk.get_chunk_property(2), (82, 0, 38, 41));
assert_eq!(a2d_chunk.get_chunk_property(3), (0, 41, 41, 41));
assert_eq!(a2d_chunk.get_chunk_property(4), (41, 41, 41, 41));
assert_eq!(a2d_chunk.get_chunk_property(5), (82, 41, 38, 41));
assert_eq!(a2d_chunk.get_chunk_property(6), (0, 82, 41, 38));
assert_eq!(a2d_chunk.get_chunk_property(7), (41, 82, 41, 38));
assert_eq!(a2d_chunk.get_chunk_property(8), (82, 82, 38, 38));
}
#[test]
fn test_a2d_chunk_set_chunk1() {
let mut a2d_chunk = Array2DChunk::new(6, 6, 2, 2, 0);
let a2d = Array2D::new(2, 2, 112233);
assert_eq!(a2d_chunk.get(2, 0), 0);
assert_eq!(a2d_chunk.get(2, 1), 0);
assert_eq!(a2d_chunk.get(3, 0), 0);
assert_eq!(a2d_chunk.get(3, 1), 0);
a2d_chunk.set_chunk(1, &a2d).unwrap();
assert_eq!(a2d_chunk.get(2, 0), 112233);
assert_eq!(a2d_chunk.get(2, 1), 112233);
assert_eq!(a2d_chunk.get(3, 0), 112233);
assert_eq!(a2d_chunk.get(3, 1), 112233);
}
#[test]
fn test_a2d_chunk_set_chunk2() {
let mut a2d_chunk = Array2DChunk::new(6, 6, 2, 2, 0);
let a2d = Array2D::new(2, 2, 456456);
assert_eq!(a2d_chunk.get(2, 2), 0);
assert_eq!(a2d_chunk.get(2, 3), 0);
assert_eq!(a2d_chunk.get(3, 2), 0);
assert_eq!(a2d_chunk.get(3, 3), 0);
a2d_chunk.set_chunk(4, &a2d).unwrap();
assert_eq!(a2d_chunk.get(2, 2), 456456);
assert_eq!(a2d_chunk.get(2, 3), 456456);
assert_eq!(a2d_chunk.get(3, 2), 456456);
assert_eq!(a2d_chunk.get(3, 3), 456456);
}
}