Amicable Numbers

P
I
A
// Operations based around amicable numbers
// Suports u32 but should be interchangable with other types
// Wikipedia reference: https://en.wikipedia.org/wiki/Amicable_numbers

// Returns vec of amicable pairs below N
// N must be positive
pub fn amicable_pairs_under_n(n: u32) -> Option<Vec<(u32, u32)>> {
    let mut factor_sums = vec![0; n as usize];

    // Make a list of the sum of the factors of each number below N
    for i in 1..n {
        for j in (i * 2..n).step_by(i as usize) {
            factor_sums[j as usize] += i;
        }
    }

    // Default value of (0, 0) if no pairs are found
    let mut out = vec![(0, 0)];
    // Check if numbers are amicable then append
    for (i, x) in factor_sums.iter().enumerate() {
        if (*x < n) && (factor_sums[*x as usize] == i as u32) && (*x > i as u32) {
            out.push((i as u32, *x));
        }
    }

    // Check if anything was added to the vec, if so remove the (0, 0) and return
    if out.len() == 1 {
        None
    } else {
        out.remove(0);
        Some(out)
    }
}

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

    #[test]
    pub fn test_amicable_numbers_below_n() {
        // First 10 amicable numbers, sorted (low, high)
        let expected_result = vec![
            (220, 284),
            (1184, 1210),
            (2620, 2924),
            (5020, 5564),
            (6232, 6368),
            (10744, 10856),
            (12285, 14595),
            (17296, 18416),
            (63020, 76084),
            (66928, 66992),
        ];

        // Generate pairs under 100,000
        let mut result = amicable_pairs_under_n(100_000).unwrap();

        // There should be 13 pairs under 100,000
        assert_eq!(result.len(), 13);

        // Check the first 10 against known values
        result = result[..10].to_vec();
        assert_eq!(result, expected_result);

        // N that does not have any amicable pairs below it, the result should be None
        assert_eq!(amicable_pairs_under_n(100), None);
    }
}