use crate::{ArcisField, STATISTICAL_SECURITY_FACTOR};
use ff::PrimeField;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum DataSize {
PowerOfTwo(u8),
Full, }
impl DataSize {
pub fn size(&self, full_size: Size) -> Size {
match self {
DataSize::PowerOfTwo(pow) => (1 as Size) << pow,
DataSize::Full => full_size,
}
}
}
type Location = usize;
type Size = u16;
#[derive(Debug, Clone, Copy)]
#[non_exhaustive]
pub struct PackLocation {
pub index: Location,
pub bit_offset: Size,
}
impl PackLocation {
fn new(index: Location, bit_offset: Size) -> Self {
PackLocation { index, bit_offset }
}
}
#[derive(Debug, Default)]
struct CurrentState {
container_size: Size,
n_full_containers: Location,
loads: Vec<Size>,
last_insert: Option<Location>,
}
impl CurrentState {
pub fn new(container_size: Size) -> CurrentState {
CurrentState {
container_size,
..Default::default()
}
}
fn left_load(&self, index: Location) -> Size {
if index == 0 {
self.container_size
} else {
self.loads[index - 1]
}
}
fn location_load(&self, index: Location) -> Size {
self.loads.get(index).copied().unwrap_or(0)
}
fn insert_at(&mut self, index: Location, item_size: Size) -> Size {
let res = self.location_load(index);
let new_load = res + item_size;
assert!(new_load <= self.left_load(index));
assert!(new_load <= self.container_size);
if index >= self.loads.len() {
assert_eq!(index, self.loads.len());
self.loads.resize(index + 1, 0);
}
self.loads[index] = new_load;
self.last_insert = Some(index);
res
}
fn can_insert_at(&mut self, index: Location, item_size: Size) -> bool {
let at_size = self.location_load(index);
let new_size = at_size + item_size;
new_size <= self.container_size
}
pub fn insert(&mut self, data_size: DataSize) -> PackLocation {
match data_size {
DataSize::PowerOfTwo(pow) => {
let item_size = (1 as Size) << pow;
let last = self.last_insert.unwrap_or(0);
let insert_loc = [self.n_full_containers, last]
.into_iter()
.find(|index| self.can_insert_at(*index, item_size))
.unwrap_or(last + 1);
let old_size = self.location_load(insert_loc);
self.insert_at(insert_loc, item_size);
PackLocation::new(insert_loc, old_size)
}
DataSize::Full => {
let res = PackLocation::new(self.n_full_containers, 0);
assert_eq!(self.n_full_containers, self.loads.len());
self.n_full_containers += 1;
self.loads.push(self.container_size);
res
}
}
}
pub fn state(self) -> Vec<Option<Size>> {
let len = self.loads.len();
(0..len)
.map(|index| {
if index < self.n_full_containers {
None
} else {
Some(self.loads[index])
}
})
.collect()
}
}
impl DataSize {
fn pack(data: Vec<DataSize>, container_size: Size) -> (Vec<PackLocation>, Vec<Option<Size>>) {
let mut to_sort: Vec<_> = data
.into_iter()
.enumerate()
.map(|(i, size)| (size, i))
.collect();
to_sort.sort_by(|a, b| b.0.cmp(&a.0));
let mut current_state = CurrentState::new(container_size);
let mut res = vec![PackLocation::new(0, 0); to_sort.len()];
for (size, index) in to_sort {
res[index] = current_state.insert(size);
}
(res, current_state.state())
}
pub fn pack_arcis(data: Vec<DataSize>) -> (Vec<PackLocation>, Vec<Option<Size>>) {
Self::pack(data, Self::PACKING_SIZE)
}
pub const PACKING_SIZE: Size =
ArcisField::CAPACITY as Size - (STATISTICAL_SECURITY_FACTOR as Size);
}
#[cfg(test)]
mod tests {
use super::*;
use rand::Rng;
fn test_pack(sizes: Vec<DataSize>, container_size: Size, expected_len: Option<usize>) {
let computed_expected = {
let max_exp = sizes.iter().fold(0, |acc, x| {
if let DataSize::PowerOfTwo(pow) = x {
acc.max(*pow)
} else {
acc
}
});
let mut pow_count = vec![0usize; 1 + max_exp as usize];
let mut n_full = 0usize;
for size in &sizes {
match size {
DataSize::PowerOfTwo(pow) => {
pow_count[*pow as usize] += 1;
}
DataSize::Full => {
n_full += 1;
}
}
}
let mut free_spaces = 0usize;
let mut n_used_by_pows = 0usize;
for (exp, count) in pow_count.iter().enumerate().rev() {
let current_pow = 1 << exp;
if container_size & current_pow != 0 {
free_spaces += n_used_by_pows * (current_pow as usize);
}
let count_spaces = count * current_pow as usize;
if count_spaces <= free_spaces {
free_spaces -= count_spaces;
continue;
}
let remaining_to_pack = (count_spaces - free_spaces) / current_pow as usize;
let n_packable_per_pack = container_size >> exp;
let ceil = remaining_to_pack.div_ceil(n_packable_per_pack as usize);
n_used_by_pows += ceil;
free_spaces = (ceil * n_packable_per_pack as usize - remaining_to_pack)
* current_pow as usize;
}
n_full + n_used_by_pows
};
if let Some(expected_len) = expected_len {
assert_eq!(computed_expected, expected_len);
}
let (chosen_pack, v) = DataSize::pack(sizes.clone(), container_size);
assert_eq!(v.len(), computed_expected);
let mut packs = vec![false; computed_expected * container_size as usize];
for (size, location) in std::iter::zip(sizes, chosen_pack) {
let size_begin = location.bit_offset;
let location = location.index;
assert!(location < computed_expected);
let data_size = size.size(container_size);
let size_end = size_begin + data_size;
assert!(size_end <= container_size);
for index in size_begin..size_end {
let location = location * container_size as usize + index as usize;
assert!(!packs[location]);
packs[location] = true;
}
}
}
#[test]
fn powers_of_two() {
let rng = &mut crate::utils::test_rng::get();
for exp in [1u8, 2, 3, 4, 5, 6, 7, 8] {
let pow_2 = (1 as Size) << exp;
for is_size_exact_pow_2 in [true, false, false, false] {
let offset = if is_size_exact_pow_2 {
0
} else {
rng.gen_range(0..pow_2)
};
let container_size = pow_2 + offset;
for n_items in [1, 4, 16, 64, 256, 1024, 4096] {
let mut sizes = Vec::new();
for _ in 0..n_items {
if rng.gen_bool(0.5) {
sizes.push(DataSize::PowerOfTwo(rng.gen_range(0..=exp)));
} else {
sizes.push(DataSize::Full);
}
}
let optimal = if is_size_exact_pow_2 {
Some(
sizes
.iter()
.map(|size| size.size(container_size) as usize)
.sum::<usize>()
.div_ceil(container_size as usize),
)
} else {
None
};
test_pack(sizes, container_size, optimal);
}
}
}
}
#[test]
fn all_full() {
let container_size = 7;
let n_data = 63;
let sizes = vec![DataSize::Full; n_data];
test_pack(sizes, container_size, Some(n_data));
}
}