Skip to content

Commit

Permalink
Remove recursion in normalized
Browse files Browse the repository at this point in the history
Remove the recursive call in `semantic::Policy::normalized`, using
the `TreeLike` trait to iterate nodes instead.
  • Loading branch information
tcharding committed Oct 12, 2023
1 parent 03df11c commit 333682e
Showing 1 changed file with 57 additions and 48 deletions.
105 changes: 57 additions & 48 deletions src/policy/semantic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -430,62 +430,71 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
(m, n)
}

match self {
Policy::Threshold(k, subs) => {
let mut ret_subs = Vec::with_capacity(subs.len());

let subs: Vec<_> = subs
.into_iter()
.map(|sub| Arc::new(sub.as_ref().clone().normalized()))
.collect();

let (m, n) = normalized_threshold_values(k, &subs);
use Policy::*;

// Call the current threshold "parent" to differentiate it from its "child" threshold below.
let parent_is_and = m == n; // (n, n)-thresh is an AND
let parent_is_or = m == 1; // (1, n)-thresh is an OR
let mut normalized: Vec<Arc<Policy<Pk>>> = vec![];
for data in Arc::new(self).post_order_iter() {
let child_n = |n| Arc::clone(&normalized[data.child_indices[n]]);

for sub in subs {
match sub.as_ref() {
Policy::Trivial | Policy::Unsatisfiable => {}
Policy::Threshold(ref k, ref subs) => {
let child_is_and = *k == subs.len();
let child_is_or = *k == 1;

if parent_is_and && parent_is_or {
// m = n = 1, child must be the non-trivial, non-unsatisfiable node.
ret_subs.push(Arc::clone(&sub));
} else if parent_is_and && child_is_and {
// If both parent and child are ANDs we can flatten them.
subs.iter().for_each(|sub| ret_subs.push(Arc::clone(sub)));
} else if parent_is_or && child_is_or {
// If both parent and child are ORs we can flatten them.
subs.iter().for_each(|sub| ret_subs.push(Arc::clone(sub)));
} else {
ret_subs.push(Arc::clone(&sub));
let new_policy = match data.node.as_ref() {
Threshold(k, ref subs) => {
let mut ret_subs = Vec::with_capacity(subs.len());
let (m, n) = normalized_threshold_values(*k, &subs);

// Call the current threshold "parent" to differentiate it from its "child" threshold below.
let parent_is_and = m == n; // (n, n)-thresh is an AND
let parent_is_or = m == 1; // (1, n)-thresh is an OR

for sub in (0..subs.len()).map(child_n) {
match sub.as_ref() {
Trivial | Unsatisfiable => {}
Threshold(ref k, ref subs) => {
let child_is_and = *k == subs.len();
let child_is_or = *k == 1;

if parent_is_and && parent_is_or {
// m = n = 1, child must be the non-trivial, non-unsatisfiable node.
ret_subs.push(Arc::clone(&sub));
} else if parent_is_and && child_is_and {
// If both parent and child are ANDs we can flatten them.
subs.iter().for_each(|sub| ret_subs.push(Arc::clone(sub)));
} else if parent_is_or && child_is_or {
// If both parent and child are ORs we can flatten them.
subs.iter().for_each(|sub| ret_subs.push(Arc::clone(sub)));
} else {
ret_subs.push(Arc::clone(&sub));
}
}
_ => ret_subs.push(Arc::clone(&sub)),
}
_ => ret_subs.push(Arc::clone(&sub)),
}
// Now reason about m of n threshold
if m == 0 {
Some(Trivial)
} else if m > ret_subs.len() {
Some(Unsatisfiable)
} else if ret_subs.len() == 1 {
let policy = ret_subs.pop().unwrap();
Some((*policy).clone()) // I'm lost now, can we try_unwrap still?
} else if parent_is_and {
Some(Threshold(ret_subs.len(), ret_subs))
} else if parent_is_or {
Some(Threshold(1, ret_subs))
} else {
Some(Threshold(m, ret_subs))
}
}
// Now reason about m of n threshold
if m == 0 {
Policy::Trivial
} else if m > ret_subs.len() {
Policy::Unsatisfiable
} else if ret_subs.len() == 1 {
let policy = ret_subs.pop().unwrap();
(*policy).clone() // I'm lost now, can we try_unwrap still?
} else if parent_is_and {
Policy::Threshold(ret_subs.len(), ret_subs)
} else if parent_is_or {
Policy::Threshold(1, ret_subs)
} else {
Policy::Threshold(m, ret_subs)
}
_ => None,
};
match new_policy {
Some(new_policy) => normalized.push(Arc::new(new_policy)),
None => normalized.push(Arc::clone(&data.node)),
}
x => x,
}
// Unwrap is ok because we know we processed at least one node.
let root_node = normalized.pop().unwrap();
// Unwrap is ok because we know `root_node` is the only strong reference.
Arc::try_unwrap(root_node).unwrap()
}

/// Detects a true/trivial policy.
Expand Down

0 comments on commit 333682e

Please sign in to comment.