pub fn generate(ratios: &[usize], total_length: usize) -> Vec<Vec<usize>> {
let mut patterns = Vec::with_capacity(ratios.len());
for &ratio in ratios {
if ratio == 0 {
patterns.push(Vec::new());
continue;
}
let mut hits = Vec::new();
let step_size = total_length as f32 / ratio as f32;
for i in 0..ratio {
let hit = (i as f32 * step_size).round() as usize;
if hit < total_length && !hits.contains(&hit) {
hits.push(hit);
}
}
hits.sort_unstable();
patterns.push(hits);
}
patterns
}
pub fn lcm(numbers: &[usize]) -> usize {
if numbers.is_empty() || numbers.contains(&0) {
return 0;
}
numbers.iter().copied().reduce(lcm_two).unwrap()
}
fn lcm_two(a: usize, b: usize) -> usize {
if a == 0 || b == 0 {
return 0;
}
(a * b) / gcd_two(a, b)
}
fn gcd_two(mut a: usize, mut b: usize) -> usize {
while b != 0 {
let temp = b;
b = a % b;
a = temp;
}
a
}
pub fn polyrhythm_cycle(ratios: &[usize]) -> (Vec<Vec<usize>>, usize) {
let cycle_length = lcm(ratios);
let patterns = generate(ratios, cycle_length);
(patterns, cycle_length)
}
pub fn polyrhythm_timings(ratios: &[usize], cycle_duration: f32) -> Vec<Vec<f32>> {
let mut timings = Vec::with_capacity(ratios.len());
for &ratio in ratios {
if ratio == 0 {
timings.push(Vec::new());
continue;
}
let mut times = Vec::new();
let interval = cycle_duration / ratio as f32;
for i in 0..=ratio {
let time = i as f32 * interval;
if time <= cycle_duration {
times.push(time);
}
}
timings.push(times);
}
timings
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_polyrhythm_34() {
let patterns = generate(&[3, 4], 12);
assert_eq!(patterns.len(), 2);
assert_eq!(patterns[0].len(), 3);
assert_eq!(patterns[0], vec![0, 4, 8]);
assert_eq!(patterns[1].len(), 4);
assert_eq!(patterns[1], vec![0, 3, 6, 9]);
}
#[test]
fn test_lcm_calculation() {
assert_eq!(lcm(&[3, 4]), 12);
assert_eq!(lcm(&[2, 3, 4]), 12);
assert_eq!(lcm(&[5, 7]), 35);
assert_eq!(lcm(&[6, 8]), 24);
assert_eq!(lcm(&[5, 6, 7]), 210);
}
#[test]
fn test_gcd() {
assert_eq!(gcd_two(12, 8), 4);
assert_eq!(gcd_two(7, 5), 1);
assert_eq!(gcd_two(100, 50), 50);
}
#[test]
fn test_polyrhythm_cycle() {
let (patterns, length) = polyrhythm_cycle(&[3, 4]);
assert_eq!(length, 12);
assert_eq!(patterns[0].len(), 3);
assert_eq!(patterns[1].len(), 4);
}
#[test]
fn test_polyrhythm_timings() {
let timings = polyrhythm_timings(&[2, 3], 6.0);
assert_eq!(timings[0].len(), 3); assert!((timings[0][0] - 0.0).abs() < 0.001);
assert!((timings[0][1] - 3.0).abs() < 0.001);
assert!((timings[0][2] - 6.0).abs() < 0.001);
assert_eq!(timings[1].len(), 4); assert!((timings[1][0] - 0.0).abs() < 0.001);
assert!((timings[1][1] - 2.0).abs() < 0.001);
assert!((timings[1][2] - 4.0).abs() < 0.001);
assert!((timings[1][3] - 6.0).abs() < 0.001);
}
#[test]
fn test_triple_polyrhythm() {
let patterns = generate(&[3, 5, 7], 105); assert_eq!(patterns.len(), 3);
assert_eq!(patterns[0].len(), 3); assert_eq!(patterns[1].len(), 5); assert_eq!(patterns[2].len(), 7); }
#[test]
fn test_empty_and_zero() {
let patterns = generate(&[], 16);
assert_eq!(patterns.len(), 0);
let patterns = generate(&[3, 0, 4], 12);
assert_eq!(patterns.len(), 3);
assert!(patterns[1].is_empty()); }
}
pub fn three_two() -> Vec<Vec<usize>> {
generate(&[3, 2], 6)
}
pub fn four_three() -> Vec<Vec<usize>> {
generate(&[4, 3], 12)
}
pub fn five_four() -> Vec<Vec<usize>> {
generate(&[5, 4], 20)
}
pub fn five_three() -> Vec<Vec<usize>> {
generate(&[5, 3], 15)
}
pub fn seven_five() -> Vec<Vec<usize>> {
generate(&[7, 5], 35)
}
pub fn triple() -> Vec<Vec<usize>> {
generate(&[3, 4, 5], 60)
}