use core::hash::{Hash, Hasher};
use core::borrow::Borrow;
use core::marker::PhantomData;
use array_init::array_init;
use crate::LookUpError;
#[derive(Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
struct Patch<T> {
contents: [T; 64]
}
impl<T> Copy for Patch<T> where T: Copy{}
impl<T> Clone for Patch<T> where T: Clone{
fn clone(&self) -> Self {
Self{contents: self.contents.clone()}
}
}
impl<T> PartialEq for Patch<T> where T: PartialEq{
fn eq(&self, other: &Self) -> bool {
self.contents.eq(&other.contents)
}
}
impl<T> Eq for Patch<T> where T: Eq{}
impl<T> Hash for Patch<T> where T: Hash{
fn hash<H: Hasher>(&self, state: &mut H) {
for item in &self.contents {
item.hash(state);
}
}
}
impl<T> Patch<T> {
fn get(&self, x: usize, y: usize) -> &T {
return &self.contents[zorder_4bit_to_8bit(x as u8 & 0x07, y as u8 & 0x07) as usize];
}
fn set(&mut self, x: usize, y: usize, new_val: T) {
let i = zorder_4bit_to_8bit(x as u8 & 0x07, y as u8 & 0x07) as usize;
self.contents[i] = new_val;
}
}
fn patch_index(x: usize, y: usize, pwidth: usize) -> usize {
return (x >> 3) + ((y >> 3) * (pwidth));
}
fn patch_coords(pwidth: usize, pindex: usize) -> [(usize, usize); 64] {
let mut outbuffer = [(0usize, 0usize); 64];
let bx = (pindex % pwidth) << 3;
let by = (pindex / pwidth) << 3;
for i in 0..64 {
let bitmask = REVERSE_ZLUT[i];
let dx = bitmask & 0b00000111u8;
let dy = (bitmask >> 3u8) & 0b00000111u8;
outbuffer[i] = (bx + dx as usize, by + dy as usize);
}
return outbuffer;
}
#[derive(Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ZArray2D<T> {
width: usize,
height: usize,
pwidth: usize,
patches: Vec<Patch<T>>,
_phantomdata: PhantomData<T>,
}
impl<T> Clone for ZArray2D<T> where T: Clone{
fn clone(&self) -> Self {
Self{
width: self.width,
height: self.height,
pwidth: self.pwidth,
patches: self.patches.clone(),
_phantomdata: self._phantomdata
}
}
}
impl<T> PartialEq for ZArray2D<T> where T: PartialEq{
fn eq(&self, other: &Self) -> bool {
self.width == other.width
&& self.height == other.height
&& self.pwidth == other.pwidth
&& self.patches == other.patches
}
}
impl<T> Eq for ZArray2D<T> where T: Eq{}
impl<T> Hash for ZArray2D<T> where T: Hash{
fn hash<H: Hasher>(&self, state: &mut H) {
for patch in &self.patches {
patch.hash(state);
}
}
}
impl<T> ZArray2D<T> where T: Default {
pub fn new_with_default(width: usize, height: usize) -> ZArray2D<T> {
let pwidth = ((width-1) >> 3) + 1;
let pheight = ((height-1) >> 3) + 1;
let patch_count = pwidth * pheight;
let mut p = Vec::with_capacity(patch_count);
for _ in 0..patch_count {
let default_contents: [T; 64] = array_init(|_|T::default());
p.push(Patch { contents: default_contents });
}
return ZArray2D { width, height, pwidth, patches: p, _phantomdata: PhantomData };
}
}
impl<T> ZArray2D<T> where T: Copy {
pub fn new(width: usize, height: usize, default_val: T) -> ZArray2D<T> {
let pwidth = ((width-1) >> 3) + 1;
let pheight = ((height-1) >> 3) + 1;
let patch_count = pwidth * pheight;
let mut p = Vec::with_capacity(patch_count);
for _ in 0..patch_count {
p.push(Patch { contents: [default_val; 64] });
}
return ZArray2D { width, height, pwidth, patches: p, _phantomdata: PhantomData };
}
}
impl<T> ZArray2D<T> where T: Clone {
pub fn fill(&mut self, x1: usize, y1: usize, x2: usize, y2: usize, new_val: impl Borrow<T>)
-> Result<(), LookUpError> {
for y in y1..y2 {
for x in x1..x2 {
self.set(x, y, new_val.borrow().clone())?;
}
}
Ok(())
}
pub fn wrapped_fill(&mut self, x1: isize, y1: isize, x2: isize, y2: isize, new_val: impl Borrow<T>) {
for y in y1..y2 {
for x in x1..x2 {
self.wrapped_set(x, y, new_val.borrow().clone());
}
}
}
pub fn bounded_fill(&mut self, x1: isize, y1: isize, x2: isize, y2: isize, new_val: impl Borrow<T>) {
for y in y1..y2 {
for x in x1..x2 {
self.bounded_set(x, y, new_val.borrow().clone());
}
}
}
}
impl<T> ZArray2D<T> {
pub fn new_with_constructor(width: usize, height: usize, constructor: impl Fn((usize, usize)) -> T) -> ZArray2D<T> {
let pwidth = ((width-1) >> 3) + 1;
let pheight = ((height-1) >> 3) + 1;
let patch_count = pwidth * pheight;
let mut p = Vec::with_capacity(patch_count);
for pindex in 0..patch_count {
let lookup_table = patch_coords(pwidth, pindex);
let initial_contents: [T; 64] = array_init(|i| constructor(lookup_table[i]));
p.push(Patch { contents: initial_contents });
}
return ZArray2D { width, height, pwidth, patches: p, _phantomdata: PhantomData };
}
pub fn dimensions(&self) -> (usize, usize) {
return (self.width, self.height);
}
pub fn xsize(&self) -> usize {
return self.width;
}
pub fn width(&self) -> usize {
return self.xsize();
}
pub fn ysize(&self) -> usize {
return self.height;
}
pub fn height(&self) -> usize {
return self.ysize();
}
pub fn get(&self, x: usize, y: usize) -> Result<&T, LookUpError> {
if x < self.width && y < self.height {
Ok(self.patches[patch_index(x, y, self.pwidth)].get(x, y))
} else {
Err(LookUpError { coord: vec![x, y], bounds: vec![self.width, self.height] })
}
}
pub fn set(&mut self, x: usize, y: usize, new_val: T) -> Result<(), LookUpError> {
if x < self.width && y < self.height {
Ok(self.patches[patch_index(x, y, self.pwidth)].set(x, y, new_val))
} else {
Err(LookUpError { coord: vec![x, y], bounds: vec![self.width, self.height] })
}
}
pub fn get_unchecked(&self, x: usize, y: usize) -> &T {
return self.patches[patch_index(x, y, self.pwidth)].get(x, y);
}
pub fn set_unchecked(&mut self, x: usize, y: usize, new_val: T) {
self.patches[patch_index(x, y, self.pwidth)].set(x, y, new_val);
}
pub fn wrapped_get(&self, x: isize, y: isize) -> &T {
let x = (self.width as isize + (x % self.width as isize)) as usize % self.width;
let y = (self.height as isize + (y % self.height as isize)) as usize % self.height;
return &self.patches[patch_index(x, y, self.pwidth)].get(x, y);
}
pub fn wrapped_set(&mut self, x: isize, y: isize, new_val: T) {
let x = (self.width as isize + (x % self.width as isize)) as usize % self.width;
let y = (self.height as isize + (y % self.height as isize)) as usize % self.height;
self.patches[patch_index(x, y, self.pwidth)].set(x, y, new_val);
}
pub fn bounded_get(&self, x: isize, y: isize) -> Option<&T> {
if x >= 0 && y >= 0 && x < self.width as isize && y < self.height as isize {
return Some(&self.patches[patch_index(x as usize, y as usize, self.pwidth)]
.get(x as usize, y as usize));
} else {
return None;
}
}
pub fn bounded_set(&mut self, x: isize, y: isize, new_val: T) {
if x >= 0 && y >= 0 && x < self.width as isize && y < self.height as isize {
self.patches[patch_index(x as usize, y as usize, self.pwidth)]
.set(x as usize, y as usize, new_val);
} else {
}
}
pub fn iter(&self) -> ZArray2DIterator<T> {
ZArray2DIterator::new(self)
}
pub fn transform(&mut self, transform_fn: impl Fn((usize, usize), &T) -> T) {
for pindex in 0..self.patches.len() {
let patch_coords = patch_coords(self.pwidth, pindex);
for coord in patch_coords {
if coord.0 < self.width && coord.1 < self.height {
let old_val = self.get_unchecked(coord.0, coord.1);
self.set_unchecked(coord.0, coord.1, transform_fn(coord, old_val));
}
}
}
}
pub fn coords(&self) -> Vec<(usize, usize)> {
let mut out: Vec<(usize, usize)> = Vec::with_capacity(self.width * self.height);
for pindex in 0..self.patches.len() {
let patch_coords = patch_coords(self.pwidth, pindex);
for coord in patch_coords {
if coord.0 < self.width && coord.1 < self.height {
out.push(coord);
}
}
}
return out;
}
}
#[test]
fn check_patch_count_2d() {
let arr = ZArray2D::new(1, 1, 0u8);
assert_eq!(arr.patches.len(), 1, "Allocated wrong number of patches for array of size {}x{}", arr.width, arr.height);
let arr = ZArray2D::new(8, 8, 0u8);
assert_eq!(arr.patches.len(), 1, "Allocated wrong number of patches for array of size {}x{}", arr.width, arr.height);
let arr = ZArray2D::new(9, 8, 0u8);
assert_eq!(arr.patches.len(), 2, "Allocated wrong number of patches for array of size {}x{}", arr.width, arr.height);
let arr = ZArray2D::new(8, 9, 0u8);
assert_eq!(arr.patches.len(), 2, "Allocated wrong number of patches for array of size {}x{}", arr.width, arr.height);
let arr = ZArray2D::new(9, 9, 0u8);
assert_eq!(arr.patches.len(), 4, "Allocated wrong number of patches for array of size {}x{}", arr.width, arr.height);
}
#[derive(Debug)]
pub struct ZArray2DIteratorItem<'a, T> {
pub x: usize,
pub y: usize,
pub value: &'a T
}
enum IterState {
Start, Processing, Done
}
pub struct ZArray2DIterator<'a, T> {
array: &'a ZArray2D<T>,
patch: usize,
index: usize,
state: IterState
}
impl<'a, T> ZArray2DIterator<'a, T> {
fn new(array: &'a ZArray2D<T>) -> ZArray2DIterator<'a, T> {
if array.width == 0 || array.height == 0 {
ZArray2DIterator{array, patch: 0, index: 0, state: IterState::Done} } else {
ZArray2DIterator{array, patch: 0, index: 0, state: IterState::Start}
}
}
}
impl<'a, T> Iterator for ZArray2DIterator<'a, T> {
type Item = ZArray2DIteratorItem<'a, T>;
fn next(&mut self) -> Option<Self::Item> {
match &self.state {
IterState::Done=> None,
IterState::Start=> {
self.state = IterState::Processing;
Some(ZArray2DIteratorItem{x: 0, y: 0, value: &self.array.patches[0].contents[0]})
},
IterState::Processing => {
let mut x ; let mut y ;
loop {
self.index += 1;
if self.index >= 64 {
self.index = 0;
self.patch += 1;
}
let yx_lower_pits = REVERSE_ZLUT[self.index];
x = ((self.patch % self.array.pwidth) << 3) | (yx_lower_pits & 0x07) as usize;
y = ((self.patch / self.array.pwidth) << 3) | ((yx_lower_pits >> 3) & 0x07) as usize;
if x < self.array.width && y < self.array.height{
break;
}
if self.patch >= self.array.patches.len() {
self.state = IterState::Done;
return None;
}
}
Some(ZArray2DIteratorItem{x, y, value: &self.array.patches[self.patch].contents[self.index]})
}
}
}
}
const ZLUT: [u8; 16] = [
0b00000000,
0b00000001,
0b00000100,
0b00000101,
0b00010000,
0b00010001,
0b00010100,
0b00010101,
0b01000000,
0b01000001,
0b01000100,
0b01000101,
0b01010000,
0b01010001,
0b01010100,
0b01010101
];
const REVERSE_ZLUT: [u8; 64] = [
0 , 1, 8, 9, 2, 3, 10, 11, 16, 17, 24, 25, 18, 19, 26, 27,
4 , 5, 12, 13, 6, 7, 14, 15, 20, 21, 28, 29, 22, 23, 30, 31,
32, 33, 40, 41, 34, 35, 42, 43, 48, 49, 56, 57, 50, 51, 58, 59,
36, 37, 44, 45, 38, 39, 46, 47, 52, 53, 60, 61, 54, 55, 62, 63
];
pub fn zorder_4bit_to_8bit(x: u8, y: u8) -> u8 {
let x_bits = ZLUT[(x & 0x0F) as usize];
let y_bits = ZLUT[(y & 0x0F) as usize] << 1;
return y_bits | x_bits;
}
pub fn zorder_8bit_to_16bit(x:u8, y:u8) -> u16 {
return ((zorder_4bit_to_8bit(x >> 4, y >> 4) as u16) << 8) | zorder_4bit_to_8bit(x, y) as u16
}
pub fn zorder_16bit_to_32bit(x:u16, y:u16) -> u32 {
return ((zorder_8bit_to_16bit((x & 0xFF) as u8, (y & 0xFF) as u8) as u32) << 16) | zorder_8bit_to_16bit((x >> 8) as u8, (y >> 8) as u8) as u32
}