use core::ops::Index;
use std::{
fs::File,
io::{BufWriter, Read, Write},
path::Path,
};
use distances::Number;
use crate::Dataset;
use super::Instance;
#[derive(Debug)]
pub struct VecDataset<I: Instance, U: Number> {
name: String,
data: Vec<I>,
metric: fn(&I, &I) -> U,
is_expensive: bool,
permuted_indices: Option<Vec<usize>>,
}
impl<I: Instance, U: Number> VecDataset<I, U> {
pub fn new(name: String, data: Vec<I>, metric: fn(&I, &I) -> U, is_expensive: bool) -> Self {
Self {
name,
data,
metric,
is_expensive,
permuted_indices: None,
}
}
#[must_use]
pub fn data(&self) -> &[I] {
&self.data
}
#[must_use]
pub fn data_owned(self) -> Vec<I> {
self.data
}
}
impl<I: Instance, U: Number> Index<usize> for VecDataset<I, U> {
type Output = I;
fn index(&self, index: usize) -> &Self::Output {
self.data.index(index)
}
}
impl<I: Instance, U: Number> Dataset<I, U> for VecDataset<I, U> {
fn type_name(&self) -> String {
format!("VecDataset<{}>", I::type_name())
}
fn name(&self) -> &str {
&self.name
}
fn cardinality(&self) -> usize {
self.data.len()
}
fn is_metric_expensive(&self) -> bool {
self.is_expensive
}
fn metric(&self) -> fn(&I, &I) -> U {
self.metric
}
fn set_permuted_indices(&mut self, indices: Option<&[usize]>) {
self.permuted_indices = indices.map(<[usize]>::to_vec);
}
fn swap(&mut self, left: usize, right: usize) -> Result<(), String> {
self.data.swap(left, right);
Ok(())
}
fn permuted_indices(&self) -> Option<&[usize]> {
self.permuted_indices.as_deref()
}
fn make_shards(mut self, max_cardinality: usize) -> Vec<Self> {
let mut shards = Vec::new();
while self.data.len() > max_cardinality {
let at = self.data.len() - max_cardinality;
let chunk = self.data.split_off(at);
let name = format!("{}-shard-{}", self.name, shards.len());
shards.push(Self::new(name, chunk, self.metric, self.is_expensive));
}
self.name = format!("{}-shard-{}", self.name, shards.len());
shards.push(self);
shards
}
fn save(&self, path: &Path) -> Result<(), String> {
let mut handle = BufWriter::new(File::create(path).map_err(|e| e.to_string())?);
let type_name = self.type_name();
handle
.write_all(&type_name.len().to_le_bytes())
.and_then(|()| handle.write_all(type_name.as_bytes()))
.map_err(|e| e.to_string())?;
let name = self.name.clone();
handle
.write_all(&name.len().to_le_bytes())
.and_then(|()| handle.write_all(name.as_bytes()))
.map_err(|e| e.to_string())?;
let cardinality_bytes = self.data.len().to_le_bytes();
handle.write_all(&cardinality_bytes).map_err(|e| e.to_string())?;
let permutation = self
.permuted_indices
.as_ref()
.map_or(Vec::new(), |p| p.iter().flat_map(|i| i.to_le_bytes()).collect());
let permutation_bytes = permutation.len().to_le_bytes();
handle
.write_all(&permutation_bytes)
.and_then(|()| handle.write_all(&permutation))
.map_err(|e| e.to_string())?;
for row in &self.data {
row.save(&mut handle)?;
}
Ok(())
}
fn load(path: &Path, metric: fn(&I, &I) -> U, is_expensive: bool) -> Result<Self, String> {
let mut handle = File::open(path).map_err(|e| e.to_string())?;
{
let mut num_type_bytes = vec![0; usize::num_bytes()];
handle.read_exact(&mut num_type_bytes).map_err(|e| e.to_string())?;
let num_type_bytes = <usize as Number>::from_le_bytes(&num_type_bytes);
let mut type_buf = vec![0; num_type_bytes];
handle.read_exact(&mut type_buf).map_err(|e| e.to_string())?;
let type_name = String::from_utf8(type_buf).map_err(|e| e.to_string())?;
let actual_type_name = format!("VecDataset<{}>", I::type_name());
if type_name != actual_type_name {
return Err(format!(
"Invalid type. File has data of type {type_name} but dataset was constructed with type {actual_type_name}"
));
}
};
let name = {
let mut num_name_bytes = vec![0; usize::num_bytes()];
handle.read_exact(&mut num_name_bytes).map_err(|e| e.to_string())?;
let num_name_bytes = <usize as Number>::from_le_bytes(&num_name_bytes);
let mut name_buf = vec![0; num_name_bytes];
handle.read_exact(&mut name_buf).map_err(|e| e.to_string())?;
String::from_utf8(name_buf).map_err(|e| e.to_string())?
};
let cardinality = {
let mut cardinality_buf = vec![0; usize::num_bytes()];
handle.read_exact(&mut cardinality_buf).map_err(|e| e.to_string())?;
<usize as Number>::from_le_bytes(&cardinality_buf)
};
let permutation = {
let mut permutation_buf = vec![0; usize::num_bytes()];
handle.read_exact(&mut permutation_buf).map_err(|e| e.to_string())?;
if <usize as Number>::from_le_bytes(&permutation_buf) == 0 {
None
} else {
let mut permutation_buf = vec![0; 8 * cardinality];
handle.read_exact(&mut permutation_buf).map_err(|e| e.to_string())?;
let permutation = permutation_buf
.chunks(8)
.map(<usize as Number>::from_le_bytes)
.collect::<Vec<_>>();
Some(permutation)
}
};
let data = (0..cardinality)
.map(|_| I::load(&mut handle))
.collect::<Result<Vec<_>, _>>()?;
Ok(Self {
name,
data,
metric,
is_expensive,
permuted_indices: permutation,
})
}
}