use std::collections::{ HashSet };
use std::fmt::{ Debug, Display };
use std::hash::{ Hash };
use bon::bon;
use itertools::Itertools;
use num_traits as nums;
use rand::prelude::*;
use rand::seq::{ SliceRandom };
use crate::*;
pub type WList<V,W> = WeightedList<V,W>;
#[derive(Clone, Hash, PartialEq, Eq, Default, Debug)]
pub struct WeightedList<V, W: Weight>
{
data: Vec<WeightedItem<V,W>>
}
impl<V, W: Weight> WeightedList<V,W>
{
pub fn new() -> Self
{
Self { data: Vec::new() }
}
pub fn with_capacity(capacity: usize) -> Self
{
Self { data: Vec::with_capacity(capacity) }
}
pub fn from_expanded<I>(values: I) -> Self
where
I: IntoIterator<Item = V>,
V: PartialEq,
{
let mut out = WeightedList::new();
for value in values {
if let Some(existing) = out.iter_mut().find(|item| item.value == value) {
existing.weight += W::one();
}
else {
out.push_value(value);
}
}
out
}
}
#[macro_export]
macro_rules! wlist {
() => { WeightedList::new() };
($( ($weight:expr, $value:expr) ),* $(,)?) =>
{
WeightedList::from_iter(
[
$( ($weight, $value), )*
].into_iter()
)
};
}
impl<V, W: Weight> FromIterator<(W,V)> for WeightedList<V,W> {
fn from_iter<I>(pairs: I) -> Self
where I: IntoIterator<Item = (W,V)>
{
Self {
data:
pairs.into_iter()
.map(
|(weight, value)| WeightedItem::new(weight, value)
)
.collect::<Vec<_>>()
}
}
}
impl<V, W: Weight> FromIterator<WeightedItem<V,W>> for WeightedList<V,W>
{
fn from_iter<I>(items: I) -> Self
where I: IntoIterator<Item = WeightedItem<V,W>>
{
let mut data = vec![];
for item in items {
data.push(item);
}
Self { data }
}
}
impl<V, W: Weight> From<Vec<(W,V)>> for WeightedList<V,W> {
fn from(pairs: Vec<(W,V)>) -> Self {
pairs.into_iter().collect()
}
}
impl<V, W: Weight> From<Vec<WeightedItem<V,W>>> for WeightedList<V,W> {
fn from(data: Vec<WeightedItem<V,W>>) -> Self {
Self { data }
}
}
impl<V, W: Weight, const N: usize> From<[(W,V); N]> for WeightedList<V,W> {
fn from(pairs: [(W,V); N]) -> Self {
pairs.into_iter().collect()
}
}
impl<V, W: Weight, const N: usize> From<[WeightedItem<V,W>; N]> for WeightedList<V,W> {
fn from(pairs: [WeightedItem<V,W>; N]) -> Self {
pairs.into_iter().collect()
}
}
impl<V, W: Weight> From<WeightedList<V,W>> for Vec<WeightedItem<V,W>> {
fn from(list: WeightedList<V,W>) -> Self {
list.data
}
}
impl<V, W: Weight> AsRef<Vec<WeightedItem<V,W>>> for WeightedList<V,W> {
fn as_ref(&self) -> &Vec<WeightedItem<V,W>> {
&self.data
}
}
impl<V, W: Weight> AsRef<[WeightedItem<V,W>]> for WeightedList<V,W> {
fn as_ref(&self) -> &[WeightedItem<V,W>] {
&self.data
}
}
impl<V, W: Weight> AsMut<Vec<WeightedItem<V,W>>> for WeightedList<V,W> {
fn as_mut(&mut self) -> &mut Vec<WeightedItem<V,W>> {
&mut self.data
}
}
impl<V, W: Weight> AsMut<[WeightedItem<V,W>]> for WeightedList<V,W> {
fn as_mut(&mut self) -> &mut [WeightedItem<V,W>] {
&mut self.data
}
}
impl<V, W: Weight> std::ops::Deref for WeightedList<V,W> {
type Target = [WeightedItem<V,W>];
fn deref(&self) -> &Self::Target {
self.data.deref()
}
}
impl<V, W: Weight> std::ops::DerefMut for WeightedList<V,W> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.data.deref_mut()
}
}
impl<V, W: Weight> Extend<WeightedItem<V,W>> for WeightedList<V,W>
{
fn extend<T>(&mut self, iter: T)
where T: IntoIterator<Item = WeightedItem<V,W>>
{
for item in iter {
self.push_item(item);
}
}
}
impl<V, W: Weight> Display for WeightedList<V,W>
where
V: Display,
W: Display,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
{
write!(f,
"WeightedList[{}]",
self.data.iter().map(|item| item.to_string()).join(", ")
)
}
}
impl<V, W: Weight> std::ops::Index<W> for WeightedList<V,W>
{
type Output = WeightedItem<V,W>;
fn index(&self, weighted_index: W) -> &Self::Output
{
let mut t = W::zero();
for item in &self.data {
t += item.weight;
if t > weighted_index {
return item;
}
};
panic!(
"index out of bounds: the len is {:?} but the index is {:?}",
self.len(), weighted_index
);
}
}
impl<V, W: Weight> std::ops::IndexMut<W> for WeightedList<V,W>
{
fn index_mut(&mut self, weighted_index: W) -> &mut Self::Output
{
let idx = self._unweight_index_(weighted_index);
&mut self.data[idx]
}
}
impl<V, W: Weight> IntoIterator for WeightedList<V,W>
{
type Item = WeightedItem<V,W>;
type IntoIter = <Vec<Self::Item> as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.data.into_iter()
}
}
impl<'l, V, W: Weight> IntoIterator for &'l WeightedList<V,W>
{
type Item = &'l WeightedItem<V,W>;
type IntoIter = std::slice::Iter<'l, WeightedItem<V,W>>;
fn into_iter(self) -> Self::IntoIter {
self.data.iter()
}
}
impl<'l, V, W: Weight> IntoIterator for &'l mut WeightedList<V,W>
{
type Item = &'l mut WeightedItem<V,W>;
type IntoIter = std::slice::IterMut<'l, WeightedItem<V,W>>;
fn into_iter(self) -> Self::IntoIter {
self.data.iter_mut()
}
}
impl<V, W: Weight> WeightedList<V,W>
{
pub fn weights(&self) -> impl Iterator<Item = W>
{
self.data.iter().map(|item| item.weight)
}
pub fn values(&self) -> impl Iterator<Item = &V>
{
self.data.iter().map(|item| &item.value)
}
pub fn items(&self) -> Vec<&WeightedItem<V,W>>
{
self.data.iter().collect_vec()
}
pub fn raw(&self) -> impl Iterator<Item = (W,&V)>
{
self.data.iter().map(|item| (item.weight, &item.value))
}
pub fn expanded(&self) -> impl Iterator<Item = &V>
where W: nums::PrimInt
{
self.data
.iter()
.flat_map(|item| std::iter::repeat_n(
&item.value,
nums::cast::<W, usize>(item.weight).unwrap_or(0)
))
}
pub fn collect_weights(&self) -> Vec<W>
{
self.weights().collect_vec()
}
pub fn collect_values(&self) -> Vec<&V>
{
self.values().collect_vec()
}
}
impl<V, W: Weight> WeightedList<V,W>
{
pub fn len(&self) -> W
{
self.data.iter().map(|item| item.weight).sum()
}
pub fn capacity(&self) -> usize
{
self.data.capacity()
}
pub fn total_items(&self) -> usize
{
self.data.len()
}
pub fn is_empty(&self) -> bool
{
self.data.is_empty()
}
pub fn is_zero(&self) -> bool
{
self.is_empty()
|| self.data.iter().all(|item| item.weight == W::zero())
}
pub fn has_negative_weights(&self) -> bool
{
self.data.iter().any(|item| item.weight < W::zero())
}
}
impl<V, W: Weight> WeightedList<V,W>
where
V: Clone
{
pub fn sorted(&self) -> Self
where V: Eq, W: Ord
{
let mut out = self.clone();
out.sort();
out
}
pub fn reversed(&self) -> Self
{
let mut out = self.clone();
out.reverse();
out
}
}
impl<V, W: Weight> WeightedList<V,W>
{
pub fn reserve(&mut self, additional: usize) -> &mut Self
{
self.data.reserve(additional);
self
}
pub fn push_item(&mut self, item: WeightedItem<V,W>) -> &mut Self
{
self.data.push(item);
self
}
pub fn push_new_item(&mut self, weight: W, value: V) -> &mut Self
{
self.push_item(WeightedItem { weight, value })
}
pub fn push_value(&mut self, value: V) -> &mut Self
{
self.push_item(WeightedItem::unit(value))
}
pub fn insert_item(&mut self,
weighted_index: W,
item: WeightedItem<V,W>
) -> &mut Self
{
self.data.insert(self._unweight_index_nopanic_(weighted_index), item);
self
}
pub fn insert_new_item(&mut self,
weighted_index: W,
(weight, value): (W, V)
) -> &mut Self
{
self.insert_item(weighted_index, WeightedItem::new(weight, value))
}
pub fn insert_value(&mut self,
weighted_index: W,
value: V,
) -> &mut Self
{
self.insert_item(weighted_index, WeightedItem::unit(value))
}
pub fn append(&mut self, other: &mut WeightedList<V,W>) -> &mut Self
{
self.data.append(&mut other.data);
self
}
pub fn reverse(&mut self) -> &mut Self
{
self.data.reverse();
self
}
pub fn swap(&mut self, left: W, right: W) -> &mut Self
{
let l = self._unweight_index_(left);
let r = self._unweight_index_(right);
self.data.swap(l, r);
self
}
pub fn pop(&mut self) -> Option<WeightedItem<V,W>>
{
self.data.pop()
}
pub fn pop_if(&mut self,
predicate: impl FnOnce(&mut WeightedItem<V,W>) -> bool
) -> Option<WeightedItem<V,W>>
{
self.data.pop_if(predicate)
}
pub fn remove_at(&mut self, weighted_index: W) -> WeightedItem<V,W>
{
self.data.remove(self._unweight_index_(weighted_index))
}
pub fn truncate(&mut self, len: W) -> &mut Self
where W: Debug
{
if len == W::zero() {
return self.clear();
}
let mut t = W::zero();
let mut n = 0;
for (i, each) in self.iter_mut().enumerate() {
t += each.weight;
if t >= len {
if t > len {
each.weight -= t - len;
}
n = i + 1;
break;
}
}
self.data.truncate(n);
self
}
pub fn retain<F>(&mut self, predicate: F) -> &mut Self
where F: FnMut(&WeightedItem<V,W>) -> bool
{
self.data.retain(predicate);
self
}
pub fn retain_mut<F>(&mut self, predicate: F) -> &mut Self
where F: FnMut(&mut WeightedItem<V,W>) -> bool
{
self.data.retain_mut(predicate);
self
}
pub fn clear(&mut self) -> &mut Self
{
self.data.clear();
self
}
}
impl<V, W: Weight> WeightedList<V,W>
{
pub fn contains_value(&self, value: &V) -> bool
where V: PartialEq
{
self.data.iter().any(|item| item.value == *value)
}
pub fn contains_weight(&self, weight: W) -> bool
{
self.data.iter().any(|item| item.weight == weight)
}
}
impl<V, W: Weight> WeightedList<V,W>
{
pub fn prune(&mut self) -> &mut Self
{
self.data.retain(|item| item.weight > W::zero());
self
}
pub fn pruned(&self) -> Self
where V: Clone
{
let mut out = self.clone();
out.prune();
out
}
pub fn remove_value_first(&mut self, value: &V) -> &mut Self
where V: PartialEq
{
self.remove_first_where(|item| item.value == *value)
}
pub fn remove_value_last(&mut self, value: &V) -> &mut Self
where V: PartialEq
{
self.remove_last_where(|item| item.value == *value)
}
pub fn remove_first_where<F>(&mut self, predicate: F) -> &mut Self
where F: FnMut(&WeightedItem<V,W>) -> bool
{
if let Some(idx) = self.iter().position(predicate) {
self.data.remove(idx);
}
self
}
pub fn remove_last_where<F>(&mut self, predicate: F) -> &mut Self
where F: FnMut(&WeightedItem<V,W>) -> bool
{
if let Some(idx) = self.iter().rposition(predicate) {
self.data.remove(idx);
}
self
}
pub fn zero_all_weights(&mut self) -> &mut Self
{
for item in &mut self.data {
item.weight = W::zero();
}
self
}
pub fn set_all_weights(&mut self, weight: W) -> &mut Self
{
for item in &mut self.data {
item.weight = weight;
}
self
}
pub fn normalised(&mut self) -> Result<WeightedList<V, f64>, &str>
where V: Clone
{
let total;
if let Some(t) = nums::cast::<W, f64>(self.len()) {
total = t;
} else {
return Err("Error casting total weights to `f64`");
};
let items =
self.data
.iter()
.map(|item| {
let weight = nums::cast::<W, f64>(item.weight)?;
Some(WeightedItem {
weight: weight / total,
value: item.value.clone()
})
})
.collect::<Option<Vec<WeightedItem<V, f64>>>>()
;
match items {
Some(data) => Ok(WeightedList { data }),
None => Err("Error casting weights to `f64`")
}
}
}
impl<V, W: Weight> WeightedList<V,W>
where
V: PartialEq
{
pub fn merge_item(&mut self, item: WeightedItem<V,W>) -> &mut Self
{
if let Some(existing) = self.data.iter_mut().find(|each| each.value == item.value) {
existing.weight += item.weight;
}
else {
self.data.push(item);
}
self
}
pub fn merge_new_item(&mut self, weight: W, value: V) -> &mut Self
{
self.merge_item(WeightedItem { weight, value })
}
pub fn merge_value(&mut self, value: V) -> &mut Self
{
self.merge_item(WeightedItem::unit(value))
}
pub fn merge_with(&mut self, other: WeightedList<V,W>) -> &mut Self
{
for item in other {
self.merge_item(item);
}
self
}
pub fn merge_duplicates(&mut self) -> &mut Self
{
let orig = std::mem::replace(self, WeightedList::new());
self.merge_with(orig);
self
}
}
impl<V, W: Weight> WeightedList<V,W>
where
V: Clone
{
pub fn take_one_at(&mut self, weighted_index: W) -> WeightedItem<V,W>
{
self.take_by_at(W::one(), weighted_index)
}
pub fn take_by_at(&mut self, decrement: W, weighted_index: W) -> WeightedItem<V,W>
{
let idx = self._unweight_index_(weighted_index);
let target = &mut self.data[idx];
if decrement >= target.weight {
target.weight = W::zero();
self.data.remove(idx)
}
else {
target.weight -= decrement;
target.clone()
}
}
pub fn take_entire_at(&mut self, weighted_index: W) -> WeightedItem<V,W>
{
self.remove_at(weighted_index)
}
}
impl<V, W: Weight> WeightedList<V,W>
{
fn _get_random_weighted_index_up_to_<RNG>(&self, rng: &mut RNG, upper: W) -> Option<W>
where RNG: Rng + ?Sized
{
let len: f64 = nums::cast::<W, f64>(upper)?;
let scalar: f64 = rng.random();
let idx = (len * scalar).floor();
let out = nums::cast::<f64, W>(idx)?;
Some(out)
}
fn _get_random_weighted_index_<RNG>(&self, rng: &mut RNG) -> Option<W>
where RNG: Rng + ?Sized
{
self._get_random_weighted_index_up_to_(rng, self.len())
}
pub fn select_random_value<RNG>(&self, rng: &mut RNG) -> Option<&V>
where RNG: Rng + ?Sized
{
let out = &self.select_random_item(rng)?.value;
Some(out)
}
pub fn select_random_item<RNG>(&self, rng: &mut RNG) -> Option<&WeightedItem<V,W>>
where RNG: Rng + ?Sized
{
if self.data.is_empty() { return None }
let idx = self._get_random_weighted_index_(rng)?;
let out = &self[idx];
Some(out)
}
}
impl<V, W: Weight> WeightedList<V,W>
where
V: Clone
{
pub fn take_one_random<RNG>(&mut self, rng: &mut RNG) -> Option<WeightedItem<V,W>>
where RNG: Rng + ?Sized
{
self.take_by_random(rng, W::one())
}
pub fn take_by_random<RNG>(&mut self, rng: &mut RNG, decrement: W) -> Option<WeightedItem<V,W>>
where RNG: Rng + ?Sized
{
if self.data.is_empty() { return None }
let idx = self._get_random_weighted_index_(rng)?;
let out = self.take_by_at(decrement, idx);
Some(out)
}
pub fn take_entire_random<RNG>(&mut self, rng: &mut RNG) -> Option<WeightedItem<V,W>>
where RNG: Rng + ?Sized
{
if self.data.is_empty() { return None }
let idx = self._get_random_weighted_index_(rng)?;
let out = self.take_entire_at(idx);
Some(out)
}
}
#[bon]
impl<V, W: Weight> WeightedList<V,W>
where
V: Clone + Eq
{
#[builder]
pub fn select_random_values<RNG>(&self,
rng: &mut RNG,
count: usize,
replace: Option<bool>,
decrement: Option<W>,
) -> Vec<V>
where RNG: Rng + ?Sized
{
let replace = replace.unwrap_or(true);
let decrement = decrement.unwrap_or(W::one());
if replace {
(0..count)
.filter_map(|_| self.select_random_value(rng).cloned())
.collect()
}
else {
let mut pool = self.clone();
let mut out = Vec::with_capacity(
if count > 16 {
count.min(self.total_items())
} else {
count
}
);
for _ in 0..count {
if self.is_zero() { break }
if let Some(item) = pool.take_by_random(rng, decrement) {
out.push(item.value);
}
}
out
}
}
#[builder]
pub fn select_random_values_unique<RNG>(&self,
rng: &mut RNG,
count: usize,
merge_duplicates: Option<bool>,
) -> Vec<V>
where
RNG: Rng + ?Sized,
V: Clone + Eq,
{
let merge_duplicates = merge_duplicates.unwrap_or(false);
let mut l = self.len();
let mut seen_indices = HashSet::<usize>::new();
let mut out = Vec::with_capacity(
if count > 16 {
count.min(self.total_items())
} else {
count
}
);
for _ in 0..count
{
if l == W::zero() { break }
let Some(weighted_index) = self._get_random_weighted_index_up_to_(rng, l) else { continue };
let Some(idx) = self._unweight_index_skipping_(weighted_index, &seen_indices) else { continue };
let target = &self.data[idx];
if !merge_duplicates {
seen_indices.insert(idx);
l -= target.weight;
}
else {
let (indices, weights): (Vec<usize>, Vec<W>) =
self.data.iter().enumerate()
.filter_map(
|(i, item)| (item.value == target.value).then_some((i, item.weight))
)
.unzip();
seen_indices.extend(indices);
l -= weights.into_iter().sum::<W>();
}
out.push(target.value.clone());
}
out
}
#[builder]
pub fn take_random_values<RNG>(&mut self,
rng: &mut RNG,
count: usize,
take_entire: Option<bool>,
decrement: Option<W>,
) -> Vec<V>
where RNG: Rng + ?Sized
{
let take_entire = take_entire.unwrap_or(true);
let decrement = decrement.unwrap_or(W::one());
let mut n = 0;
let mut out = Vec::with_capacity(count);
loop
{
n += 1;
if n > count || self.data.is_empty() { break }
if let Some(item) = {
if take_entire { self.take_entire_random(rng) }
else { self.take_by_random(rng, decrement) }
} {
out.push(item.value.clone());
}
}
out
}
#[builder]
pub fn take_random_values_unique<RNG>(&mut self,
rng: &mut RNG,
count: usize,
decrement: Option<W>,
) -> Vec<V>
where RNG: Rng + ?Sized,
{
let decrement = decrement.unwrap_or(W::one());
let mut l = self.len();
let mut seen_indices = HashSet::<usize>::new();
let mut out = Vec::with_capacity(
if count > 16 {
count.min(self.total_items())
} else {
count
}
);
for _ in 0..count
{
if l == W::zero() || self.is_zero() { break }
let Some(weighted_index) = self._get_random_weighted_index_up_to_(rng, l) else { continue };
let Some(idx) = self._unweight_index_skipping_(weighted_index, &seen_indices) else { continue };
let target = &self.data[idx];
let (indices, weights): (Vec<usize>, Vec<W>) =
self.data.iter().enumerate()
.filter_map(
|(i, item)| (item.value == target.value).then_some((i, item.weight))
)
.unzip();
seen_indices.extend(indices);
l -= weights.into_iter().sum::<W>();
let target = &mut self.data[idx];
if decrement >= target.weight {
target.weight = W::zero();
} else {
target.weight -= decrement;
}
out.push(target.value.clone());
}
self.prune();
out
}
}
impl<V, W: Weight> WeightedList<V,W>
where
V: Clone,
{
pub fn shuffle_items<RNG>(&mut self, rng: &mut RNG) -> &mut Self
where RNG: Rng + ?Sized
{
self.data.shuffle(rng);
self
}
pub fn shuffled_items<RNG>(&self, rng: &mut RNG) -> Self
where RNG: Rng + ?Sized
{
let mut out = self.clone();
out.shuffle_items(rng);
out
}
pub fn shuffle_weights<RNG>(&mut self, rng: &mut RNG) -> &mut Self
where RNG: Rng + ?Sized
{
let mut weights: Vec<W> = self.weights().collect();
weights.shuffle(rng);
for item in &mut self.data {
item.weight = weights.pop().unwrap();
}
self
}
pub fn shuffled_weights<RNG>(&self, rng: &mut RNG) -> Self
where RNG: Rng + ?Sized
{
let mut out = self.clone();
out.shuffle_weights(rng);
out
}
}
impl<V, W: Weight> WeightedList<V,W>
{
fn _unweight_index_nopanic_(&self, weighted_index: W) -> usize
{
let mut t = W::zero();
let mut i = 0;
for item in &self.data {
t += item.weight;
if t > weighted_index {
return i;
}
i += 1;
}
i
}
fn _unweight_index_(&self, weighted_index: W) -> usize
{
let mut t = W::zero();
for (i, item) in self.data.iter().enumerate() {
t += item.weight;
if t > weighted_index {
return i;
}
}
panic!(
"index out of bounds: the len is {:?} but the index is {:?}",
self.len(), weighted_index
);
}
fn _unweight_index_skipping_(&self,
weighted_index: W,
seen: &HashSet<usize>,
) -> Option<usize>
{
let mut t = W::zero();
for (i, item) in self.data.iter().enumerate()
{
if seen.contains(&i) { continue }
t += item.weight;
if t > weighted_index {
return Some(i);
}
}
None
}
}
#[cfg(test)] mod tests
{
use super::*;
fn el() -> WeightedList<String, i32>
{
wlist![]
}
fn wl() -> WeightedList<String, i32>
{
wlist![
(2, "sup".to_string()),
(3, "nova".to_string()),
(5, "shard".to_string()),
]
}
#[test] fn _unweight_index_()
{
let list = wl();
assert_eq!( list._unweight_index_(0), 0 );
assert_eq!( list._unweight_index_(1), 0 );
assert_eq!( list._unweight_index_(2), 1 );
assert_eq!( list._unweight_index_(3), 1 );
assert_eq!( list._unweight_index_(4), 1 );
assert_eq!( list._unweight_index_(5), 2 );
assert_eq!( list._unweight_index_(6), 2 );
assert_eq!( list._unweight_index_(7), 2 );
assert_eq!( list._unweight_index_(8), 2 );
assert_eq!( list._unweight_index_(9), 2 );
}
#[test] #[should_panic] fn _unweight_index_empty_()
{
el()._unweight_index_(0);
}
#[test] #[should_panic] fn _unweight_index_out_of_bounds_()
{
wl()._unweight_index_(10);
}
#[test] fn _unweight_index_nopanic_()
{
let list = wl();
assert_eq!( list._unweight_index_nopanic_(10), 3 );
assert_eq!( list._unweight_index_nopanic_(11), 3 );
assert_eq!( list._unweight_index_nopanic_(12), 3 );
}
#[test] fn _unweight_index_skipping_()
{
let list = wl();
let seen = HashSet::from([0]);
assert_eq!( list._unweight_index_skipping_(0, &seen), Some(1) );
assert_eq!( list._unweight_index_skipping_(1, &seen), Some(1) );
assert_eq!( list._unweight_index_skipping_(2, &seen), Some(1) );
assert_eq!( list._unweight_index_skipping_(3, &seen), Some(2) );
assert_eq!( list._unweight_index_skipping_(4, &seen), Some(2) );
assert_eq!( list._unweight_index_skipping_(5, &seen), Some(2) );
assert_eq!( list._unweight_index_skipping_(6, &seen), Some(2) );
assert_eq!( list._unweight_index_skipping_(7, &seen), Some(2) );
let seen = HashSet::from([1]);
assert_eq!( list._unweight_index_skipping_(0, &seen), Some(0) );
assert_eq!( list._unweight_index_skipping_(1, &seen), Some(0) );
assert_eq!( list._unweight_index_skipping_(2, &seen), Some(2) );
assert_eq!( list._unweight_index_skipping_(3, &seen), Some(2) );
assert_eq!( list._unweight_index_skipping_(4, &seen), Some(2) );
assert_eq!( list._unweight_index_skipping_(5, &seen), Some(2) );
assert_eq!( list._unweight_index_skipping_(6, &seen), Some(2) );
}
}