Some utils

This commit is contained in:
Maxime Augier 2023-12-26 12:09:40 +01:00
parent a07df912c6
commit b6eb9a1737
4 changed files with 252 additions and 0 deletions

65
myutils/Cargo.lock generated Normal file
View File

@ -0,0 +1,65 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "myutils"
version = "0.1.0"
dependencies = [
"thiserror",
]
[[package]]
name = "proc-macro2"
version = "1.0.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
dependencies = [
"proc-macro2",
]
[[package]]
name = "syn"
version = "2.0.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee659fb5f3d355364e1f3e5bc10fb82068efbf824a1e9d1c9504244a6469ad53"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "1.0.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83a48fd946b02c0a526b2e9481c8e2a17755e47039164a86c4070446e3a4614d"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7fbe9b594d6568a6a1443250a7e67d80b74e1e96f6d1715e1e21cc1888291d3"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"

9
myutils/Cargo.toml Normal file
View File

@ -0,0 +1,9 @@
[package]
name = "myutils"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
thiserror = "1.0.52"

1
myutils/src/lib.rs Normal file
View File

@ -0,0 +1 @@
pub mod matrix;

177
myutils/src/matrix.rs Normal file
View File

@ -0,0 +1,177 @@
//! A large number of logical games, by virtue of existing on paper, use 2-dimensional structures.
//! This module implement packed matrices, without the overhead of supporting multiple dimensions.
use std::ops::{Index, IndexMut};
use thiserror::Error;
/// A Matrix of dynamic size, with elements in `T`.
/// Indexing exposes rows as slices. Individual elements of matrix `m`
/// can be accessed with `m[x][y]`.
#[derive(Clone,Debug,PartialEq,Eq)]
pub struct Matrix<T> {
stride: usize,
vec: Vec<T>,
}
#[derive(PartialEq, Eq, Debug,Error)]
#[error("incorrect shape")]
pub struct ShapeError;
impl <T> Matrix<T> {
/// Create a new matrix from a vector of elements in row-major order.
/// Will fail if the length of `vec` doesn't match the requested shape.
pub fn new(vec: Vec<T>, shape: (usize,usize)) -> Result<Self, ShapeError> {
if vec.len() != shape.0 * shape.1 { return Err(ShapeError) }
Ok(Self { vec, stride: shape.1 })
}
/// Total number of elements in the matrix. Equal to `shape.0 * shape.1`
pub fn len(&self) -> usize {
self.vec.len()
}
/// Shape of the matrix
pub fn shape(&self) -> (usize, usize) {
(self.vec.len() / self.stride, self.stride)
}
/// Creates a new matrix of the same shape by applying a closure to every element
pub fn map<U,F>(&self, f: F) -> Matrix<U>
where F: FnMut(&T) -> U
{
Matrix { vec: self.vec.iter().map(f).collect(), stride: self.stride }
}
/// Iterates over the matrix rows
pub fn lines(&self) -> impl Iterator<Item=&[T]> + '_ {
(0..self.vec.len())
.step_by(self.stride)
.map(|i| &self.vec[i..][..self.stride])
}
/// Iterate over all the coordinate pairs in row-major order
pub fn indices(&self) -> impl Iterator<Item=(usize,usize)> {
let (h,w) = self.shape();
(0..h).flat_map(move |x| (0..w).map(move |y| (x,y)))
}
/// Lists all the neighbors of the given location, truncating at the edge.
pub fn neighbors(&self, pos: (usize, usize)) -> Vec<(usize,usize)> {
let (x,y) = pos;
let (h, w) = self.shape();
let mut neighs = Vec::with_capacity(9);
let mut row = |x| {
if y > 0 { neighs.push((x, y-1)) };
neighs.push((x, y));
if y+1 < w { neighs.push((x, y+1))};
};
if x > 0 { row(x-1) };
row(x);
if x+1 < h { row(x+1) };
neighs
}
/// Create a new matrix by applying in parallel an operation to every pair of elements from
/// two source matrices of identical shape.
pub fn zip_with<U,V,F>(&self, other: &Matrix<U>, f: F) -> Result<Matrix<V>, ShapeError>
where F: FnMut((&T, &U)) -> V
{
if self.shape() != other.shape() {
return Err(ShapeError)
}
Ok(Matrix {
stride: self.stride,
vec: self.vec.iter().zip(&other.vec).map(f).collect(),
})
}
}
impl <T> Index<usize> for Matrix<T> {
type Output = [T];
fn index(&self, index: usize) -> &Self::Output {
&self.vec[index * self.stride..][..self.stride]
}
}
impl <T> IndexMut<usize> for Matrix<T> {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
&mut self.vec[index * self.stride..][..self.stride]
}
}
macro_rules! umat {
[$e:expr; $shape:expr] => {
$crate::util::matrix::Matrix::new(vec![$e; $shape.0 * $shape.1], $shape).unwrap()
};
}
pub(crate) use umat;
#[allow(unused_macros)]
macro_rules! mat {
[ $( $( $e:expr ),+ );* ] => {{
let mut v = vec![];
let (mut h, mut t) = (0,0);
$(
$(
v.push($e); t += 1;
)+
h += 1;
)*
$crate::util::matrix::Matrix::new(v, (h, t/h)).unwrap()
}}
}
#[allow(unused_imports)]
pub(crate) use mat;
#[cfg(test)]
mod test {
use crate::util::matrix::ShapeError;
use super::Matrix;
#[test]
fn shape() {
assert_eq!(ShapeError, Matrix::new(vec![1,2,3], (2,2)).unwrap_err())
}
#[test]
fn lines() {
let m = Matrix::new(vec![1,2,3,4,5,6], (3,2)).unwrap();
let m2: Vec<_> = m.lines().collect();
assert_eq!(&m2, &[&[1,2], &[3,4], &[5,6]]);
}
#[test]
fn indices() {
let m = Matrix::new(vec![(); 6], (3,2)).unwrap();
let idxs: Vec<_> = m.indices().collect();
assert_eq!(vec![(0,0),(0,1),(1,0),(1,1),(2,0),(2,1)], idxs);
}
#[test]
fn access() {
let m = Matrix::new(vec![1,2,3,4], (2,2)).unwrap();
assert_eq!(m[0][0], 1);
assert_eq!(m[0][1], 2);
assert_eq!(m[1][0], 3);
assert_eq!(m[1][1], 4);
}
#[test]
fn neighbors() {
let m = umat![(); (4,4)];
assert_eq!(m.neighbors((0,0)), vec![(0,0),(0,1),(1,0),(1,1)]);
assert_eq!(m.neighbors((0,2)), vec![(0,1),(0,2),(0,3), (1,1), (1,2), (1,3)]);
assert_eq!(m.neighbors((1,2)), vec![(0,1),(0,2),(0,3), (1,1), (1,2), (1,3), (2,1), (2,2), (2,3)]);
assert_eq!(m.neighbors((3,3)), vec![(2,2),(2,3),(3,2),(3,3)]);
}
}