use ddk_dlc::Error;
use secp256k1_zkp::PublicKey;
use crate::{
combination_iterator::CombinationIterator, OracleNumericInfo, RangeInfo, TrieIterInfo,
};
pub(crate) fn get_adaptor_point_for_indexed_paths(
indexes: &[usize],
paths: &[Vec<usize>],
precomputed_points: &[Vec<Vec<PublicKey>>],
) -> Result<PublicKey, super::Error> {
debug_assert!(indexes.len() == paths.len());
debug_assert!(precomputed_points.len() >= indexes.len());
if indexes.is_empty() {
return Err(super::Error::InvalidArgument(
"Indexes is empty".to_string(),
));
}
let mut keys = Vec::new();
for (i, j) in indexes.iter().enumerate() {
let path = &paths[i];
let k: Vec<&PublicKey> = precomputed_points[*j]
.iter()
.zip(path.iter())
.map(|(y, p)| &y[*p])
.collect();
keys.extend(k);
}
Ok(PublicKey::combine_keys(&keys)?)
}
pub(crate) fn pre_pad_vec(mut input: Vec<usize>, expected_size: usize) -> Vec<usize> {
if input.len() == expected_size {
return input;
}
let mut res = Vec::with_capacity(expected_size);
res.resize(expected_size - input.len(), 0);
res.append(&mut input);
res
}
pub(crate) fn get_max_covering_paths(
oracle_numeric_infos: &OracleNumericInfo,
threshold: usize,
) -> Vec<Vec<(usize, Vec<usize>)>> {
let mut paths: Vec<Vec<(usize, Vec<usize>)>> = Vec::new();
let min_nb_digits = oracle_numeric_infos.get_min_nb_digits();
let combination_iter =
CombinationIterator::new(oracle_numeric_infos.nb_digits.len(), threshold);
for combination in combination_iter {
let infos = oracle_numeric_infos
.nb_digits
.iter()
.enumerate()
.filter_map(|(i, x)| {
if combination.contains(&i) {
Some(x)
} else {
None
}
})
.collect::<Vec<_>>();
if infos.iter().all(|x| *x == &min_nb_digits) {
continue;
}
let max = infos.iter().map(|x| *x - min_nb_digits).collect::<Vec<_>>();
let mut counters = infos
.iter()
.map(|x| usize::from(**x != min_nb_digits))
.collect::<Vec<_>>();
let mut i = 0;
loop {
let path = counters
.iter()
.zip(combination.iter())
.map(|(x, i)| {
let p = if *x == 0 {
std::iter::repeat_n(1, min_nb_digits).collect()
} else {
let mut p = Vec::with_capacity(*x);
p.resize(x - 1, 0);
p.push(1);
p
};
(*i, p)
})
.collect::<Vec<_>>();
paths.push(path);
if counters.iter().zip(max.iter()).all(|(x, y)| x == y) {
break;
}
while counters[i] == max[i] {
if *infos[i] != min_nb_digits {
counters[i] = 1;
}
i += 1;
}
counters[i] += 1;
i = 0;
}
}
paths
}
pub(crate) fn get_value_callback(
paths: &[Vec<usize>],
oracle_indexes: &[usize],
cet_index: usize,
adaptor_index: &mut usize,
trie_infos: &mut Vec<TrieIterInfo>,
) -> Result<RangeInfo, Error> {
let range_info = RangeInfo {
cet_index,
adaptor_index: *adaptor_index,
};
let iter_info = TrieIterInfo {
value: range_info.clone(),
indexes: oracle_indexes.to_vec(),
paths: paths.to_vec(),
};
trie_infos.push(iter_info);
*adaptor_index += 1;
Ok(range_info)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_utils::get_variable_oracle_numeric_infos;
struct TestCase {
nb_digits: Vec<usize>,
threshold: usize,
expected_paths: Vec<Vec<(usize, Vec<usize>)>>,
}
fn test_cases() -> Vec<TestCase> {
vec![
TestCase {
nb_digits: vec![10, 12, 11, 10],
threshold: 4,
expected_paths: vec![
vec![
(0, vec![1, 1, 1, 1, 1, 1, 1, 1, 1, 1]),
(1, vec![1]),
(2, vec![1]),
(3, vec![1, 1, 1, 1, 1, 1, 1, 1, 1, 1]),
],
vec![
(0, vec![1, 1, 1, 1, 1, 1, 1, 1, 1, 1]),
(1, vec![0, 1]),
(2, vec![1]),
(3, vec![1, 1, 1, 1, 1, 1, 1, 1, 1, 1]),
],
],
},
TestCase {
nb_digits: vec![10, 12, 11, 10],
threshold: 2,
expected_paths: vec![
vec![(0, vec![1, 1, 1, 1, 1, 1, 1, 1, 1, 1]), (1, vec![1])],
vec![(0, vec![1, 1, 1, 1, 1, 1, 1, 1, 1, 1]), (1, vec![0, 1])],
vec![(0, vec![1, 1, 1, 1, 1, 1, 1, 1, 1, 1]), (2, vec![1])],
vec![(1, vec![1]), (2, vec![1])],
vec![(1, vec![0, 1]), (2, vec![1])],
vec![(1, vec![1]), (3, vec![1, 1, 1, 1, 1, 1, 1, 1, 1, 1])],
vec![(1, vec![0, 1]), (3, vec![1, 1, 1, 1, 1, 1, 1, 1, 1, 1])],
vec![(2, vec![1]), (3, vec![1, 1, 1, 1, 1, 1, 1, 1, 1, 1])],
],
},
]
}
#[test]
fn max_covering_paths_test() {
for test_case in test_cases() {
let oracle_infos = get_variable_oracle_numeric_infos(&test_case.nb_digits, 2);
let max_covering_paths = get_max_covering_paths(&oracle_infos, test_case.threshold);
assert_eq!(test_case.expected_paths, max_covering_paths);
}
}
}