Skip to content

Commit 333682e

Browse files
committed
Remove recursion in normalized
Remove the recursive call in `semantic::Policy::normalized`, using the `TreeLike` trait to iterate nodes instead.
1 parent 03df11c commit 333682e

File tree

1 file changed

+57
-48
lines changed

1 file changed

+57
-48
lines changed

src/policy/semantic.rs

+57-48
Original file line numberDiff line numberDiff line change
@@ -430,62 +430,71 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
430430
(m, n)
431431
}
432432

433-
match self {
434-
Policy::Threshold(k, subs) => {
435-
let mut ret_subs = Vec::with_capacity(subs.len());
436-
437-
let subs: Vec<_> = subs
438-
.into_iter()
439-
.map(|sub| Arc::new(sub.as_ref().clone().normalized()))
440-
.collect();
441-
442-
let (m, n) = normalized_threshold_values(k, &subs);
433+
use Policy::*;
443434

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

448-
for sub in subs {
449-
match sub.as_ref() {
450-
Policy::Trivial | Policy::Unsatisfiable => {}
451-
Policy::Threshold(ref k, ref subs) => {
452-
let child_is_and = *k == subs.len();
453-
let child_is_or = *k == 1;
454-
455-
if parent_is_and && parent_is_or {
456-
// m = n = 1, child must be the non-trivial, non-unsatisfiable node.
457-
ret_subs.push(Arc::clone(&sub));
458-
} else if parent_is_and && child_is_and {
459-
// If both parent and child are ANDs we can flatten them.
460-
subs.iter().for_each(|sub| ret_subs.push(Arc::clone(sub)));
461-
} else if parent_is_or && child_is_or {
462-
// If both parent and child are ORs we can flatten them.
463-
subs.iter().for_each(|sub| ret_subs.push(Arc::clone(sub)));
464-
} else {
465-
ret_subs.push(Arc::clone(&sub));
439+
let new_policy = match data.node.as_ref() {
440+
Threshold(k, ref subs) => {
441+
let mut ret_subs = Vec::with_capacity(subs.len());
442+
let (m, n) = normalized_threshold_values(*k, &subs);
443+
444+
// Call the current threshold "parent" to differentiate it from its "child" threshold below.
445+
let parent_is_and = m == n; // (n, n)-thresh is an AND
446+
let parent_is_or = m == 1; // (1, n)-thresh is an OR
447+
448+
for sub in (0..subs.len()).map(child_n) {
449+
match sub.as_ref() {
450+
Trivial | Unsatisfiable => {}
451+
Threshold(ref k, ref subs) => {
452+
let child_is_and = *k == subs.len();
453+
let child_is_or = *k == 1;
454+
455+
if parent_is_and && parent_is_or {
456+
// m = n = 1, child must be the non-trivial, non-unsatisfiable node.
457+
ret_subs.push(Arc::clone(&sub));
458+
} else if parent_is_and && child_is_and {
459+
// If both parent and child are ANDs we can flatten them.
460+
subs.iter().for_each(|sub| ret_subs.push(Arc::clone(sub)));
461+
} else if parent_is_or && child_is_or {
462+
// If both parent and child are ORs we can flatten them.
463+
subs.iter().for_each(|sub| ret_subs.push(Arc::clone(sub)));
464+
} else {
465+
ret_subs.push(Arc::clone(&sub));
466+
}
466467
}
468+
_ => ret_subs.push(Arc::clone(&sub)),
467469
}
468-
_ => ret_subs.push(Arc::clone(&sub)),
470+
}
471+
// Now reason about m of n threshold
472+
if m == 0 {
473+
Some(Trivial)
474+
} else if m > ret_subs.len() {
475+
Some(Unsatisfiable)
476+
} else if ret_subs.len() == 1 {
477+
let policy = ret_subs.pop().unwrap();
478+
Some((*policy).clone()) // I'm lost now, can we try_unwrap still?
479+
} else if parent_is_and {
480+
Some(Threshold(ret_subs.len(), ret_subs))
481+
} else if parent_is_or {
482+
Some(Threshold(1, ret_subs))
483+
} else {
484+
Some(Threshold(m, ret_subs))
469485
}
470486
}
471-
// Now reason about m of n threshold
472-
if m == 0 {
473-
Policy::Trivial
474-
} else if m > ret_subs.len() {
475-
Policy::Unsatisfiable
476-
} else if ret_subs.len() == 1 {
477-
let policy = ret_subs.pop().unwrap();
478-
(*policy).clone() // I'm lost now, can we try_unwrap still?
479-
} else if parent_is_and {
480-
Policy::Threshold(ret_subs.len(), ret_subs)
481-
} else if parent_is_or {
482-
Policy::Threshold(1, ret_subs)
483-
} else {
484-
Policy::Threshold(m, ret_subs)
485-
}
487+
_ => None,
488+
};
489+
match new_policy {
490+
Some(new_policy) => normalized.push(Arc::new(new_policy)),
491+
None => normalized.push(Arc::clone(&data.node)),
486492
}
487-
x => x,
488493
}
494+
// Unwrap is ok because we know we processed at least one node.
495+
let root_node = normalized.pop().unwrap();
496+
// Unwrap is ok because we know `root_node` is the only strong reference.
497+
Arc::try_unwrap(root_node).unwrap()
489498
}
490499

491500
/// Detects a true/trivial policy.

0 commit comments

Comments
 (0)