use std::convert::TryFrom; use std::fmt::{self, Debug}; use std::iter; use hdf5::types::{FixedAscii, FixedUnicode, VarLenArray, VarLenAscii, VarLenUnicode}; use hdf5::H5Type; use hdf5_metno as hdf5; use half::f16; use ndarray::{ArrayD, SliceInfo, SliceInfoElem}; use num_complex::Complex; use rand::distributions::Standard; use rand::distributions::{Alphanumeric, Uniform}; use rand::prelude::Distribution; use rand::prelude::{Rng, SliceRandom}; pub fn gen_shape(rng: &mut R, ndim: usize) -> Vec { iter::repeat(()).map(|_| rng.gen_range(0..11)).take(ndim).collect() } pub fn gen_ascii(rng: &mut R, len: usize) -> String { iter::repeat(()).map(|_| rng.sample(Alphanumeric)).map(char::from).take(len).collect() } /// Generate a random slice of elements inside the given `shape` dimension. pub fn gen_slice( rng: &mut R, shape: &[usize], ) -> SliceInfo, ndarray::IxDyn, ndarray::IxDyn> { let rand_slice: Vec = shape.into_iter().map(|s| gen_slice_one_dim(rng, *s)).collect(); SliceInfo::try_from(rand_slice).unwrap() } /// Generate a random 1D slice of the interval [0, shape). fn gen_slice_one_dim(rng: &mut R, shape: usize) -> ndarray::SliceInfoElem { if shape == 0 { return ndarray::SliceInfoElem::Slice { start: 0, end: None, step: 1 }; } if rng.gen_bool(0.1) { ndarray::SliceInfoElem::Index(rng.gen_range(0..shape) as isize) } else { let start = rng.gen_range(0..shape) as isize; let end = if rng.gen_bool(0.5) { None } else if rng.gen_bool(0.9) { Some(rng.gen_range(start..shape as isize)) } else { // Occasionally generate a slice with end < start. Some(rng.gen_range(0..shape as isize)) }; let step = if rng.gen_bool(0.9) { 1isize } else { rng.gen_range(1..shape * 2) as isize }; ndarray::SliceInfoElem::Slice { start, end, step } } } pub trait Gen: Sized + fmt::Debug { fn gen(rng: &mut R) -> Self; } macro_rules! impl_gen_primitive { ($ty:ty) => { impl Gen for $ty { fn gen(rng: &mut R) -> Self { rng.gen() } } }; ($ty:ty, $($tys:ty),+) => { impl_gen_primitive!($ty); impl_gen_primitive!($($tys),*); }; } impl_gen_primitive!(usize, isize, u8, u16, u32, u64, i8, i16, i32, i64, bool, f32, f64); macro_rules! impl_gen_tuple { ($t:ident) => ( impl<$t> Gen for ($t,) where $t: Gen { fn gen(rng: &mut R) -> Self { (<$t as Gen>::gen(rng),) } } ); ($t:ident, $($tt:ident),*) => ( impl<$t, $($tt),*> Gen for ($t, $($tt),*) where $t: Gen, $($tt: Gen),* { fn gen(rng: &mut R) -> Self { (<$t as Gen>::gen(rng), $(<$tt as Gen>::gen(rng)),*) } } impl_gen_tuple!($($tt),*); ); } impl_gen_tuple! { A, B, C, D, E, F, G, H, I, J, K, L } impl Gen for f16 { fn gen(rng: &mut R) -> Self { Self::from_f32(rng.gen()) } } impl Gen for Complex where Standard: Distribution, { fn gen(rng: &mut R) -> Self { Self::new(rng.gen(), rng.gen()) } } pub fn gen_vec(rng: &mut R, size: usize) -> Vec { iter::repeat(()).map(|_| T::gen(rng)).take(size).collect() } pub fn gen_arr(rng: &mut R, ndim: usize) -> ArrayD where T: H5Type + Gen, R: Rng + ?Sized, { let shape = gen_shape(rng, ndim); let size = shape.iter().product(); let vec = gen_vec(rng, size); ArrayD::from_shape_vec(shape, vec).unwrap() } impl Gen for FixedAscii { fn gen(rng: &mut R) -> Self { let len = rng.sample(Uniform::new_inclusive(0, N)); let dist = Uniform::new_inclusive(0, 127); let mut v = Vec::with_capacity(len); for _ in 0..len { v.push(rng.sample(dist)); } unsafe { FixedAscii::from_ascii_unchecked(&v) } } } impl Gen for FixedUnicode { fn gen(rng: &mut R) -> Self { let len = rng.sample(Uniform::new_inclusive(0, N)); let mut s = String::new(); for _ in 0..len { let c = rng.gen::(); if c != '\0' { if s.as_bytes().len() + c.len_utf8() >= len { break; } s.push(c); } } unsafe { FixedUnicode::from_str_unchecked(s) } } } impl Gen for VarLenAscii { fn gen(rng: &mut R) -> Self { let len = rng.sample(Uniform::new_inclusive(0, 8)); let dist = Uniform::new_inclusive(0, 127); let mut v = Vec::with_capacity(len); for _ in 0..len { v.push(rng.sample(dist)); } unsafe { VarLenAscii::from_ascii_unchecked(&v) } } } impl Gen for VarLenUnicode { fn gen(rng: &mut R) -> Self { let len = rng.sample(Uniform::new_inclusive(0, 8)); let mut s = String::new(); while s.len() < len { let c = rng.gen::(); if c != '\0' { s.push(c); } } unsafe { VarLenUnicode::from_str_unchecked(s) } } } impl Gen for VarLenArray { fn gen(rng: &mut R) -> Self { let len = rng.sample(Uniform::new_inclusive(0, 8)); let mut v = Vec::with_capacity(len); for _ in 0..len { v.push(Gen::gen(rng)); } VarLenArray::from_slice(&v) } } #[derive(H5Type, Clone, Copy, Debug, PartialEq)] #[repr(i16)] pub enum Enum { X = -2, Y = 3, } impl Gen for Enum { fn gen(rng: &mut R) -> Self { *[Enum::X, Enum::Y].choose(rng).unwrap() } } #[derive(H5Type, Clone, Copy, Debug, PartialEq)] #[repr(C)] pub struct TupleStruct(bool, Enum); impl Gen for TupleStruct { fn gen(rng: &mut R) -> Self { TupleStruct(Gen::gen(rng), Gen::gen(rng)) } } #[derive(H5Type, Clone, Debug, PartialEq)] #[repr(C)] pub struct FixedStruct { fa: FixedAscii<3>, fu: FixedUnicode<11>, tuple: (i8, u64, f32), array: [TupleStruct; 2], } impl Gen for FixedStruct { fn gen(rng: &mut R) -> Self { FixedStruct { fa: Gen::gen(rng), fu: Gen::gen(rng), tuple: (Gen::gen(rng), Gen::gen(rng), Gen::gen(rng)), array: [Gen::gen(rng), Gen::gen(rng)], } } } #[derive(H5Type, Clone, Debug, PartialEq)] #[repr(C)] pub struct VarLenStruct { va: VarLenAscii, vu: VarLenUnicode, vla: VarLenArray, } impl Gen for VarLenStruct { fn gen(rng: &mut R) -> Self { VarLenStruct { va: Gen::gen(rng), vu: Gen::gen(rng), vla: Gen::gen(rng) } } } #[derive(H5Type, Clone, Debug, PartialEq)] #[repr(C)] pub struct RenameStruct { first: i32, #[hdf5(rename = "field.second")] second: i64, } impl Gen for RenameStruct { fn gen(rng: &mut R) -> Self { RenameStruct { first: Gen::gen(rng), second: Gen::gen(rng) } } } #[derive(H5Type, Clone, Copy, Debug, PartialEq)] #[repr(C)] pub struct RenameTupleStruct(#[hdf5(rename = "my_boolean")] bool, #[hdf5(rename = "my_enum")] Enum); impl Gen for RenameTupleStruct { fn gen(rng: &mut R) -> Self { RenameTupleStruct(Gen::gen(rng), Gen::gen(rng)) } } #[derive(H5Type, Clone, Copy, Debug, PartialEq)] #[repr(i16)] pub enum RenameEnum { #[hdf5(rename = "coord.x")] X = -2, #[hdf5(rename = "coord.y")] Y = 3, } impl Gen for RenameEnum { fn gen(rng: &mut R) -> Self { *[RenameEnum::X, RenameEnum::Y].choose(rng).unwrap() } }