#![warn(clippy::pedantic)]
#![warn(clippy::cargo)]
#![allow(clippy::semicolon_if_nothing_returned)]
use std::fmt::Debug;
use std::ops::Deref;
use thiserror::Error;
pub trait KeyValue<'a> {
type K: Ord;
type V;
fn key(&'a self) -> Self::K;
fn value(&'a self) -> Self::V;
}
impl<'a, KEY: Ord, VALUE> KeyValue<'a> for (KEY, VALUE)
where
KEY: 'a,
VALUE: 'a,
{
type K = &'a KEY;
type V = &'a VALUE;
fn key(&'a self) -> Self::K {
&self.0
}
fn value(&'a self) -> Self::V {
&self.1
}
}
#[derive(Error, Debug, PartialEq, Eq, Hash)]
pub enum MapOfIndexesError {
#[error("At least two elements with same keys. Keys must be uniques.")]
DuplicateKeys,
#[error("No elements were found with this key.")]
KeyNotFound,
#[error("Attempted to push an element with a lower key than last element.")]
SmallerKey,
}
#[derive(Clone, Debug)]
pub struct MapOfIndexes<T> {
inner: Vec<T>,
}
impl<T> Deref for MapOfIndexes<T> {
type Target = Vec<T>;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<T: for<'a> KeyValue<'a>> Default for MapOfIndexes<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: for<'a> KeyValue<'a>> TryFrom<Vec<T>> for MapOfIndexes<T> {
type Error = MapOfIndexesError;
fn try_from(mut vec: Vec<T>) -> Result<Self, Self::Error> {
vec.sort_unstable_by(|a, b| a.key().cmp(&b.key()));
let duplicate = vec.windows(2).any(|w| w[0].key() == w[1].key());
if duplicate {
Err(MapOfIndexesError::DuplicateKeys)
} else {
Ok(Self { inner: vec })
}
}
}
impl<T: for<'a> KeyValue<'a>> MapOfIndexes<T> {
#[must_use]
pub fn new() -> Self {
Self { inner: Vec::new() }
}
#[must_use]
pub fn with_capacity(capacity: usize) -> Self {
Self {
inner: Vec::with_capacity(capacity),
}
}
#[inline]
pub fn push(&mut self, element: T) {
match self.push_checked(element) {
Ok(()) => (),
Err(err) => panic!("{}", err),
}
}
pub fn push_checked(&mut self, element: T) -> Result<(), MapOfIndexesError> {
if let Some(last) = self.last() {
if last.key() >= element.key() {
return Err(MapOfIndexesError::SmallerKey);
}
}
self.inner.push(element);
Ok(())
}
#[inline]
fn get_idx<'a>(&'a self, key: &<T as KeyValue<'a>>::K) -> Option<usize> {
self.binary_search_by(|t| t.key().cmp(key)).ok()
}
pub fn get_element<'a>(&'a self, key: &<T as KeyValue<'a>>::K) -> Option<&T> {
self.get_idx(key).map(|idx| &self[idx])
}
pub fn get_value<'a>(&'a self, key: &<T as KeyValue<'a>>::K) -> Option<<T as KeyValue<'_>>::V> {
self.get_idx(key).map(|idx| self[idx].value())
}
pub fn set(&mut self, element: T) -> Result<T, MapOfIndexesError> {
let idx_opt = self.get_idx(&element.key());
idx_opt
.map(|idx| std::mem::replace(&mut self.inner[idx], element))
.ok_or(MapOfIndexesError::KeyNotFound)
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
pub struct CombinedKeyValue<T, const KEY_NB_BITS: u8, const VALUE_NB_BITS: u8>(T);
impl<T, const KEY_NB_BITS: u8, const VALUE_NB_BITS: u8> AsRef<T>
for CombinedKeyValue<T, KEY_NB_BITS, VALUE_NB_BITS>
{
fn as_ref(&self) -> &T {
&self.0
}
}
impl<T: TryFrom<u128> + Into<u128>, const KEY_NB_BITS: u8, const VALUE_NB_BITS: u8>
CombinedKeyValue<T, { KEY_NB_BITS }, { VALUE_NB_BITS }>
where
<T as TryFrom<u128>>::Error: Debug,
{
pub fn safety_check() {
assert!(
!(std::mem::size_of::<T>() * 8 < (KEY_NB_BITS + VALUE_NB_BITS).into()),
"KEY_NB_BITS + VALUE_NB_BITS value is higher than the number of bits of the backup type."
);
}
pub fn new<K: Into<u128>, V: Into<u128>>(key: K, value: V) -> Self {
let key_uint: u128 = key.into();
let value_uint: u128 = value.into();
assert!(key_uint < 2u128.pow(KEY_NB_BITS.into()));
assert!(value_uint < 2u128.pow(VALUE_NB_BITS.into()));
Self(
T::try_from(key_uint | (value_uint << KEY_NB_BITS))
.expect("Run `Self::safety_check` and should never panic"),
)
}
}
impl<'a, T: TryFrom<u128> + Ord + Copy, const KEY_NB_BITS: u8, const VALUE_NB_BITS: u8> KeyValue<'a>
for CombinedKeyValue<T, { KEY_NB_BITS }, { VALUE_NB_BITS }>
where
u128: From<T>,
<T as TryFrom<u128>>::Error: Debug,
{
type K = T;
type V = T;
fn key(&self) -> Self::K {
T::try_from(u128::from(self.0) & (u128::MAX >> (u128::BITS - u32::from(KEY_NB_BITS))))
.expect("Run `Self::safety_check` and should never panic")
}
fn value(&self) -> Self::V {
T::try_from(u128::from(self.0) >> KEY_NB_BITS)
.expect("Run `Self::safety_check` and should never panic")
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_push_sorted() {
let mut s = MapOfIndexes::<(i128, u8)>::new();
s.push((1, 1));
s.push((2, 1));
assert_eq!(&s.inner, &[(1, 1), (2, 1)]);
}
#[test]
fn test_get_1() {
let mut s = MapOfIndexes::<(i128, u8)>::new();
s.push((10, 10));
s.push((11, 11));
s.push((12, 12));
s.push((13, 13));
for i in 10..14 {
assert_eq!(s.get_value(&&i128::from(i)), Some(&(i as u8)));
}
}
#[test]
fn test_get_2() {
let mut s = MapOfIndexes::<(u8, u8)>::new();
for i in 0..u8::MAX {
s.push((i, i));
assert_eq!(s.get_value(&&i), Some(&i));
}
}
#[test]
fn test_try_from() {
let s: Vec<(i32, u64)> = vec![(1, 1), (-100, 2), (3, 15)];
let sorted_map: MapOfIndexes<(i32, u64)> = s.try_into().unwrap();
assert_eq!(&sorted_map.inner, &[(-100, 2), (1, 1), (3, 15)])
}
#[test]
fn test_try_from_fail_duplicate() {
let s: Vec<(i32, u64)> = vec![(1, 1), (1, 2), (3, 15)];
let sorted_map_err = MapOfIndexes::<(i32, u64)>::try_from(s).err().unwrap();
assert_eq!(sorted_map_err, MapOfIndexesError::DuplicateKeys)
}
#[test]
fn test_combined_key_value_type() {
type CombinedU8 = CombinedKeyValue<u8, 4, 4>;
CombinedU8::safety_check();
}
#[test]
#[should_panic]
fn test_combined_key_value_type_error_key() {
type CombinedU8 = CombinedKeyValue<u8, 5, 4>;
CombinedU8::safety_check();
}
#[test]
#[should_panic]
fn test_combined_key_value_type_error_value() {
type CombinedU8 = CombinedKeyValue<u8, 4, 5>;
CombinedU8::safety_check();
}
#[test]
fn test_combined_key_value_new() {
type CombinedU8 = CombinedKeyValue<u8, 4, 4>;
CombinedU8::safety_check();
let x = CombinedU8::new(4u8, 3u8);
assert_eq!(x.key(), 4u8);
assert_eq!(x.value(), 3u8);
}
#[test]
fn test_get_element_on_large_map() {
let mut map = MapOfIndexes::<(u32, u64)>::with_capacity(400000);
for i in 0..400000 {
map.push((i as u32, i as u64))
}
println!("{:?}", &map[0..10]);
for j in 0..400000 {
assert_eq!(map.get_element(&&(j as u32)), Some(&(j as u32, j as u64)))
}
}
}