use crate::{
taiko::difficulty::utils::has_interval::HasInterval,
util::{
float_ext::FloatExt,
sync::{RefCount, Weak},
},
};
pub struct IntervalGroupingUtils;
impl IntervalGroupingUtils {
pub const MARGIN_OF_ERROR: f64 = 5.0;
}
pub const fn group_by_interval<T: HasInterval>(
objects: &[RefCount<T>],
) -> GroupedByIntervalIter<'_, T> {
GroupedByIntervalIter::new(objects)
}
pub struct GroupedByIntervalIter<'a, T> {
objects: &'a [RefCount<T>],
i: usize,
}
impl<'a, T> GroupedByIntervalIter<'a, T> {
const fn new(objects: &'a [RefCount<T>]) -> Self {
Self { objects, i: 0 }
}
}
impl<T: HasInterval> Iterator for GroupedByIntervalIter<'_, T> {
type Item = Vec<Weak<T>>;
fn next(&mut self) -> Option<Self::Item> {
if self.i < self.objects.len() {
Some(self.create_next_group())
} else {
None
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let min = usize::from(!self.objects.is_empty());
let max = self.objects.len() - self.i;
(min, Some(max))
}
}
impl<T: HasInterval> GroupedByIntervalIter<'_, T> {
fn create_next_group(&mut self) -> Vec<Weak<T>> {
const MARGIN_OF_ERROR: f64 = IntervalGroupingUtils::MARGIN_OF_ERROR;
let &mut Self { objects, ref mut i } = self;
let mut grouped_objects = vec![objects[*i].downgrade()];
*i += 1;
while *i < objects.len() - 1 {
if !(objects[*i]
.get()
.interval()
.almost_eq(objects[*i + 1].get().interval(), MARGIN_OF_ERROR))
{
if objects[*i + 1].get().interval() > objects[*i].get().interval() + MARGIN_OF_ERROR
{
grouped_objects.push(objects[*i].downgrade());
*i += 1;
}
return grouped_objects;
}
grouped_objects.push(objects[*i].downgrade());
*i += 1;
}
if objects.len() > 2
&& *i < objects.len()
&& objects[objects.len() - 1]
.get()
.interval()
.almost_eq(objects[objects.len() - 2].get().interval(), MARGIN_OF_ERROR)
{
grouped_objects.push(objects[*i].downgrade());
*i += 1;
}
grouped_objects
}
}