use std::cmp;
pub fn make_index_combinations(index_upper_bounds: &[usize]) -> Option<Vec<Vec<usize>>> {
if index_upper_bounds.is_empty() || index_upper_bounds.iter().any(|&val| val == 0) {
return None;
}
let indices: Vec<Vec<usize>> = index_upper_bounds
.iter()
.map(|&count| (0..count).collect())
.collect();
let mut combs: Vec<Vec<usize>> = vec![];
let mut stack: Vec<usize> = vec![];
make_combs(&mut combs, &mut stack, &indices, 0);
Some(combs)
}
fn make_combs(
combs: &mut Vec<Vec<usize>>,
stack: &mut Vec<usize>,
indices: &Vec<Vec<usize>>,
idx: usize,
) {
for i in indices[idx].iter() {
stack.push(*i);
if idx == indices.len() - 1 {
combs.push(stack.to_vec());
} else {
make_combs(combs, stack, indices, idx + 1);
}
stack.pop();
}
}
pub fn largest_common_dividing_power_of_two(x: u128, y: u128, z: u128) -> u8 {
if x & 1 != 0 || y & 1 != 0 || z & 1 != 0 {
return 0;
}
if x == 0 || y == 0 {
return 0;
}
let x_trail_zeros = x.trailing_zeros() as u8;
let y_trail_zeros = y.trailing_zeros() as u8;
let min_x_t_y_t = cmp::min(x_trail_zeros, y_trail_zeros);
if z == 0 {
min_x_t_y_t
} else {
let z_trail_zeros = z.trailing_zeros() as u8;
cmp::min(z_trail_zeros, min_x_t_y_t)
}
}
#[cfg(test)]
mod tests {
use super::{largest_common_dividing_power_of_two, make_index_combinations};
fn verify_combination(correct_comb: &Vec<Vec<usize>>, test_comb: &Vec<Vec<usize>>) {
assert_eq!(
correct_comb.len(),
test_comb.len(),
"corr len: {}, test len: {}",
correct_comb.len(),
test_comb.len()
);
let it = correct_comb.iter().zip(test_comb.iter());
for (j, (corr, test)) in it.enumerate() {
assert_eq!(
corr.len(),
test.len(),
"comb: {}, corr: {:?}, test: {:?}",
j,
corr,
test
);
assert_eq!(corr, test);
}
}
#[test]
fn index_combination_invalid_case() {
let idx_upper_bounds: [usize; 4] = [1, 1, 2, 0];
match make_index_combinations(&idx_upper_bounds) {
Some(_) => panic!("got return value `Some(_)`"),
None => assert!(true),
}
}
#[test]
fn index_combination_bound_one_two_times() {
let idx_upper_bounds: [usize; 2] = [1; 2];
let correct_index_combs: Vec<Vec<usize>> = vec![vec![0, 0]];
match make_index_combinations(&idx_upper_bounds) {
None => panic!("got return value `None`"),
Some(combinations) => {
verify_combination(&correct_index_combs, &combinations);
}
}
}
#[test]
fn index_combination_bound_two_two_times() {
let idx_upper_bounds: [usize; 2] = [2; 2];
let correct_index_combs: Vec<Vec<usize>> =
vec![vec![0, 0], vec![0, 1], vec![1, 0], vec![1, 1]];
match make_index_combinations(&idx_upper_bounds) {
None => panic!("got return value `None`"),
Some(combinations) => {
verify_combination(&correct_index_combs, &combinations);
}
}
}
#[test]
fn index_combination_bound_two_three_times() {
let idx_upper_bounds: [usize; 3] = [2; 3];
let correct_index_combs: Vec<Vec<usize>> = vec![
vec![0, 0, 0],
vec![0, 0, 1],
vec![0, 1, 0],
vec![0, 1, 1],
vec![1, 0, 0],
vec![1, 0, 1],
vec![1, 1, 0],
vec![1, 1, 1],
];
match make_index_combinations(&idx_upper_bounds) {
None => panic!("got return value `None`"),
Some(combinations) => {
verify_combination(&correct_index_combs, &combinations);
}
}
}
#[test]
fn index_combination_three_mix() {
let idx_upper_bounds: [usize; 3] = [2, 2, 3];
let correct_index_combs: Vec<Vec<usize>> = vec![
vec![0, 0, 0],
vec![0, 0, 1],
vec![0, 0, 2],
vec![0, 1, 0],
vec![0, 1, 1],
vec![0, 1, 2],
vec![1, 0, 0],
vec![1, 0, 1],
vec![1, 0, 2],
vec![1, 1, 0],
vec![1, 1, 1],
vec![1, 1, 2],
];
match make_index_combinations(&idx_upper_bounds) {
None => panic!("got return value `None`"),
Some(combinations) => {
verify_combination(&correct_index_combs, &combinations);
}
}
}
#[test]
fn index_combination_two_mix() {
let idx_upper_bounds: [usize; 2] = [4, 2];
let correct_index_combs: Vec<Vec<usize>> = vec![
vec![0, 0],
vec![0, 1],
vec![1, 0],
vec![1, 1],
vec![2, 0],
vec![2, 1],
vec![3, 0],
vec![3, 1],
];
match make_index_combinations(&idx_upper_bounds) {
None => panic!("got return value `None`"),
Some(combinations) => {
verify_combination(&correct_index_combs, &combinations);
}
}
}
#[test]
fn largest_common_dividing_power_of_two_test() {
let large_num = i128::MAX as u128 + 1;
let test_cases: [[u128; 4]; 10] = [
[3, 8, 4, 0],
[12, 16, 1, 0],
[4, 0, 4, 0],
[4, 4, 0, 2],
[12, 16, 16, 2],
[2, 16, 2, 1],
[2, 32, 32, 1],
[64, 64, 32, 5],
[large_num, 2, large_num, 1],
[large_num, large_num, large_num, 127],
];
for test in test_cases.iter() {
let res = largest_common_dividing_power_of_two(test[0], test[1], test[2]);
assert_eq!(res as u128, test[3]);
}
}
}