use cryiorust::frame::Array;
use integrustio::integrator::Pattern;
use itertools::izip;
use miniz_oxide::deflate::CompressionLevel::BestCompression;
use miniz_oxide::{deflate, inflate};
use std::fs::File;
use std::io::{BufRead, Write};
use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::{fmt, fs, io};
pub const KEY_DONT_TOUCH: &'static str = "Bubble_normalized";
pub const KEY_DUBBLE_MONITOR: &'static str = "Monitor";
pub const KEY_DUBBLE_PHOTO: &'static str = "Photo";
pub trait Decompressor {
fn decompress(&self) -> io::Result<Vec<u8>>;
}
impl Decompressor for str {
fn decompress(&self) -> io::Result<Vec<u8>> {
let packed = match base64::decode(self) {
Ok(packed) => packed,
Err(e) => return Err(io::Error::new(io::ErrorKind::InvalidData, e)),
};
match inflate::decompress_to_vec_zlib(&packed) {
Ok(decompressed) => Ok(decompressed),
Err(e) => Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("could not decompress: {:?}", e),
)),
}
}
}
pub trait Compressor {
fn compress(&self) -> String;
}
impl Compressor for [u8] {
fn compress(&self) -> String {
let packed = deflate::compress_to_vec_zlib(self, BestCompression as u8);
base64::encode(&packed)
}
}
pub trait Texter {
fn to_text<P: io::Write>(&self, writer: &mut P) -> io::Result<()>;
}
impl Texter for Pattern {
fn to_text<P: io::Write>(&self, writer: &mut P) -> io::Result<()> {
for (position, intensity, sigma) in
izip!(self.positions.iter(), &self.intensity, &self.sigma)
{
writeln!(writer, "{} {} {}", position, intensity, sigma)?;
}
Ok(())
}
}
pub trait Resizer {
fn resize(&self, bin: usize) -> Option<Array>;
fn bindim(bin: usize, mut dim: usize) -> usize {
while dim % bin != 0 {
dim -= 1;
}
dim / bin
}
}
impl Resizer for Array {
fn resize(&self, bin: usize) -> Option<Array> {
if bin <= 1 {
return None;
}
let binf = bin as f64;
let dim1 = Self::bindim(bin, self.dim1());
let dim2 = Self::bindim(bin, self.dim2());
let dim1bin = dim1 * bin;
let dim2bin = dim2 * bin;
let mut data = vec![0.; dim1 * dim2];
for i in 0..dim1 {
if i + bin >= dim1bin {
continue;
}
let l = i * dim2;
let ibin = i * bin;
for j in 0..dim2 {
if j + bin >= dim2bin {
continue;
}
let k = l + j;
let jbin = j * bin;
for x in 0..bin {
let offset = (ibin + x) * self.dim2() + jbin;
for y in 0..bin {
data[k] += self.data()[offset + y];
}
}
data[k] /= binf;
}
}
Some(Array::with_data(dim1, dim2, data))
}
}
pub enum WalkResult {
None(io::Error),
Dir(PathBuf),
File(PathBuf),
}
pub trait MultiColumnWalker: AsRef<Path> + fmt::Debug {
fn walk(&self, name: Arc<String>, ext: Arc<String>) {
let entries = match fs::read_dir(self.as_ref()) {
Ok(entries) => entries,
Err(err) => {
warn!(
"Failed to read path {:?} for multi column file: {}",
self, err
);
return;
}
};
let mut files = vec![];
for entry in entries {
let entry = match entry {
Ok(entry) => entry,
Err(err) => {
warn!(
"Failed to process entry in {:?} for multi column file: {}",
self, err
);
return;
}
};
match entry.process() {
WalkResult::None(err) => {
warn!(
"Failed to process entry in {:?} for multi column file: {}",
self, err
);
return;
}
WalkResult::Dir(path) => {
let name = name.clone();
let ext = ext.clone();
rayon::spawn(move || Arc::new(path).walk(name, ext));
}
WalkResult::File(path) => {
if let Some(path_ext) = path.extension() {
if let Some(path_ext) = path_ext.to_str() {
if path_ext == ext.as_ref() {
files.push(path);
}
}
}
}
}
}
files.sort();
self.save(files, name);
}
fn save(&self, files: Vec<PathBuf>, name: Arc<String>) {
let name = name.as_str();
let mut out = self.as_ref().to_path_buf();
out.push(name);
match Self::write(&out, files) {
Ok(_) => info!("Multicolumn file {:?} is written", out),
Err(e) => warn!("Failed to write multicolumn file {:?}: {}", out, e),
}
}
fn write(name: &PathBuf, files: Vec<PathBuf>) -> io::Result<()> {
let mut data = vec![];
let mut names = vec![];
let mut line_counter = 0;
for path in &files {
if path.as_os_str() == name {
continue;
}
let reader = io::BufReader::new(File::open(path)?);
let mut i = 0;
for line in reader.lines() {
let line = line?;
data.push(line);
i += 1;
}
names.push(path);
if line_counter == 0 {
line_counter = i
} else if line_counter != i {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(
"Integrated files in {:?} have different number of bins",
path
),
));
}
}
if line_counter == 0 || names.len() == 0 {
return Ok(());
}
let mut writer = io::BufWriter::new(File::create(&name)?);
write!(writer, "#q")?;
for name in &names {
if let Some(stem) = name.file_stem() {
if let Some(name) = stem.to_str() {
write!(writer, " {}", name)?;
}
}
}
write!(writer, "\n")?;
let mut write_q = true;
for i in 0..line_counter {
for j in 0..names.len() {
let sl = data[j * line_counter + i].split_ascii_whitespace();
for (k, item) in sl.enumerate() {
match k {
0 => {
if write_q {
write!(writer, "{}", item)?;
write_q = false;
}
}
1 => write!(writer, " {}", item)?,
_ => break,
}
}
}
write!(writer, "\n")?;
write_q = true;
}
Ok(())
}
}
impl MultiColumnWalker for String {}
impl MultiColumnWalker for PathBuf {}
pub trait EntryProcesser {
fn process(&self) -> WalkResult;
}
impl EntryProcesser for fs::DirEntry {
fn process(&self) -> WalkResult {
let path = self.path();
match self.metadata() {
Ok(md) => {
if md.is_dir() {
WalkResult::Dir(path)
} else {
WalkResult::File(path)
}
}
Err(e) => WalkResult::None(e),
}
}
}
pub fn py_stamp() -> f64 {
chrono::Local::now().timestamp_nanos() as f64 / 1e9
}