#[derive(Default, Debug, Clone, PartialEq)]
pub struct FastestComboInfo {
pub start_second: f32,
pub end_second: f32,
pub length: u32,
pub speed: f32,
}
pub fn find_fastest_note_subset(
seconds: &[f32],
min_num_notes: u32,
max_num_notes: u32, ) -> FastestComboInfo {
assert!(crate::util::is_sorted(seconds));
assert!(max_num_notes >= min_num_notes);
let mut fastest = FastestComboInfo {
start_second: 0.0,
end_second: 0.0,
length: 0, speed: 0.0,
};
if seconds.len() <= min_num_notes as usize {
return fastest;
}
let end_n = std::cmp::min(seconds.len(), max_num_notes as usize + 1);
for n in (min_num_notes as usize)..end_n {
for i in 0..(seconds.len() - n) {
let end_i = i + n;
let nps: f32 = (end_i - i) as f32 / (seconds[end_i] - seconds[i]);
if nps >= fastest.speed {
fastest = FastestComboInfo {
length: n as u32,
start_second: seconds[i],
end_second: seconds[end_i],
speed: nps,
};
}
}
}
fastest
}
pub fn find_fastest_note_subset_wife_pts(
seconds: &[f32],
min_num_notes: u32,
max_num_notes: u32, wife_pts: &[f32],
) -> FastestComboInfo {
assert!(wife_pts.len() == seconds.len());
assert!(crate::util::is_sorted(seconds));
assert!(max_num_notes >= min_num_notes);
let mut fastest = FastestComboInfo {
start_second: 0.0,
end_second: 0.0,
length: 0, speed: 0.0,
};
if seconds.len() <= min_num_notes as usize {
return fastest;
}
let mut wife_pts_sum_start = wife_pts[0..min_num_notes as usize].iter().sum();
let end_n = std::cmp::min(seconds.len(), max_num_notes as usize + 1);
for n in (min_num_notes as usize)..end_n {
let mut wife_pts_sum: f32 = wife_pts_sum_start;
for i in 0..(seconds.len() - n) {
let end_i = i + n;
let mut nps: f32 = (end_i - i) as f32 / (seconds[end_i] - seconds[i]);
nps *= wife_pts_sum / n as f32;
if nps >= fastest.speed {
fastest = FastestComboInfo {
length: n as u32,
start_second: seconds[i],
end_second: seconds[end_i],
speed: nps,
};
}
wife_pts_sum -= wife_pts[i];
wife_pts_sum += wife_pts[end_i];
}
wife_pts_sum_start += wife_pts[n];
}
fastest
}
pub fn find_fastest_combo_in_score<I, T>(
seconds: &[f32],
are_cbs: impl IntoIterator<Item = bool>,
min_num_notes: u32,
max_num_notes: u32,
wife_pts: Option<&[f32]>,
rate: f32,
) -> FastestComboInfo {
assert!(crate::util::is_sorted(seconds));
assert!(max_num_notes >= min_num_notes);
if let Some(wife_pts) = wife_pts {
assert_eq!(seconds.len(), wife_pts.len());
}
let mut fastest_combo = FastestComboInfo::default();
let mut combo_start_i: Option<usize> = Some(0);
let mut trigger_combo_end = |combo_end_i| {
if let Some(combo_start_i) = combo_start_i {
let combo = &seconds[combo_start_i..combo_end_i];
let fastest_note_subset;
if let Some(wife_pts) = wife_pts {
let wife_pts_slice = &wife_pts[combo_start_i..combo_end_i];
fastest_note_subset = find_fastest_note_subset_wife_pts(
combo,
min_num_notes,
max_num_notes,
wife_pts_slice,
);
} else {
fastest_note_subset = find_fastest_note_subset(combo, min_num_notes, max_num_notes);
}
if fastest_note_subset.speed > fastest_combo.speed {
fastest_combo = fastest_note_subset;
}
}
combo_start_i = None; };
for (i, is_cb) in are_cbs.into_iter().enumerate() {
if is_cb {
trigger_combo_end(i);
}
}
trigger_combo_end(seconds.len());
fastest_combo.speed *= rate;
fastest_combo
}
#[cfg(test)]
mod tests {
use super::*;
use crate::assert_float_eq;
#[test]
fn test_find_fastest_note_subset() {
fn test_the_functions(
seconds: &[f32],
min_num_notes: u32,
max_num_notes: u32,
expected_length: u32,
expected_speed: f32,
) {
let fastest_subset = find_fastest_note_subset(&seconds, min_num_notes, max_num_notes);
let fastest_wife_pts_subset = find_fastest_note_subset_wife_pts(
&seconds,
min_num_notes,
max_num_notes,
&vec![1.0; seconds.len()],
);
assert_float_eq!(fastest_subset.start_second, fastest_wife_pts_subset.start_second;
epsilon=0.00001);
assert_float_eq!(fastest_subset.end_second, fastest_wife_pts_subset.end_second;
epsilon=0.00001);
assert_eq!(fastest_subset.length, fastest_wife_pts_subset.length);
assert_float_eq!(fastest_subset.speed, fastest_wife_pts_subset.speed;
epsilon=0.00001);
assert_eq!(fastest_subset.length, expected_length);
assert_float_eq!(fastest_subset.speed, expected_speed;
epsilon=0.00001);
}
let seconds: &[f32] = &[0.0, 3.0, 5.0, 6.0, 8.0];
test_the_functions(seconds, 2, 99, 2, 0.6666666); test_the_functions(seconds, 3, 99, 3, 0.6);
let seconds: &[f32] = &[0.0, 0.0, 1.0, 2.0, 3.0, 4.0, 4.0];
test_the_functions(seconds, 5, 6, 6, 1.5); test_the_functions(seconds, 5, 5, 5, 1.25);
}
}