tunes 1.1.0

A music composition, synthesis, and audio generation library
Documentation
/// Generate Van der Corput sequence (low-discrepancy/quasi-random sequence)
///
/// The Van der Corput sequence is a "quasi-random" sequence that fills space more
/// evenly than pure random numbers. It's used in ray tracing, Monte Carlo integration,
/// and anywhere you want random-looking but well-distributed values.
///
/// The sequence is generated by reversing the binary representation of integers:
/// - 1 (binary: 1) → 0.1 (binary) = 0.5
/// - 2 (binary: 10) → 0.01 (binary) = 0.25
/// - 3 (binary: 11) → 0.11 (binary) = 0.75
/// - 4 (binary: 100) → 0.001 (binary) = 0.125
///
/// This produces values in [0, 1) that are more evenly distributed than random.
///
/// # Arguments
/// * `n` - Number of terms to generate
/// * `base` - Base for the sequence (typically 2 for binary, but can use other bases)
///
/// # Returns
/// Vector of values in range [0.0, 1.0) with quasi-random distribution
///
/// # Typical Parameters
/// - **base = 2**: Binary (most common, best distribution)
/// - **base = 3**: Ternary (different distribution pattern)
/// - **base = 5**: Pentary (yet another pattern)
/// - **n = 16-64**: Good for melodic/rhythmic use
/// - **n = 100+**: For sampling large parameter spaces
///
/// # Recipe: Well-Distributed Melody
/// ```
/// use tunes::prelude::*;
/// use tunes::sequences;
///
/// let mut comp = Composition::new(Tempo::new(120.0));
///
/// // Generate quasi-random values
/// let quasi = sequences::van_der_corput::generate(32, 2);
///
/// // Map to C minor pentatonic
/// let melody = sequences::map_to_scale_f32(
///     &quasi,
///     &sequences::Scale::minor_pentatonic(),
///     C5,
///     2
/// );
///
/// comp.instrument("quasi_melody", &Instrument::synth_lead())
///     .delay(Delay::new(0.375, 0.3, 0.5))
///     .notes(&melody, 0.25);
/// ```
///
/// # Examples
/// ```
/// use tunes::sequences;
///
/// // Generate quasi-random values
/// let quasi = sequences::van_der_corput::generate(16, 2);
/// // More evenly distributed than random!
///
/// // Use for note placement that avoids clumping
/// let positions = sequences::van_der_corput::generate(32, 2);
/// let note_times: Vec<f32> = positions.iter()
///     .map(|&x| x * 4.0)  // Spread over 4 seconds
///     .collect();
///
/// // Use for parameter sweeps
/// # use tunes::prelude::*;
/// # let mut comp = Composition::new(Tempo::new(120.0));
/// let cutoff_values = sequences::van_der_corput::generate(64, 2);
/// for (i, &cutoff) in cutoff_values.iter().enumerate() {
///     let freq = 200.0 + cutoff * 600.0;  // 200-800 Hz range
///     comp.instrument("sweep", &Instrument::synth_lead())
///         .at(i as f32 * 0.125)
///         .note(&[freq], 0.1);
/// }
/// ```
///
/// # Musical Applications
/// - **Note distribution**: Place notes evenly without grid-like regularity
/// - **Rhythm generation**: Better than random for avoiding clumps
/// - **Parameter sampling**: Sweep through filter/pan/volume space efficiently
/// - **Chord voicings**: Distribute notes across register evenly
/// - **Polyrhythms**: Create non-periodic but well-distributed patterns
/// - **Microtonal scales**: Sample pitch space quasi-randomly
///
/// # Quasi-Random vs Random
/// Pure random can create clumps and gaps. Van der Corput fills space more evenly:
/// - **Random**: Unpredictable, can cluster
/// - **Quasi-random**: Looks random, mathematically even distribution
/// - **Grid**: Predictable, mechanical
///
/// Perfect middle ground for generative music that needs randomness without chaos.
pub fn generate(n: usize, base: u32) -> Vec<f32> {
    (0..n)
        .map(|i| {
            let mut result = 0.0;
            let mut f = 1.0 / base as f32;
            let mut num = (i + 1) as u32;

            while num > 0 {
                result += f * (num % base) as f32;
                num /= base;
                f /= base as f32;
            }

            result
        })
        .collect()
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_van_der_corput_basic() {
        let seq = generate(8, 2);
        assert_eq!(seq.len(), 8);
        
        assert!((seq[0] - 0.5).abs() < 0.001);
        assert!((seq[1] - 0.25).abs() < 0.001);
        assert!((seq[2] - 0.75).abs() < 0.001);
        assert!((seq[3] - 0.125).abs() < 0.001);
    }

    #[test]
    fn test_van_der_corput_range() {
        let seq = generate(100, 2);
        for &val in &seq {
            assert!(val >= 0.0 && val < 1.0);
        }
    }

    #[test]
    fn test_van_der_corput_base_3() {
        // Test with base 3
        let seq = generate(9, 3);

        assert_eq!(seq.len(), 9);

        // Should still be in [0, 1)
        for &val in &seq {
            assert!(val >= 0.0 && val < 1.0);
        }

        // Base 3 should give different distribution than base 2
        let seq2 = generate(9, 2);
        assert_ne!(seq, seq2);
    }

    #[test]
    fn test_van_der_corput_distribution() {
        // Van der Corput should fill space more evenly than random
        let seq = generate(32, 2);

        // Divide [0,1) into 8 bins and count how many values in each
        let mut bins = vec![0; 8];
        for &val in &seq {
            let bin = (val * 8.0).floor() as usize;
            bins[bin.min(7)] += 1;
        }

        // Each bin should have some values (quasi-random distributes evenly)
        for &count in &bins {
            assert!(count > 0, "Bin empty - poor distribution");
        }
    }
}

// ========== PRESETS ==========

/// Short binary sequence (16 terms, base 2)
pub fn short() -> Vec<f32> {
    generate(16, 2)
}

/// Classic binary (32 terms, base 2) - well-distributed
pub fn classic() -> Vec<f32> {
    generate(32, 2)
}

/// Long binary (64 terms, base 2) - extended quasi-random
pub fn long() -> Vec<f32> {
    generate(64, 2)
}

/// Ternary (32 terms, base 3) - different distribution pattern
pub fn ternary() -> Vec<f32> {
    generate(32, 3)
}

/// Pentary (32 terms, base 5) - another distribution variant
pub fn pentary() -> Vec<f32> {
    generate(32, 5)
}