diff --git a/examples/htlc.rs b/examples/htlc.rs
index 8f864d639..9e476f25c 100644
--- a/examples/htlc.rs
+++ b/examples/htlc.rs
@@ -7,7 +7,8 @@ use std::str::FromStr;
 
 use miniscript::bitcoin::Network;
 use miniscript::descriptor::Wsh;
-use miniscript::policy::{Concrete, Liftable};
+use miniscript::policy::concrete::Policy as Concrete;
+use miniscript::r#abstract::Liftable;
 
 fn main() {
     // HTLC policy with 10:1 odds for happy (co-operative) case compared to uncooperative case.
diff --git a/examples/taproot.rs b/examples/taproot.rs
index 3482d3ddb..04ae0a54d 100644
--- a/examples/taproot.rs
+++ b/examples/taproot.rs
@@ -8,7 +8,7 @@ use miniscript::bitcoin::key::{KeyPair, XOnlyPublicKey};
 use miniscript::bitcoin::secp256k1::rand;
 use miniscript::bitcoin::Network;
 use miniscript::descriptor::DescriptorType;
-use miniscript::policy::Concrete;
+use miniscript::policy::concrete::Policy as Concrete;
 use miniscript::{translate_hash_fail, Descriptor, Miniscript, Tap, TranslatePk, Translator};
 
 // Refer to https://github.com/sanket1729/adv_btc_workshop/blob/master/workshop.md#creating-a-taproot-descriptor
diff --git a/fuzz/fuzz_targets/compile_descriptor.rs b/fuzz/fuzz_targets/compile_descriptor.rs
index cfd6c7f4f..54950ed88 100644
--- a/fuzz/fuzz_targets/compile_descriptor.rs
+++ b/fuzz/fuzz_targets/compile_descriptor.rs
@@ -1,11 +1,11 @@
 use std::str::FromStr;
 
 use honggfuzz::fuzz;
+use miniscript::r#abstract::Liftable;
 use miniscript::{policy, Miniscript, Segwitv0};
-use policy::Liftable;
 
 type Script = Miniscript<String, Segwitv0>;
-type Policy = policy::Concrete<String>;
+type Policy = policy::concrete::Policy<String>;
 
 fn do_test(data: &[u8]) {
     let data_str = String::from_utf8_lossy(data);
diff --git a/fuzz/fuzz_targets/roundtrip_concrete.rs b/fuzz/fuzz_targets/roundtrip_concrete.rs
index 41e437221..2d2c4564d 100644
--- a/fuzz/fuzz_targets/roundtrip_concrete.rs
+++ b/fuzz/fuzz_targets/roundtrip_concrete.rs
@@ -4,7 +4,7 @@ use honggfuzz::fuzz;
 use miniscript::policy;
 use regex::Regex;
 
-type Policy = policy::Concrete<String>;
+type Policy = policy::concrete::Policy<String>;
 
 fn do_test(data: &[u8]) {
     let data_str = String::from_utf8_lossy(data);
diff --git a/fuzz/fuzz_targets/roundtrip_semantic.rs b/fuzz/fuzz_targets/roundtrip_semantic.rs
index cebc223b2..0e309d163 100644
--- a/fuzz/fuzz_targets/roundtrip_semantic.rs
+++ b/fuzz/fuzz_targets/roundtrip_semantic.rs
@@ -1,9 +1,8 @@
 use std::str::FromStr;
 
 use honggfuzz::fuzz;
-use miniscript::policy;
 
-type Policy = policy::Semantic<String>;
+type Policy = miniscript::r#abstract::Abstract<String>;
 
 fn do_test(data: &[u8]) {
     let data_str = String::from_utf8_lossy(data);
diff --git a/src/abstract/lift.rs b/src/abstract/lift.rs
new file mode 100644
index 000000000..6c68a5bec
--- /dev/null
+++ b/src/abstract/lift.rs
@@ -0,0 +1,528 @@
+// SPDX-License-Identifier: CC0-1.0
+
+//! Tools for representing Bitcoin scriptpubkeys as abstract spending policies.
+//!
+//! These may be compiled to Miniscript, which contains extra information to
+//! describe the exact representation as Bitcoin script.
+//!
+//! The format represents EC public keys abstractly to allow wallets to replace
+//! these with BIP32 paths, pay-to-contract instructions, etc.
+//!
+use core::fmt;
+#[cfg(feature = "std")]
+use std::error;
+
+use crate::descriptor::Descriptor;
+use crate::miniscript::{Miniscript, ScriptContext};
+use crate::policy::concrete::Policy as Concrete;
+use crate::r#abstract::Abstract;
+use crate::sync::Arc;
+use crate::{Error, MiniscriptKey, Terminal};
+
+/// Trait describing script representations which can be lifted into
+/// an abstract policy, by discarding information.
+///
+/// After Lifting all policies are converted into `KeyHash(Pk::HasH)` to
+/// maintain the following invariant(modulo resource limits):
+/// `Lift(Concrete) == Concrete -> Miniscript -> Script -> Miniscript -> Abstract`
+///
+/// Lifting from [`Miniscript`] or [`Descriptor`] can fail if the miniscript
+/// contains a timelock combination or if it contains a branch that exceeds
+/// resource limits.
+///
+/// Lifting from concrete policies can fail if the policy contains a timelock
+/// combination. It is possible that a concrete policy has some branches that
+/// exceed resource limits for any compilation but cannot detect such policies
+/// while lifting. Note that our compiler would not succeed for any such
+/// policies.
+pub trait Liftable<Pk: MiniscriptKey> {
+    /// Converts this object into an abstract policy.
+    fn lift(&self) -> Result<Abstract<Pk>, Error>;
+}
+
+/// Error occurring during lifting.
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub enum LiftError {
+    /// Cannot lift policies that have a combination of height and timelocks.
+    HeightTimelockCombination,
+    /// Duplicate public keys.
+    BranchExceedResourceLimits,
+    /// Cannot lift raw descriptors.
+    RawDescriptorLift,
+}
+
+impl fmt::Display for LiftError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match *self {
+            LiftError::HeightTimelockCombination => {
+                f.write_str("Cannot lift policies that have a heightlock and timelock combination")
+            }
+            LiftError::BranchExceedResourceLimits => f.write_str(
+                "Cannot lift policies containing one branch that exceeds resource limits",
+            ),
+            LiftError::RawDescriptorLift => f.write_str("Cannot lift raw descriptors"),
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+impl error::Error for LiftError {
+    fn cause(&self) -> Option<&dyn error::Error> {
+        use self::LiftError::*;
+
+        match self {
+            HeightTimelockCombination | BranchExceedResourceLimits | RawDescriptorLift => None,
+        }
+    }
+}
+
+impl<Pk: MiniscriptKey, Ctx: ScriptContext> Miniscript<Pk, Ctx> {
+    /// Lifting corresponds to conversion of a miniscript into a [`Abstract`]
+    /// policy for human readable or machine analysis. However, naively lifting
+    /// miniscripts can result in incorrect interpretations that don't
+    /// correspond to the underlying semantics when we try to spend them on
+    /// bitcoin network. This can occur if the miniscript contains:
+    /// 1. A combination of timelocks
+    /// 2. A spend that exceeds resource limits
+    pub fn lift_check(&self) -> Result<(), LiftError> {
+        if !self.within_resource_limits() {
+            Err(LiftError::BranchExceedResourceLimits)
+        } else if self.has_mixed_timelocks() {
+            Err(LiftError::HeightTimelockCombination)
+        } else {
+            Ok(())
+        }
+    }
+}
+
+impl<Pk: MiniscriptKey, Ctx: ScriptContext> Liftable<Pk> for Miniscript<Pk, Ctx> {
+    fn lift(&self) -> Result<Abstract<Pk>, Error> {
+        // check whether the root miniscript can have a spending path that is
+        // a combination of heightlock and timelock
+        self.lift_check()?;
+        self.as_inner().lift()
+    }
+}
+
+impl<Pk: MiniscriptKey, Ctx: ScriptContext> Liftable<Pk> for Terminal<Pk, Ctx> {
+    fn lift(&self) -> Result<Abstract<Pk>, Error> {
+        let ret = match *self {
+            Terminal::PkK(ref pk) | Terminal::PkH(ref pk) => Abstract::Key(pk.clone()),
+            Terminal::RawPkH(ref _pkh) => {
+                return Err(Error::LiftError(LiftError::RawDescriptorLift))
+            }
+            Terminal::After(t) => Abstract::After(t),
+            Terminal::Older(t) => Abstract::Older(t),
+            Terminal::Sha256(ref h) => Abstract::Sha256(h.clone()),
+            Terminal::Hash256(ref h) => Abstract::Hash256(h.clone()),
+            Terminal::Ripemd160(ref h) => Abstract::Ripemd160(h.clone()),
+            Terminal::Hash160(ref h) => Abstract::Hash160(h.clone()),
+            Terminal::False => Abstract::Unsatisfiable,
+            Terminal::True => Abstract::Trivial,
+            Terminal::Alt(ref sub)
+            | Terminal::Swap(ref sub)
+            | Terminal::Check(ref sub)
+            | Terminal::DupIf(ref sub)
+            | Terminal::Verify(ref sub)
+            | Terminal::NonZero(ref sub)
+            | Terminal::ZeroNotEqual(ref sub) => sub.node.lift()?,
+            Terminal::AndV(ref left, ref right) | Terminal::AndB(ref left, ref right) => {
+                Abstract::Threshold(2, vec![left.node.lift()?, right.node.lift()?])
+            }
+            Terminal::AndOr(ref a, ref b, ref c) => Abstract::Threshold(
+                1,
+                vec![
+                    Abstract::Threshold(2, vec![a.node.lift()?, b.node.lift()?]),
+                    c.node.lift()?,
+                ],
+            ),
+            Terminal::OrB(ref left, ref right)
+            | Terminal::OrD(ref left, ref right)
+            | Terminal::OrC(ref left, ref right)
+            | Terminal::OrI(ref left, ref right) => {
+                Abstract::Threshold(1, vec![left.node.lift()?, right.node.lift()?])
+            }
+            Terminal::Thresh(k, ref subs) => {
+                let semantic_subs: Result<_, Error> = subs.iter().map(|s| s.node.lift()).collect();
+                Abstract::Threshold(k, semantic_subs?)
+            }
+            Terminal::Multi(k, ref keys) | Terminal::MultiA(k, ref keys) => {
+                Abstract::Threshold(k, keys.iter().map(|k| Abstract::Key(k.clone())).collect())
+            }
+        }
+        .normalized();
+        Ok(ret)
+    }
+}
+
+impl<Pk: MiniscriptKey> Liftable<Pk> for Descriptor<Pk> {
+    fn lift(&self) -> Result<Abstract<Pk>, Error> {
+        match *self {
+            Descriptor::Bare(ref bare) => bare.lift(),
+            Descriptor::Pkh(ref pkh) => pkh.lift(),
+            Descriptor::Wpkh(ref wpkh) => wpkh.lift(),
+            Descriptor::Wsh(ref wsh) => wsh.lift(),
+            Descriptor::Sh(ref sh) => sh.lift(),
+            Descriptor::Tr(ref tr) => tr.lift(),
+        }
+    }
+}
+
+impl<Pk: MiniscriptKey> Liftable<Pk> for Abstract<Pk> {
+    fn lift(&self) -> Result<Abstract<Pk>, Error> { Ok(self.clone()) }
+}
+
+impl<Pk: MiniscriptKey> Liftable<Pk> for Concrete<Pk> {
+    fn lift(&self) -> Result<Abstract<Pk>, Error> {
+        // do not lift if there is a possible satisfaction
+        // involving combination of timelocks and heightlocks
+        self.check_timelocks()?;
+        let ret = match *self {
+            Concrete::Unsatisfiable => Abstract::Unsatisfiable,
+            Concrete::Trivial => Abstract::Trivial,
+            Concrete::Key(ref pk) => Abstract::Key(pk.clone()),
+            Concrete::After(t) => Abstract::After(t),
+            Concrete::Older(t) => Abstract::Older(t),
+            Concrete::Sha256(ref h) => Abstract::Sha256(h.clone()),
+            Concrete::Hash256(ref h) => Abstract::Hash256(h.clone()),
+            Concrete::Ripemd160(ref h) => Abstract::Ripemd160(h.clone()),
+            Concrete::Hash160(ref h) => Abstract::Hash160(h.clone()),
+            Concrete::And(ref subs) => {
+                let semantic_subs: Result<_, Error> = subs.iter().map(Liftable::lift).collect();
+                Abstract::Threshold(2, semantic_subs?)
+            }
+            Concrete::Or(ref subs) => {
+                let semantic_subs: Result<_, Error> =
+                    subs.iter().map(|(_p, sub)| sub.lift()).collect();
+                Abstract::Threshold(1, semantic_subs?)
+            }
+            Concrete::Threshold(k, ref subs) => {
+                let semantic_subs: Result<_, Error> = subs.iter().map(Liftable::lift).collect();
+                Abstract::Threshold(k, semantic_subs?)
+            }
+        }
+        .normalized();
+        Ok(ret)
+    }
+}
+impl<Pk: MiniscriptKey> Liftable<Pk> for Arc<Concrete<Pk>> {
+    fn lift(&self) -> Result<Abstract<Pk>, Error> { self.as_ref().lift() }
+}
+
+#[cfg(test)]
+mod tests {
+    use core::str::FromStr;
+
+    use bitcoin::Sequence;
+    #[cfg(feature = "compiler")]
+    use sync::Arc;
+
+    use super::*;
+    #[cfg(feature = "compiler")]
+    use crate::descriptor::Tr;
+    use crate::miniscript::context::Segwitv0;
+    use crate::miniscript::Miniscript;
+    use crate::prelude::*;
+    #[cfg(feature = "compiler")]
+    use crate::{descriptor::TapTree, Descriptor, Tap};
+
+    type ConcretePol = crate::policy::concrete::Policy<String>;
+    type AbstractPol = crate::r#abstract::Abstract<String>;
+
+    fn concrete_policy_rtt(s: &str) {
+        let conc = ConcretePol::from_str(s).unwrap();
+        let output = conc.to_string();
+        assert_eq!(s.to_lowercase(), output.to_lowercase());
+    }
+
+    fn semantic_policy_rtt(s: &str) {
+        let sem = AbstractPol::from_str(s).unwrap();
+        let output = sem.normalized().to_string();
+        assert_eq!(s.to_lowercase(), output.to_lowercase());
+    }
+
+    #[test]
+    fn test_timelock_validity() {
+        // only height
+        assert!(ConcretePol::from_str("after(100)").is_ok());
+        // only time
+        assert!(ConcretePol::from_str("after(1000000000)").is_ok());
+        // disjunction
+        assert!(ConcretePol::from_str("or(after(1000000000),after(100))").is_ok());
+        // conjunction
+        assert!(ConcretePol::from_str("and(after(1000000000),after(100))").is_err());
+        // thresh with k = 1
+        assert!(ConcretePol::from_str("thresh(1,pk(),after(1000000000),after(100))").is_ok());
+        // thresh with k = 2
+        assert!(ConcretePol::from_str("thresh(2,after(1000000000),after(100),pk())").is_err());
+    }
+    #[test]
+    fn policy_rtt_tests() {
+        concrete_policy_rtt("pk()");
+        concrete_policy_rtt("or(1@pk(),1@pk())");
+        concrete_policy_rtt("or(99@pk(),1@pk())");
+        concrete_policy_rtt("and(pk(),or(99@pk(),1@older(12960)))");
+
+        semantic_policy_rtt("pk()");
+        semantic_policy_rtt("or(pk(),pk())");
+        semantic_policy_rtt("and(pk(),pk())");
+
+        //fuzzer crashes
+        assert!(ConcretePol::from_str("thresh()").is_err());
+        assert!(AbstractPol::from_str("thresh(0)").is_err());
+        assert!(AbstractPol::from_str("thresh()").is_err());
+        concrete_policy_rtt("ripemd160()");
+    }
+
+    #[test]
+    fn compile_invalid() {
+        // Since the root Error does not support Eq type, we have to
+        // compare the string representations of the error
+        assert_eq!(
+            ConcretePol::from_str("thresh(2,pk(),thresh(0))")
+                .unwrap_err()
+                .to_string(),
+            "Threshold k must be greater than 0 and less than or equal to n 0<k<=n"
+        );
+        assert_eq!(
+            ConcretePol::from_str("thresh(2,pk(),thresh(0,pk()))")
+                .unwrap_err()
+                .to_string(),
+            "Threshold k must be greater than 0 and less than or equal to n 0<k<=n"
+        );
+        assert_eq!(
+            ConcretePol::from_str("and(pk())").unwrap_err().to_string(),
+            "And policy fragment must take 2 arguments"
+        );
+        assert_eq!(
+            ConcretePol::from_str("or(pk())").unwrap_err().to_string(),
+            "Or policy fragment must take 2 arguments"
+        );
+        assert_eq!(
+            ConcretePol::from_str("thresh(3,after(0),pk(),pk())")
+                .unwrap_err()
+                .to_string(),
+            "Time must be greater than 0; n > 0"
+        );
+
+        assert_eq!(
+            ConcretePol::from_str("thresh(2,older(2147483650),pk(),pk())")
+                .unwrap_err()
+                .to_string(),
+            "Relative/Absolute time must be less than 2^31; n < 2^31"
+        );
+    }
+
+    //https://github.com/apoelstra/rust-miniscript/issues/41
+    #[test]
+    fn heavy_nest() {
+        let policy_string = "thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk()))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))";
+        ConcretePol::from_str(policy_string).unwrap_err();
+    }
+
+    #[test]
+    fn lift_andor() {
+        let key_a: bitcoin::PublicKey =
+            "02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e"
+                .parse()
+                .unwrap();
+        let key_b: bitcoin::PublicKey =
+            "03b506a1dbe57b4bf48c95e0c7d417b87dd3b4349d290d2e7e9ba72c912652d80a"
+                .parse()
+                .unwrap();
+
+        let ms_str: Miniscript<bitcoin::PublicKey, Segwitv0> =
+            format!("andor(multi(1,{}),older(42),c:pk_k({}))", key_a.inner, key_b.inner)
+                .parse()
+                .unwrap();
+        assert_eq!(
+            Abstract::Threshold(
+                1,
+                vec![
+                    Abstract::Threshold(
+                        2,
+                        vec![
+                            Abstract::Key(key_a),
+                            Abstract::Older(Sequence::from_height(42))
+                        ]
+                    ),
+                    Abstract::Key(key_b)
+                ]
+            ),
+            ms_str.lift().unwrap()
+        );
+    }
+
+    #[test]
+    #[cfg(feature = "compiler")]
+    fn taproot_compile() {
+        // Trivial single-node compilation
+        let unspendable_key: String = "UNSPENDABLE".to_string();
+        {
+            let policy: Concrete<String> = policy_str!("thresh(2,pk(A),pk(B),pk(C),pk(D))");
+            let descriptor = policy.compile_tr(Some(unspendable_key.clone())).unwrap();
+
+            let ms_compilation: Miniscript<String, Tap> = ms_str!("multi_a(2,A,B,C,D)");
+            let tree: TapTree<String> = TapTree::Leaf(Arc::new(ms_compilation));
+            let expected_descriptor =
+                Descriptor::new_tr(unspendable_key.clone(), Some(tree)).unwrap();
+            assert_eq!(descriptor, expected_descriptor);
+        }
+
+        // Trivial multi-node compilation
+        {
+            let policy: Concrete<String> = policy_str!("or(and(pk(A),pk(B)),and(pk(C),pk(D)))");
+            let descriptor = policy.compile_tr(Some(unspendable_key.clone())).unwrap();
+
+            let left_ms_compilation: Arc<Miniscript<String, Tap>> =
+                Arc::new(ms_str!("and_v(v:pk(C),pk(D))"));
+            let right_ms_compilation: Arc<Miniscript<String, Tap>> =
+                Arc::new(ms_str!("and_v(v:pk(A),pk(B))"));
+
+            let left = TapTree::Leaf(left_ms_compilation);
+            let right = TapTree::Leaf(right_ms_compilation);
+            let tree = TapTree::combine(left, right);
+
+            let expected_descriptor =
+                Descriptor::new_tr(unspendable_key.clone(), Some(tree)).unwrap();
+            assert_eq!(descriptor, expected_descriptor);
+        }
+
+        {
+            // Invalid policy compilation (Duplicate PubKeys)
+            let policy: Concrete<String> = policy_str!("or(and(pk(A),pk(B)),and(pk(A),pk(D)))");
+            let descriptor = policy.compile_tr(Some(unspendable_key.clone()));
+
+            assert_eq!(descriptor.unwrap_err().to_string(), "Policy contains duplicate keys");
+        }
+
+        // Non-trivial multi-node compilation
+        {
+            let node_policies = [
+                "and(pk(A),pk(B))",
+                "and(pk(C),older(12960))",
+                "pk(D)",
+                "pk(E)",
+                "thresh(3,pk(F),pk(G),pk(H))",
+                "and(and(or(2@pk(I),1@pk(J)),or(1@pk(K),20@pk(L))),pk(M))",
+                "pk(N)",
+            ];
+
+            // Floating-point precision errors cause the minor errors
+            let node_probabilities: [f64; 7] =
+                [0.12000002, 0.28, 0.08, 0.12, 0.19, 0.18999998, 0.02];
+
+            let policy: Concrete<String> = policy_str!(
+                "{}",
+                &format!(
+                    "or(4@or(3@{},7@{}),6@thresh(1,or(4@{},6@{}),{},or(9@{},1@{})))",
+                    node_policies[0],
+                    node_policies[1],
+                    node_policies[2],
+                    node_policies[3],
+                    node_policies[4],
+                    node_policies[5],
+                    node_policies[6]
+                )
+            );
+            let descriptor = policy.compile_tr(Some(unspendable_key.clone())).unwrap();
+
+            let mut sorted_policy_prob = node_policies
+                .iter()
+                .zip(node_probabilities.iter())
+                .collect::<Vec<_>>();
+            sorted_policy_prob.sort_by(|a, b| (a.1).partial_cmp(&b.1).unwrap());
+            let sorted_policies = sorted_policy_prob
+                .into_iter()
+                .map(|(x, _prob)| x)
+                .collect::<Vec<_>>();
+
+            // Generate TapTree leaves compilations from the given sub-policies
+            let node_compilations = sorted_policies
+                .into_iter()
+                .map(|x| {
+                    let leaf_policy: Concrete<String> = policy_str!("{}", x);
+                    TapTree::Leaf(Arc::from(leaf_policy.compile::<Tap>().unwrap()))
+                })
+                .collect::<Vec<_>>();
+
+            // Arrange leaf compilations (acc. to probabilities) using huffman encoding into a TapTree
+            let tree = TapTree::combine(
+                TapTree::combine(node_compilations[4].clone(), node_compilations[5].clone()),
+                TapTree::combine(
+                    TapTree::combine(
+                        TapTree::combine(
+                            node_compilations[0].clone(),
+                            node_compilations[1].clone(),
+                        ),
+                        node_compilations[3].clone(),
+                    ),
+                    node_compilations[6].clone(),
+                ),
+            );
+
+            let expected_descriptor = Descriptor::new_tr("E".to_string(), Some(tree)).unwrap();
+            assert_eq!(descriptor, expected_descriptor);
+        }
+    }
+
+    #[test]
+    #[cfg(feature = "compiler")]
+    fn experimental_taproot_compile() {
+        let unspendable_key = "UNSPEND".to_string();
+
+        {
+            let pol = Concrete::<String>::from_str(
+                "thresh(7,pk(A),pk(B),pk(C),pk(D),pk(E),pk(F),pk(G),pk(H))",
+            )
+            .unwrap();
+            let desc = pol
+                .compile_tr_private_experimental(Some(unspendable_key.clone()))
+                .unwrap();
+            let expected_desc = Descriptor::Tr(
+                Tr::<String>::from_str(
+                    "tr(UNSPEND ,{
+                {
+                    {multi_a(7,B,C,D,E,F,G,H),multi_a(7,A,C,D,E,F,G,H)},
+                    {multi_a(7,A,B,D,E,F,G,H),multi_a(7,A,B,C,E,F,G,H)}
+                },
+                {
+                    {multi_a(7,A,B,C,D,F,G,H),multi_a(7,A,B,C,D,E,G,H)}
+                   ,{multi_a(7,A,B,C,D,E,F,H),multi_a(7,A,B,C,D,E,F,G)}
+                }})"
+                    .replace(&['\t', ' ', '\n'][..], "")
+                    .as_str(),
+                )
+                .unwrap(),
+            );
+            assert_eq!(desc, expected_desc);
+        }
+
+        {
+            let pol =
+                Concrete::<String>::from_str("thresh(3,pk(A),pk(B),pk(C),pk(D),pk(E))").unwrap();
+            let desc = pol
+                .compile_tr_private_experimental(Some(unspendable_key.clone()))
+                .unwrap();
+            let expected_desc = Descriptor::Tr(
+                Tr::<String>::from_str(
+                    "tr(UNSPEND,
+                    {{
+                        {multi_a(3,A,D,E),multi_a(3,A,C,E)},
+                        {multi_a(3,A,C,D),multi_a(3,A,B,E)}\
+                    },
+                    {
+                        {multi_a(3,A,B,D),multi_a(3,A,B,C)},
+                        {
+                            {multi_a(3,C,D,E),multi_a(3,B,D,E)},
+                            {multi_a(3,B,C,E),multi_a(3,B,C,D)}
+                    }}})"
+                        .replace(&['\t', ' ', '\n'][..], "")
+                        .as_str(),
+                )
+                .unwrap(),
+            );
+            assert_eq!(desc, expected_desc);
+        }
+    }
+}
diff --git a/src/policy/semantic.rs b/src/abstract/mod.rs
similarity index 63%
rename from src/policy/semantic.rs
rename to src/abstract/mod.rs
index 9636c0d2a..c64e2e3f9 100644
--- a/src/policy/semantic.rs
+++ b/src/abstract/mod.rs
@@ -1,28 +1,30 @@
 // SPDX-License-Identifier: CC0-1.0
 
 //! Abstract Policies
-//!
-//! We use the terms "semantic" and "abstract" interchangeably because
-//! "abstract" is a reserved keyword in Rust.
+
+mod lift;
+
+#[rustfmt::skip]                // Keep public re-exports separate.
+pub use self::lift::{LiftError, Liftable};
 
 use core::str::FromStr;
 use core::{fmt, str};
 
 use bitcoin::{absolute, Sequence};
 
-use super::concrete::PolicyError;
-use super::ENTAILMENT_MAX_TERMINALS;
+use crate::policy::concrete::PolicyError;
+use crate::policy::ENTAILMENT_MAX_TERMINALS;
 use crate::prelude::*;
 use crate::{errstr, expression, AbsLockTime, Error, ForEachKey, MiniscriptKey, Translator};
 
 /// Abstract policy which corresponds to the semantics of a miniscript and
 /// which allows complex forms of analysis, e.g. filtering and normalization.
 ///
-/// Semantic policies store only hashes of keys to ensure that objects
+/// Abstract policies store only hashes of keys to ensure that objects
 /// representing the same policy are lifted to the same abstract `Policy`,
 /// regardless of their choice of `pk` or `pk_h` nodes.
 #[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
-pub enum Policy<Pk: MiniscriptKey> {
+pub enum Abstract<Pk: MiniscriptKey> {
     /// Unsatisfiable.
     Unsatisfiable,
     /// Trivially satisfiable.
@@ -42,44 +44,44 @@ pub enum Policy<Pk: MiniscriptKey> {
     /// A HASH160 whose preimage must be provided to satisfy the descriptor.
     Hash160(Pk::Hash160),
     /// A set of descriptors, satisfactions must be provided for `k` of them.
-    Threshold(usize, Vec<Policy<Pk>>),
+    Threshold(usize, Vec<Abstract<Pk>>),
 }
 
-impl<Pk> Policy<Pk>
+impl<Pk> Abstract<Pk>
 where
     Pk: MiniscriptKey,
 {
-    /// Constructs a `Policy::After` from `n`.
+    /// Constructs a `Abstract::After` from `n`.
     ///
-    /// Helper function equivalent to `Policy::After(absolute::LockTime::from_consensus(n))`.
-    pub fn after(n: u32) -> Policy<Pk> {
-        Policy::After(AbsLockTime::from(absolute::LockTime::from_consensus(n)))
+    /// Helper function equivalent to `Abstract::After(absolute::LockTime::from_consensus(n))`.
+    pub fn after(n: u32) -> Abstract<Pk> {
+        Abstract::After(AbsLockTime::from(absolute::LockTime::from_consensus(n)))
     }
 
-    /// Construct a `Policy::Older` from `n`.
+    /// Construct a `Abstract::Older` from `n`.
     ///
-    /// Helper function equivalent to `Policy::Older(Sequence::from_consensus(n))`.
-    pub fn older(n: u32) -> Policy<Pk> { Policy::Older(Sequence::from_consensus(n)) }
+    /// Helper function equivalent to `Abstract::Older(Sequence::from_consensus(n))`.
+    pub fn older(n: u32) -> Abstract<Pk> { Abstract::Older(Sequence::from_consensus(n)) }
 }
 
-impl<Pk: MiniscriptKey> ForEachKey<Pk> for Policy<Pk> {
+impl<Pk: MiniscriptKey> ForEachKey<Pk> for Abstract<Pk> {
     fn for_each_key<'a, F: FnMut(&'a Pk) -> bool>(&'a self, mut pred: F) -> bool {
         self.real_for_each_key(&mut pred)
     }
 }
 
-impl<Pk: MiniscriptKey> Policy<Pk> {
+impl<Pk: MiniscriptKey> Abstract<Pk> {
     fn real_for_each_key<'a, F: FnMut(&'a Pk) -> bool>(&'a self, pred: &mut F) -> bool {
         match *self {
-            Policy::Unsatisfiable | Policy::Trivial => true,
-            Policy::Key(ref pk) => pred(pk),
-            Policy::Sha256(..)
-            | Policy::Hash256(..)
-            | Policy::Ripemd160(..)
-            | Policy::Hash160(..)
-            | Policy::After(..)
-            | Policy::Older(..) => true,
-            Policy::Threshold(_, ref subs) => {
+            Abstract::Unsatisfiable | Abstract::Trivial => true,
+            Abstract::Key(ref pk) => pred(pk),
+            Abstract::Sha256(..)
+            | Abstract::Hash256(..)
+            | Abstract::Ripemd160(..)
+            | Abstract::Hash160(..)
+            | Abstract::After(..)
+            | Abstract::Older(..) => true,
+            Abstract::Threshold(_, ref subs) => {
                 subs.iter().all(|sub| sub.real_for_each_key(&mut *pred))
             }
         }
@@ -93,10 +95,10 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
     /// use std::collections::HashMap;
     /// use std::str::FromStr;
     /// use miniscript::bitcoin::{hashes::hash160, PublicKey};
-    /// use miniscript::{translate_hash_fail, policy::semantic::Policy, Translator};
+    /// use miniscript::{translate_hash_fail, r#abstract::Abstract, Translator};
     /// let alice_pk = "02c79ef3ede6d14f72a00d0e49b4becfb152197b64c0707425c4f231df29500ee7";
     /// let bob_pk = "03d008a849fbf474bd17e9d2c1a827077a468150e58221582ec3410ab309f5afe4";
-    /// let placeholder_policy = Policy::<String>::from_str("and(pk(alice_pk),pk(bob_pk))").unwrap();
+    /// let placeholder_policy = Abstract::<String>::from_str("and(pk(alice_pk),pk(bob_pk))").unwrap();
     ///
     /// // Information to translate abstract string type keys to concrete `bitcoin::PublicKey`s.
     /// // In practice, wallets would map from string key names to BIP32 keys.
@@ -123,10 +125,10 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
     ///
     /// let real_policy = placeholder_policy.translate_pk(&mut t).unwrap();
     ///
-    /// let expected_policy = Policy::from_str(&format!("and(pk({}),pk({}))", alice_pk, bob_pk)).unwrap();
+    /// let expected_policy = Abstract::from_str(&format!("and(pk({}),pk({}))", alice_pk, bob_pk)).unwrap();
     /// assert_eq!(real_policy, expected_policy);
     /// ```
-    pub fn translate_pk<Q, E, T>(&self, t: &mut T) -> Result<Policy<Q>, E>
+    pub fn translate_pk<Q, E, T>(&self, t: &mut T) -> Result<Abstract<Q>, E>
     where
         T: Translator<Pk, Q, E>,
         Q: MiniscriptKey,
@@ -134,25 +136,25 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
         self._translate_pk(t)
     }
 
-    fn _translate_pk<Q, E, T>(&self, t: &mut T) -> Result<Policy<Q>, E>
+    fn _translate_pk<Q, E, T>(&self, t: &mut T) -> Result<Abstract<Q>, E>
     where
         T: Translator<Pk, Q, E>,
         Q: MiniscriptKey,
     {
         match *self {
-            Policy::Unsatisfiable => Ok(Policy::Unsatisfiable),
-            Policy::Trivial => Ok(Policy::Trivial),
-            Policy::Key(ref pk) => t.pk(pk).map(Policy::Key),
-            Policy::Sha256(ref h) => t.sha256(h).map(Policy::Sha256),
-            Policy::Hash256(ref h) => t.hash256(h).map(Policy::Hash256),
-            Policy::Ripemd160(ref h) => t.ripemd160(h).map(Policy::Ripemd160),
-            Policy::Hash160(ref h) => t.hash160(h).map(Policy::Hash160),
-            Policy::After(n) => Ok(Policy::After(n)),
-            Policy::Older(n) => Ok(Policy::Older(n)),
-            Policy::Threshold(k, ref subs) => {
-                let new_subs: Result<Vec<Policy<Q>>, _> =
+            Abstract::Unsatisfiable => Ok(Abstract::Unsatisfiable),
+            Abstract::Trivial => Ok(Abstract::Trivial),
+            Abstract::Key(ref pk) => t.pk(pk).map(Abstract::Key),
+            Abstract::Sha256(ref h) => t.sha256(h).map(Abstract::Sha256),
+            Abstract::Hash256(ref h) => t.hash256(h).map(Abstract::Hash256),
+            Abstract::Ripemd160(ref h) => t.ripemd160(h).map(Abstract::Ripemd160),
+            Abstract::Hash160(ref h) => t.hash160(h).map(Abstract::Hash160),
+            Abstract::After(n) => Ok(Abstract::After(n)),
+            Abstract::Older(n) => Ok(Abstract::Older(n)),
+            Abstract::Threshold(k, ref subs) => {
+                let new_subs: Result<Vec<Abstract<Q>>, _> =
                     subs.iter().map(|sub| sub._translate_pk(t)).collect();
-                new_subs.map(|ok| Policy::Threshold(k, ok))
+                new_subs.map(|ok| Abstract::Threshold(k, ok))
             }
         }
     }
@@ -165,15 +167,15 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
     /// sufficient for most practical policies.
     // This algorithm has a naive implementation. It is possible to optimize this
     // by memoizing and maintaining a hashmap.
-    pub fn entails(self, other: Policy<Pk>) -> Result<bool, PolicyError> {
+    pub fn entails(self, other: Abstract<Pk>) -> Result<bool, PolicyError> {
         if self.n_terminals() > ENTAILMENT_MAX_TERMINALS {
             return Err(PolicyError::EntailmentMaxTerminals);
         }
         match (self, other) {
-            (Policy::Unsatisfiable, _) => Ok(true),
-            (Policy::Trivial, Policy::Trivial) => Ok(true),
-            (Policy::Trivial, _) => Ok(false),
-            (_, Policy::Unsatisfiable) => Ok(false),
+            (Abstract::Unsatisfiable, _) => Ok(true),
+            (Abstract::Trivial, Abstract::Trivial) => Ok(true),
+            (Abstract::Trivial, _) => Ok(false),
+            (_, Abstract::Unsatisfiable) => Ok(false),
             (a, b) => {
                 let (a_norm, b_norm) = (a.normalized(), b.normalized());
                 let first_constraint = a_norm.first_constraint();
@@ -185,7 +187,7 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
                     a_norm.satisfy_constraint(&first_constraint, false),
                     b_norm.satisfy_constraint(&first_constraint, false),
                 );
-                Ok(Policy::entails(a1, b1)? && Policy::entails(a2, b2)?)
+                Ok(Abstract::entails(a1, b1)? && Abstract::entails(a2, b2)?)
             }
         }
     }
@@ -193,8 +195,8 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
     // Helper function to compute the number of constraints in policy.
     fn n_terminals(&self) -> usize {
         match self {
-            &Policy::Threshold(_k, ref subs) => subs.iter().map(|sub| sub.n_terminals()).sum(),
-            &Policy::Trivial | &Policy::Unsatisfiable => 0,
+            &Abstract::Threshold(_k, ref subs) => subs.iter().map(|sub| sub.n_terminals()).sum(),
+            &Abstract::Trivial | &Abstract::Unsatisfiable => 0,
             _leaf => 1,
         }
     }
@@ -202,10 +204,10 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
     // Helper function to get the first constraint in the policy.
     // Returns the first leaf policy. Used in policy entailment.
     // Assumes that the current policy is normalized.
-    fn first_constraint(&self) -> Policy<Pk> {
+    fn first_constraint(&self) -> Abstract<Pk> {
         debug_assert!(self.clone().normalized() == self.clone());
         match self {
-            &Policy::Threshold(_k, ref subs) => subs[0].first_constraint(),
+            &Abstract::Threshold(_k, ref subs) => subs[0].first_constraint(),
             first => first.clone(),
         }
     }
@@ -214,26 +216,30 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
     // to true or false and returning the resultant normalized policy. Witness
     // is currently encoded as policy. Only accepts leaf fragment and a
     // normalized policy
-    pub(crate) fn satisfy_constraint(self, witness: &Policy<Pk>, available: bool) -> Policy<Pk> {
+    pub(crate) fn satisfy_constraint(
+        self,
+        witness: &Abstract<Pk>,
+        available: bool,
+    ) -> Abstract<Pk> {
         debug_assert!(self.clone().normalized() == self);
-        if let Policy::Threshold { .. } = *witness {
-            // We can't debug_assert on Policy::Threshold.
+        if let Abstract::Threshold { .. } = *witness {
+            // We can't debug_assert on Abstract::Threshold.
             panic!("should be unreachable")
         }
 
         let ret = match self {
-            Policy::Threshold(k, subs) => {
+            Abstract::Threshold(k, subs) => {
                 let mut ret_subs = vec![];
                 for sub in subs {
                     ret_subs.push(sub.satisfy_constraint(witness, available));
                 }
-                Policy::Threshold(k, ret_subs)
+                Abstract::Threshold(k, ret_subs)
             }
             ref leaf if leaf == witness => {
                 if available {
-                    Policy::Trivial
+                    Abstract::Trivial
                 } else {
-                    Policy::Unsatisfiable
+                    Abstract::Unsatisfiable
                 }
             }
             x => x,
@@ -242,19 +248,19 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
     }
 }
 
-impl<Pk: MiniscriptKey> fmt::Debug for Policy<Pk> {
+impl<Pk: MiniscriptKey> fmt::Debug for Abstract<Pk> {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match *self {
-            Policy::Unsatisfiable => f.write_str("UNSATISFIABLE()"),
-            Policy::Trivial => f.write_str("TRIVIAL()"),
-            Policy::Key(ref pkh) => write!(f, "pk({:?})", pkh),
-            Policy::After(n) => write!(f, "after({})", n),
-            Policy::Older(n) => write!(f, "older({})", n),
-            Policy::Sha256(ref h) => write!(f, "sha256({})", h),
-            Policy::Hash256(ref h) => write!(f, "hash256({})", h),
-            Policy::Ripemd160(ref h) => write!(f, "ripemd160({})", h),
-            Policy::Hash160(ref h) => write!(f, "hash160({})", h),
-            Policy::Threshold(k, ref subs) => {
+            Abstract::Unsatisfiable => f.write_str("UNSATISFIABLE()"),
+            Abstract::Trivial => f.write_str("TRIVIAL()"),
+            Abstract::Key(ref pkh) => write!(f, "pk({:?})", pkh),
+            Abstract::After(n) => write!(f, "after({})", n),
+            Abstract::Older(n) => write!(f, "older({})", n),
+            Abstract::Sha256(ref h) => write!(f, "sha256({})", h),
+            Abstract::Hash256(ref h) => write!(f, "hash256({})", h),
+            Abstract::Ripemd160(ref h) => write!(f, "ripemd160({})", h),
+            Abstract::Hash160(ref h) => write!(f, "hash160({})", h),
+            Abstract::Threshold(k, ref subs) => {
                 if k == subs.len() {
                     write!(f, "and(")?;
                 } else if k == 1 {
@@ -275,19 +281,19 @@ impl<Pk: MiniscriptKey> fmt::Debug for Policy<Pk> {
     }
 }
 
-impl<Pk: MiniscriptKey> fmt::Display for Policy<Pk> {
+impl<Pk: MiniscriptKey> fmt::Display for Abstract<Pk> {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match *self {
-            Policy::Unsatisfiable => f.write_str("UNSATISFIABLE"),
-            Policy::Trivial => f.write_str("TRIVIAL"),
-            Policy::Key(ref pkh) => write!(f, "pk({})", pkh),
-            Policy::After(n) => write!(f, "after({})", n),
-            Policy::Older(n) => write!(f, "older({})", n),
-            Policy::Sha256(ref h) => write!(f, "sha256({})", h),
-            Policy::Hash256(ref h) => write!(f, "hash256({})", h),
-            Policy::Ripemd160(ref h) => write!(f, "ripemd160({})", h),
-            Policy::Hash160(ref h) => write!(f, "hash160({})", h),
-            Policy::Threshold(k, ref subs) => {
+            Abstract::Unsatisfiable => f.write_str("UNSATISFIABLE"),
+            Abstract::Trivial => f.write_str("TRIVIAL"),
+            Abstract::Key(ref pkh) => write!(f, "pk({})", pkh),
+            Abstract::After(n) => write!(f, "after({})", n),
+            Abstract::Older(n) => write!(f, "older({})", n),
+            Abstract::Sha256(ref h) => write!(f, "sha256({})", h),
+            Abstract::Hash256(ref h) => write!(f, "hash256({})", h),
+            Abstract::Ripemd160(ref h) => write!(f, "ripemd160({})", h),
+            Abstract::Hash160(ref h) => write!(f, "hash160({})", h),
+            Abstract::Threshold(k, ref subs) => {
                 if k == subs.len() {
                     write!(f, "and(")?;
                 } else if k == 1 {
@@ -309,9 +315,9 @@ impl<Pk: MiniscriptKey> fmt::Display for Policy<Pk> {
 }
 
 impl_from_str!(
-    Policy<Pk>,
+    Abstract<Pk>,
     type Err = Error;,
-    fn from_str(s: &str) -> Result<Policy<Pk>, Error> {
+    fn from_str(s: &str) -> Result<Abstract<Pk>, Error> {
         expression::check_valid_chars(s)?;
 
         let tree = expression::Tree::from_str(s)?;
@@ -319,32 +325,34 @@ impl_from_str!(
     }
 );
 
-serde_string_impl_pk!(Policy, "a miniscript semantic policy");
+serde_string_impl_pk!(Abstract, "a miniscript semantic policy");
 
 impl_from_tree!(
-    Policy<Pk>,
-    fn from_tree(top: &expression::Tree) -> Result<Policy<Pk>, Error> {
+    Abstract<Pk>,
+    fn from_tree(top: &expression::Tree) -> Result<Abstract<Pk>, Error> {
         match (top.name, top.args.len()) {
-            ("UNSATISFIABLE", 0) => Ok(Policy::Unsatisfiable),
-            ("TRIVIAL", 0) => Ok(Policy::Trivial),
-            ("pk", 1) => expression::terminal(&top.args[0], |pk| Pk::from_str(pk).map(Policy::Key)),
+            ("UNSATISFIABLE", 0) => Ok(Abstract::Unsatisfiable),
+            ("TRIVIAL", 0) => Ok(Abstract::Trivial),
+            ("pk", 1) => {
+                expression::terminal(&top.args[0], |pk| Pk::from_str(pk).map(Abstract::Key))
+            }
             ("after", 1) => expression::terminal(&top.args[0], |x| {
-                expression::parse_num(x).map(|x| Policy::after(x))
+                expression::parse_num(x).map(|x| Abstract::after(x))
             }),
             ("older", 1) => expression::terminal(&top.args[0], |x| {
-                expression::parse_num(x).map(|x| Policy::older(x))
+                expression::parse_num(x).map(|x| Abstract::older(x))
+            }),
+            ("sha256", 1) => expression::terminal(&top.args[0], |x| {
+                Pk::Sha256::from_str(x).map(Abstract::Sha256)
             }),
-            ("sha256", 1) => {
-                expression::terminal(&top.args[0], |x| Pk::Sha256::from_str(x).map(Policy::Sha256))
-            }
             ("hash256", 1) => expression::terminal(&top.args[0], |x| {
-                Pk::Hash256::from_str(x).map(Policy::Hash256)
+                Pk::Hash256::from_str(x).map(Abstract::Hash256)
             }),
             ("ripemd160", 1) => expression::terminal(&top.args[0], |x| {
-                Pk::Ripemd160::from_str(x).map(Policy::Ripemd160)
+                Pk::Ripemd160::from_str(x).map(Abstract::Ripemd160)
             }),
             ("hash160", 1) => expression::terminal(&top.args[0], |x| {
-                Pk::Hash160::from_str(x).map(Policy::Hash160)
+                Pk::Hash160::from_str(x).map(Abstract::Hash160)
             }),
             ("and", nsubs) => {
                 if nsubs < 2 {
@@ -352,9 +360,9 @@ impl_from_tree!(
                 }
                 let mut subs = Vec::with_capacity(nsubs);
                 for arg in &top.args {
-                    subs.push(Policy::from_tree(arg)?);
+                    subs.push(Abstract::from_tree(arg)?);
                 }
-                Ok(Policy::Threshold(nsubs, subs))
+                Ok(Abstract::Threshold(nsubs, subs))
             }
             ("or", nsubs) => {
                 if nsubs < 2 {
@@ -362,9 +370,9 @@ impl_from_tree!(
                 }
                 let mut subs = Vec::with_capacity(nsubs);
                 for arg in &top.args {
-                    subs.push(Policy::from_tree(arg)?);
+                    subs.push(Abstract::from_tree(arg)?);
                 }
-                Ok(Policy::Threshold(1, subs))
+                Ok(Abstract::Threshold(1, subs))
             }
             ("thresh", nsubs) => {
                 if nsubs == 0 || nsubs == 1 {
@@ -380,7 +388,7 @@ impl_from_tree!(
                 // thresh(1) and thresh(n) are disallowed in semantic policies
                 if thresh <= 1 || thresh >= (nsubs as u32 - 1) {
                     return Err(errstr(
-                        "Semantic Policy thresh cannot have k = 1 or k =n, use `and`/`or` instead",
+                        "Abstract Abstract thresh cannot have k = 1 or k =n, use `and`/`or` instead",
                     ));
                 }
                 if thresh >= (nsubs as u32) {
@@ -389,28 +397,28 @@ impl_from_tree!(
 
                 let mut subs = Vec::with_capacity(top.args.len() - 1);
                 for arg in &top.args[1..] {
-                    subs.push(Policy::from_tree(arg)?);
+                    subs.push(Abstract::from_tree(arg)?);
                 }
-                Ok(Policy::Threshold(thresh as usize, subs))
+                Ok(Abstract::Threshold(thresh as usize, subs))
             }
             _ => Err(errstr(top.name)),
         }
     }
 );
 
-impl<Pk: MiniscriptKey> Policy<Pk> {
+impl<Pk: MiniscriptKey> Abstract<Pk> {
     /// Flattens out trees of `And`s and `Or`s; eliminate `Trivial` and
     /// `Unsatisfiable`s. Does not reorder any branches; use `.sort`.
-    pub fn normalized(self) -> Policy<Pk> {
+    pub fn normalized(self) -> Abstract<Pk> {
         match self {
-            Policy::Threshold(k, subs) => {
+            Abstract::Threshold(k, subs) => {
                 let mut ret_subs = Vec::with_capacity(subs.len());
 
                 let subs: Vec<_> = subs.into_iter().map(|sub| sub.normalized()).collect();
-                let trivial_count = subs.iter().filter(|&pol| *pol == Policy::Trivial).count();
+                let trivial_count = subs.iter().filter(|&pol| *pol == Abstract::Trivial).count();
                 let unsatisfied_count = subs
                     .iter()
-                    .filter(|&pol| *pol == Policy::Unsatisfiable)
+                    .filter(|&pol| *pol == Abstract::Unsatisfiable)
                     .count();
 
                 let n = subs.len() - unsatisfied_count - trivial_count; // remove all true/false
@@ -420,16 +428,16 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
                 let is_or = m == 1;
                 for sub in subs {
                     match sub {
-                        Policy::Trivial | Policy::Unsatisfiable => {}
-                        Policy::Threshold(k, subs) => {
+                        Abstract::Trivial | Abstract::Unsatisfiable => {}
+                        Abstract::Threshold(k, subs) => {
                             match (is_and, is_or) {
                                 (true, true) => {
                                     // means m = n = 1, thresh(1,X) type thing.
-                                    ret_subs.push(Policy::Threshold(k, subs));
+                                    ret_subs.push(Abstract::Threshold(k, subs));
                                 }
                                 (true, false) if k == subs.len() => ret_subs.extend(subs), // and case
                                 (false, true) if k == 1 => ret_subs.extend(subs), // or case
-                                _ => ret_subs.push(Policy::Threshold(k, subs)),
+                                _ => ret_subs.push(Abstract::Threshold(k, subs)),
                             }
                         }
                         x => ret_subs.push(x),
@@ -437,17 +445,17 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
                 }
                 // Now reason about m of n threshold
                 if m == 0 {
-                    Policy::Trivial
+                    Abstract::Trivial
                 } else if m > ret_subs.len() {
-                    Policy::Unsatisfiable
+                    Abstract::Unsatisfiable
                 } else if ret_subs.len() == 1 {
                     ret_subs.pop().unwrap()
                 } else if is_and {
-                    Policy::Threshold(ret_subs.len(), ret_subs)
+                    Abstract::Threshold(ret_subs.len(), ret_subs)
                 } else if is_or {
-                    Policy::Threshold(1, ret_subs)
+                    Abstract::Threshold(1, ret_subs)
                 } else {
-                    Policy::Threshold(m, ret_subs)
+                    Abstract::Threshold(m, ret_subs)
                 }
             }
             x => x,
@@ -456,31 +464,31 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
 
     /// Detects a true/trivial policy.
     ///
-    /// Only checks whether the policy is `Policy::Trivial`, to check if the
+    /// Only checks whether the policy is `Abstract::Trivial`, to check if the
     /// normalized form is trivial, the caller is expected to normalize the
     /// policy first.
-    pub fn is_trivial(&self) -> bool { matches!(*self, Policy::Trivial) }
+    pub fn is_trivial(&self) -> bool { matches!(*self, Abstract::Trivial) }
 
     /// Detects a false/unsatisfiable policy.
     ///
-    /// Only checks whether the policy is `Policy::Unsatisfiable`, to check if
+    /// Only checks whether the policy is `Abstract::Unsatisfiable`, to check if
     /// the normalized form is unsatisfiable, the caller is expected to
     /// normalize the policy first.
-    pub fn is_unsatisfiable(&self) -> bool { matches!(*self, Policy::Unsatisfiable) }
+    pub fn is_unsatisfiable(&self) -> bool { matches!(*self, Abstract::Unsatisfiable) }
 
     /// Helper function to do the recursion in `timelocks`.
     fn real_relative_timelocks(&self) -> Vec<u32> {
         match *self {
-            Policy::Unsatisfiable
-            | Policy::Trivial
-            | Policy::Key(..)
-            | Policy::Sha256(..)
-            | Policy::Hash256(..)
-            | Policy::Ripemd160(..)
-            | Policy::Hash160(..) => vec![],
-            Policy::After(..) => vec![],
-            Policy::Older(t) => vec![t.to_consensus_u32()],
-            Policy::Threshold(_, ref subs) => subs.iter().fold(vec![], |mut acc, x| {
+            Abstract::Unsatisfiable
+            | Abstract::Trivial
+            | Abstract::Key(..)
+            | Abstract::Sha256(..)
+            | Abstract::Hash256(..)
+            | Abstract::Ripemd160(..)
+            | Abstract::Hash160(..) => vec![],
+            Abstract::After(..) => vec![],
+            Abstract::Older(t) => vec![t.to_consensus_u32()],
+            Abstract::Threshold(_, ref subs) => subs.iter().fold(vec![], |mut acc, x| {
                 acc.extend(x.real_relative_timelocks());
                 acc
             }),
@@ -499,16 +507,16 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
     /// Helper function for recursion in `absolute timelocks`
     fn real_absolute_timelocks(&self) -> Vec<u32> {
         match *self {
-            Policy::Unsatisfiable
-            | Policy::Trivial
-            | Policy::Key(..)
-            | Policy::Sha256(..)
-            | Policy::Hash256(..)
-            | Policy::Ripemd160(..)
-            | Policy::Hash160(..) => vec![],
-            Policy::Older(..) => vec![],
-            Policy::After(t) => vec![t.to_u32()],
-            Policy::Threshold(_, ref subs) => subs.iter().fold(vec![], |mut acc, x| {
+            Abstract::Unsatisfiable
+            | Abstract::Trivial
+            | Abstract::Key(..)
+            | Abstract::Sha256(..)
+            | Abstract::Hash256(..)
+            | Abstract::Ripemd160(..)
+            | Abstract::Hash160(..) => vec![],
+            Abstract::Older(..) => vec![],
+            Abstract::After(t) => vec![t.to_u32()],
+            Abstract::Threshold(_, ref subs) => subs.iter().fold(vec![], |mut acc, x| {
                 acc.extend(x.real_absolute_timelocks());
                 acc
             }),
@@ -526,20 +534,20 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
 
     /// Filters a policy by eliminating relative timelock constraints
     /// that are not satisfied at the given `age`.
-    pub fn at_age(mut self, age: Sequence) -> Policy<Pk> {
+    pub fn at_age(mut self, age: Sequence) -> Abstract<Pk> {
         self = match self {
-            Policy::Older(t) => {
+            Abstract::Older(t) => {
                 if t.is_height_locked() && age.is_time_locked()
                     || t.is_time_locked() && age.is_height_locked()
                     || t.to_consensus_u32() > age.to_consensus_u32()
                 {
-                    Policy::Unsatisfiable
+                    Abstract::Unsatisfiable
                 } else {
-                    Policy::Older(t)
+                    Abstract::Older(t)
                 }
             }
-            Policy::Threshold(k, subs) => {
-                Policy::Threshold(k, subs.into_iter().map(|sub| sub.at_age(age)).collect())
+            Abstract::Threshold(k, subs) => {
+                Abstract::Threshold(k, subs.into_iter().map(|sub| sub.at_age(age)).collect())
             }
             x => x,
         };
@@ -548,11 +556,11 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
 
     /// Filters a policy by eliminating absolute timelock constraints
     /// that are not satisfied at the given `n` (`n OP_CHECKLOCKTIMEVERIFY`).
-    pub fn at_lock_time(mut self, n: absolute::LockTime) -> Policy<Pk> {
+    pub fn at_lock_time(mut self, n: absolute::LockTime) -> Abstract<Pk> {
         use absolute::LockTime::*;
 
         self = match self {
-            Policy::After(t) => {
+            Abstract::After(t) => {
                 let t = absolute::LockTime::from(t);
                 let is_satisfied_by = match (t, n) {
                     (Blocks(t), Blocks(n)) => t <= n,
@@ -560,13 +568,13 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
                     _ => false,
                 };
                 if !is_satisfied_by {
-                    Policy::Unsatisfiable
+                    Abstract::Unsatisfiable
                 } else {
-                    Policy::After(t.into())
+                    Abstract::After(t.into())
                 }
             }
-            Policy::Threshold(k, subs) => {
-                Policy::Threshold(k, subs.into_iter().map(|sub| sub.at_lock_time(n)).collect())
+            Abstract::Threshold(k, subs) => {
+                Abstract::Threshold(k, subs.into_iter().map(|sub| sub.at_lock_time(n)).collect())
             }
             x => x,
         };
@@ -577,15 +585,15 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
     /// Duplicate keys will be double-counted.
     pub fn n_keys(&self) -> usize {
         match *self {
-            Policy::Unsatisfiable | Policy::Trivial => 0,
-            Policy::Key(..) => 1,
-            Policy::After(..)
-            | Policy::Older(..)
-            | Policy::Sha256(..)
-            | Policy::Hash256(..)
-            | Policy::Ripemd160(..)
-            | Policy::Hash160(..) => 0,
-            Policy::Threshold(_, ref subs) => subs.iter().map(|sub| sub.n_keys()).sum::<usize>(),
+            Abstract::Unsatisfiable | Abstract::Trivial => 0,
+            Abstract::Key(..) => 1,
+            Abstract::After(..)
+            | Abstract::Older(..)
+            | Abstract::Sha256(..)
+            | Abstract::Hash256(..)
+            | Abstract::Ripemd160(..)
+            | Abstract::Hash160(..) => 0,
+            Abstract::Threshold(_, ref subs) => subs.iter().map(|sub| sub.n_keys()).sum::<usize>(),
         }
     }
 
@@ -597,18 +605,18 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
     /// Returns `None` if the policy is not satisfiable.
     pub fn minimum_n_keys(&self) -> Option<usize> {
         match *self {
-            Policy::Unsatisfiable => None,
-            Policy::Trivial => Some(0),
-            Policy::Key(..) => Some(1),
-            Policy::After(..)
-            | Policy::Older(..)
-            | Policy::Sha256(..)
-            | Policy::Hash256(..)
-            | Policy::Ripemd160(..)
-            | Policy::Hash160(..) => Some(0),
-            Policy::Threshold(k, ref subs) => {
+            Abstract::Unsatisfiable => None,
+            Abstract::Trivial => Some(0),
+            Abstract::Key(..) => Some(1),
+            Abstract::After(..)
+            | Abstract::Older(..)
+            | Abstract::Sha256(..)
+            | Abstract::Hash256(..)
+            | Abstract::Ripemd160(..)
+            | Abstract::Hash160(..) => Some(0),
+            Abstract::Threshold(k, ref subs) => {
                 let mut sublens: Vec<usize> =
-                    subs.iter().filter_map(Policy::minimum_n_keys).collect();
+                    subs.iter().filter_map(Abstract::minimum_n_keys).collect();
                 if sublens.len() < k {
                     // Not enough branches are satisfiable
                     None
@@ -621,18 +629,18 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
     }
 }
 
-impl<Pk: MiniscriptKey> Policy<Pk> {
+impl<Pk: MiniscriptKey> Abstract<Pk> {
     /// "Sorts" a policy to bring it into a canonical form to allow comparisons.
     ///
     /// Does **not** allow policies to be compared for functional equivalence;
     /// in general this appears to require Gröbner basis techniques that are not
     /// implemented.
-    pub fn sorted(self) -> Policy<Pk> {
+    pub fn sorted(self) -> Abstract<Pk> {
         match self {
-            Policy::Threshold(k, subs) => {
-                let mut new_subs: Vec<_> = subs.into_iter().map(Policy::sorted).collect();
+            Abstract::Threshold(k, subs) => {
+                let mut new_subs: Vec<_> = subs.into_iter().map(Abstract::sorted).collect();
                 new_subs.sort();
-                Policy::Threshold(k, new_subs)
+                Abstract::Threshold(k, new_subs)
             }
             x => x,
         }
@@ -647,25 +655,25 @@ mod tests {
 
     use super::*;
 
-    type StringPolicy = Policy<String>;
+    type StringAbstract = Abstract<String>;
 
     #[test]
     fn parse_policy_err() {
-        assert!(StringPolicy::from_str("(").is_err());
-        assert!(StringPolicy::from_str("(x()").is_err());
-        assert!(StringPolicy::from_str("(\u{7f}()3").is_err());
-        assert!(StringPolicy::from_str("pk()").is_ok());
+        assert!(StringAbstract::from_str("(").is_err());
+        assert!(StringAbstract::from_str("(x()").is_err());
+        assert!(StringAbstract::from_str("(\u{7f}()3").is_err());
+        assert!(StringAbstract::from_str("pk()").is_ok());
 
-        assert!(StringPolicy::from_str("or(or)").is_err());
+        assert!(StringAbstract::from_str("or(or)").is_err());
 
-        assert!(Policy::<PublicKey>::from_str("pk()").is_err());
-        assert!(Policy::<PublicKey>::from_str(
+        assert!(Abstract::<PublicKey>::from_str("pk()").is_err());
+        assert!(Abstract::<PublicKey>::from_str(
             "pk(\
              0200000000000000000000000000000000000002\
              )"
         )
         .is_err());
-        assert!(Policy::<PublicKey>::from_str(
+        assert!(Abstract::<PublicKey>::from_str(
             "pk(\
                 02c79ef3ede6d14f72a00d0e49b4becfb152197b64c0707425c4f231df29500ee7\
              )"
@@ -675,8 +683,8 @@ mod tests {
 
     #[test]
     fn semantic_analysis() {
-        let policy = StringPolicy::from_str("pk()").unwrap();
-        assert_eq!(policy, Policy::Key("".to_owned()));
+        let policy = StringAbstract::from_str("pk()").unwrap();
+        assert_eq!(policy, Abstract::Key("".to_owned()));
         assert_eq!(policy.relative_timelocks(), vec![]);
         assert_eq!(policy.absolute_timelocks(), vec![]);
         assert_eq!(policy.clone().at_age(Sequence::ZERO), policy);
@@ -684,32 +692,32 @@ mod tests {
         assert_eq!(policy.n_keys(), 1);
         assert_eq!(policy.minimum_n_keys(), Some(1));
 
-        let policy = StringPolicy::from_str("older(1000)").unwrap();
-        assert_eq!(policy, Policy::Older(Sequence::from_height(1000)));
+        let policy = StringAbstract::from_str("older(1000)").unwrap();
+        assert_eq!(policy, Abstract::Older(Sequence::from_height(1000)));
         assert_eq!(policy.absolute_timelocks(), vec![]);
         assert_eq!(policy.relative_timelocks(), vec![1000]);
-        assert_eq!(policy.clone().at_age(Sequence::ZERO), Policy::Unsatisfiable);
-        assert_eq!(policy.clone().at_age(Sequence::from_height(999)), Policy::Unsatisfiable);
+        assert_eq!(policy.clone().at_age(Sequence::ZERO), Abstract::Unsatisfiable);
+        assert_eq!(policy.clone().at_age(Sequence::from_height(999)), Abstract::Unsatisfiable);
         assert_eq!(policy.clone().at_age(Sequence::from_height(1000)), policy);
         assert_eq!(policy.clone().at_age(Sequence::from_height(10000)), policy);
         assert_eq!(policy.n_keys(), 0);
         assert_eq!(policy.minimum_n_keys(), Some(0));
 
-        let policy = StringPolicy::from_str("or(pk(),older(1000))").unwrap();
+        let policy = StringAbstract::from_str("or(pk(),older(1000))").unwrap();
         assert_eq!(
             policy,
-            Policy::Threshold(
+            Abstract::Threshold(
                 1,
                 vec![
-                    Policy::Key("".to_owned()),
-                    Policy::Older(Sequence::from_height(1000)),
+                    Abstract::Key("".to_owned()),
+                    Abstract::Older(Sequence::from_height(1000)),
                 ]
             )
         );
         assert_eq!(policy.relative_timelocks(), vec![1000]);
         assert_eq!(policy.absolute_timelocks(), vec![]);
-        assert_eq!(policy.clone().at_age(Sequence::ZERO), Policy::Key("".to_owned()));
-        assert_eq!(policy.clone().at_age(Sequence::from_height(999)), Policy::Key("".to_owned()));
+        assert_eq!(policy.clone().at_age(Sequence::ZERO), Abstract::Key("".to_owned()));
+        assert_eq!(policy.clone().at_age(Sequence::from_height(999)), Abstract::Key("".to_owned()));
         assert_eq!(policy.clone().at_age(Sequence::from_height(1000)), policy.clone().normalized());
         assert_eq!(
             policy.clone().at_age(Sequence::from_height(10000)),
@@ -718,27 +726,27 @@ mod tests {
         assert_eq!(policy.n_keys(), 1);
         assert_eq!(policy.minimum_n_keys(), Some(0));
 
-        let policy = StringPolicy::from_str("or(pk(),UNSATISFIABLE)").unwrap();
+        let policy = StringAbstract::from_str("or(pk(),UNSATISFIABLE)").unwrap();
         assert_eq!(
             policy,
-            Policy::Threshold(1, vec![Policy::Key("".to_owned()), Policy::Unsatisfiable,])
+            Abstract::Threshold(1, vec![Abstract::Key("".to_owned()), Abstract::Unsatisfiable,])
         );
         assert_eq!(policy.relative_timelocks(), vec![]);
         assert_eq!(policy.absolute_timelocks(), vec![]);
         assert_eq!(policy.n_keys(), 1);
         assert_eq!(policy.minimum_n_keys(), Some(1));
 
-        let policy = StringPolicy::from_str("and(pk(),UNSATISFIABLE)").unwrap();
+        let policy = StringAbstract::from_str("and(pk(),UNSATISFIABLE)").unwrap();
         assert_eq!(
             policy,
-            Policy::Threshold(2, vec![Policy::Key("".to_owned()), Policy::Unsatisfiable,])
+            Abstract::Threshold(2, vec![Abstract::Key("".to_owned()), Abstract::Unsatisfiable,])
         );
         assert_eq!(policy.relative_timelocks(), vec![]);
         assert_eq!(policy.absolute_timelocks(), vec![]);
         assert_eq!(policy.n_keys(), 1);
         assert_eq!(policy.minimum_n_keys(), None);
 
-        let policy = StringPolicy::from_str(
+        let policy = StringAbstract::from_str(
             "thresh(\
              2,older(1000),older(10000),older(1000),older(2000),older(2000)\
              )",
@@ -746,14 +754,14 @@ mod tests {
         .unwrap();
         assert_eq!(
             policy,
-            Policy::Threshold(
+            Abstract::Threshold(
                 2,
                 vec![
-                    Policy::Older(Sequence::from_height(1000)),
-                    Policy::Older(Sequence::from_height(10000)),
-                    Policy::Older(Sequence::from_height(1000)),
-                    Policy::Older(Sequence::from_height(2000)),
-                    Policy::Older(Sequence::from_height(2000)),
+                    Abstract::Older(Sequence::from_height(1000)),
+                    Abstract::Older(Sequence::from_height(10000)),
+                    Abstract::Older(Sequence::from_height(1000)),
+                    Abstract::Older(Sequence::from_height(2000)),
+                    Abstract::Older(Sequence::from_height(2000)),
                 ]
             )
         );
@@ -762,7 +770,7 @@ mod tests {
             vec![1000, 2000, 10000] //sorted and dedup'd
         );
 
-        let policy = StringPolicy::from_str(
+        let policy = StringAbstract::from_str(
             "thresh(\
              2,older(1000),older(10000),older(1000),UNSATISFIABLE,UNSATISFIABLE\
              )",
@@ -770,14 +778,14 @@ mod tests {
         .unwrap();
         assert_eq!(
             policy,
-            Policy::Threshold(
+            Abstract::Threshold(
                 2,
                 vec![
-                    Policy::Older(Sequence::from_height(1000)),
-                    Policy::Older(Sequence::from_height(10000)),
-                    Policy::Older(Sequence::from_height(1000)),
-                    Policy::Unsatisfiable,
-                    Policy::Unsatisfiable,
+                    Abstract::Older(Sequence::from_height(1000)),
+                    Abstract::Older(Sequence::from_height(10000)),
+                    Abstract::Older(Sequence::from_height(1000)),
+                    Abstract::Unsatisfiable,
+                    Abstract::Unsatisfiable,
                 ]
             )
         );
@@ -789,16 +797,16 @@ mod tests {
         assert_eq!(policy.minimum_n_keys(), Some(0));
 
         // Block height 1000.
-        let policy = StringPolicy::from_str("after(1000)").unwrap();
-        assert_eq!(policy, Policy::after(1000));
+        let policy = StringAbstract::from_str("after(1000)").unwrap();
+        assert_eq!(policy, Abstract::after(1000));
         assert_eq!(policy.absolute_timelocks(), vec![1000]);
         assert_eq!(policy.relative_timelocks(), vec![]);
-        assert_eq!(policy.clone().at_lock_time(absolute::LockTime::ZERO), Policy::Unsatisfiable);
+        assert_eq!(policy.clone().at_lock_time(absolute::LockTime::ZERO), Abstract::Unsatisfiable);
         assert_eq!(
             policy
                 .clone()
                 .at_lock_time(absolute::LockTime::from_height(999).expect("valid block height")),
-            Policy::Unsatisfiable
+            Abstract::Unsatisfiable
         );
         assert_eq!(
             policy
@@ -817,48 +825,48 @@ mod tests {
             policy
                 .clone()
                 .at_lock_time(absolute::LockTime::from_time(500_000_001).expect("valid timestamp")),
-            Policy::Unsatisfiable
+            Abstract::Unsatisfiable
         );
         assert_eq!(policy.n_keys(), 0);
         assert_eq!(policy.minimum_n_keys(), Some(0));
 
         // UNIX timestamp of 10 seconds after the epoch.
-        let policy = StringPolicy::from_str("after(500000010)").unwrap();
-        assert_eq!(policy, Policy::after(500_000_010));
+        let policy = StringAbstract::from_str("after(500000010)").unwrap();
+        assert_eq!(policy, Abstract::after(500_000_010));
         assert_eq!(policy.absolute_timelocks(), vec![500_000_010]);
         assert_eq!(policy.relative_timelocks(), vec![]);
         // Pass a block height to at_lock_time while policy uses a UNIX timestapm.
-        assert_eq!(policy.clone().at_lock_time(absolute::LockTime::ZERO), Policy::Unsatisfiable);
+        assert_eq!(policy.clone().at_lock_time(absolute::LockTime::ZERO), Abstract::Unsatisfiable);
         assert_eq!(
             policy
                 .clone()
                 .at_lock_time(absolute::LockTime::from_height(999).expect("valid block height")),
-            Policy::Unsatisfiable
+            Abstract::Unsatisfiable
         );
         assert_eq!(
             policy
                 .clone()
                 .at_lock_time(absolute::LockTime::from_height(1000).expect("valid block height")),
-            Policy::Unsatisfiable
+            Abstract::Unsatisfiable
         );
         assert_eq!(
             policy
                 .clone()
                 .at_lock_time(absolute::LockTime::from_height(10000).expect("valid block height")),
-            Policy::Unsatisfiable
+            Abstract::Unsatisfiable
         );
         // And now pass a UNIX timestamp to at_lock_time while policy also uses a timestamp.
         assert_eq!(
             policy
                 .clone()
                 .at_lock_time(absolute::LockTime::from_time(500_000_000).expect("valid timestamp")),
-            Policy::Unsatisfiable
+            Abstract::Unsatisfiable
         );
         assert_eq!(
             policy
                 .clone()
                 .at_lock_time(absolute::LockTime::from_time(500_000_001).expect("valid timestamp")),
-            Policy::Unsatisfiable
+            Abstract::Unsatisfiable
         );
         assert_eq!(
             policy
@@ -879,25 +887,25 @@ mod tests {
     #[test]
     fn entailment_liquid_test() {
         //liquid policy
-        let liquid_pol = StringPolicy::from_str(
+        let liquid_pol = StringAbstract::from_str(
             "or(and(older(4096),thresh(2,pk(A),pk(B),pk(C))),thresh(11,pk(F1),pk(F2),pk(F3),pk(F4),pk(F5),pk(F6),pk(F7),pk(F8),pk(F9),pk(F10),pk(F11),pk(F12),pk(F13),pk(F14)))").unwrap();
         // Very bad idea to add master key,pk but let's have it have 50M blocks
-        let master_key = StringPolicy::from_str("and(older(50000000),pk(master))").unwrap();
-        let new_liquid_pol = Policy::Threshold(1, vec![liquid_pol.clone(), master_key]);
+        let master_key = StringAbstract::from_str("and(older(50000000),pk(master))").unwrap();
+        let new_liquid_pol = Abstract::Threshold(1, vec![liquid_pol.clone(), master_key]);
 
         assert!(liquid_pol.clone().entails(new_liquid_pol.clone()).unwrap());
         assert!(!new_liquid_pol.entails(liquid_pol.clone()).unwrap());
 
         // test liquid backup policy before the emergency timeout
-        let backup_policy = StringPolicy::from_str("thresh(2,pk(A),pk(B),pk(C))").unwrap();
+        let backup_policy = StringAbstract::from_str("thresh(2,pk(A),pk(B),pk(C))").unwrap();
         assert!(!backup_policy
             .entails(liquid_pol.clone().at_age(Sequence::from_height(4095)))
             .unwrap());
 
         // Finally test both spending paths
-        let fed_pol = StringPolicy::from_str("thresh(11,pk(F1),pk(F2),pk(F3),pk(F4),pk(F5),pk(F6),pk(F7),pk(F8),pk(F9),pk(F10),pk(F11),pk(F12),pk(F13),pk(F14))").unwrap();
+        let fed_pol = StringAbstract::from_str("thresh(11,pk(F1),pk(F2),pk(F3),pk(F4),pk(F5),pk(F6),pk(F7),pk(F8),pk(F9),pk(F10),pk(F11),pk(F12),pk(F13),pk(F14))").unwrap();
         let backup_policy_after_expiry =
-            StringPolicy::from_str("and(older(4096),thresh(2,pk(A),pk(B),pk(C)))").unwrap();
+            StringAbstract::from_str("and(older(4096),thresh(2,pk(A),pk(B),pk(C)))").unwrap();
         assert!(fed_pol.entails(liquid_pol.clone()).unwrap());
         assert!(backup_policy_after_expiry.entails(liquid_pol).unwrap());
     }
@@ -905,17 +913,18 @@ mod tests {
     #[test]
     fn entailment_escrow() {
         // Escrow contract
-        let escrow_pol = StringPolicy::from_str("thresh(2,pk(Alice),pk(Bob),pk(Judge))").unwrap();
+        let escrow_pol = StringAbstract::from_str("thresh(2,pk(Alice),pk(Bob),pk(Judge))").unwrap();
         // Alice's authorization constraint
         // Authorization is a constraint that states the conditions under which one party must
         // be able to redeem the funds.
-        let auth_alice = StringPolicy::from_str("and(pk(Alice),pk(Judge))").unwrap();
+        let auth_alice = StringAbstract::from_str("and(pk(Alice),pk(Judge))").unwrap();
 
         //Alice's Control constraint
         // The control constraint states the conditions that one party requires
         // must be met if the funds are spent by anyone
         // Either Alice must authorize the funds or both Judge and Bob must control it
-        let control_alice = StringPolicy::from_str("or(pk(Alice),and(pk(Judge),pk(Bob)))").unwrap();
+        let control_alice =
+            StringAbstract::from_str("or(pk(Alice),and(pk(Judge),pk(Bob)))").unwrap();
 
         // Entailment rules
         // Authorization entails |- policy |- control constraints
@@ -925,7 +934,7 @@ mod tests {
         // Entailment HTLC's
         // Escrow contract
         let h = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
-        let htlc_pol = StringPolicy::from_str(&format!(
+        let htlc_pol = StringAbstract::from_str(&format!(
             "or(and(pk(Alice),older(100)),and(pk(Bob),sha256({})))",
             h
         ))
@@ -934,14 +943,14 @@ mod tests {
         // Authorization is a constraint that states the conditions under which one party must
         // be able to redeem the funds. In HLTC, alice only cares that she can
         // authorize her funds with Pk and CSV 100.
-        let auth_alice = StringPolicy::from_str("and(pk(Alice),older(100))").unwrap();
+        let auth_alice = StringAbstract::from_str("and(pk(Alice),older(100))").unwrap();
 
         //Alice's Control constraint
         // The control constraint states the conditions that one party requires
         // must be met if the funds are spent by anyone
         // Either Alice must authorize the funds or sha2 preimage must be revealed.
         let control_alice =
-            StringPolicy::from_str(&format!("or(pk(Alice),sha256({}))", h)).unwrap();
+            StringAbstract::from_str(&format!("or(pk(Alice),sha256({}))", h)).unwrap();
 
         // Entailment rules
         // Authorization entails |- policy |- control constraints
@@ -951,7 +960,7 @@ mod tests {
 
     #[test]
     fn for_each_key() {
-        let liquid_pol = StringPolicy::from_str(
+        let liquid_pol = StringAbstract::from_str(
             "or(and(older(4096),thresh(2,pk(A),pk(B),pk(C))),thresh(11,pk(F1),pk(F2),pk(F3),pk(F4),pk(F5),pk(F6),pk(F7),pk(F8),pk(F9),pk(F10),pk(F11),pk(F12),pk(F13),pk(F14)))").unwrap();
         let mut count = 0;
         assert!(liquid_pol.for_each_key(|_| {
diff --git a/src/descriptor/bare.rs b/src/descriptor/bare.rs
index c4ca14b14..b20b6205f 100644
--- a/src/descriptor/bare.rs
+++ b/src/descriptor/bare.rs
@@ -18,8 +18,8 @@ use crate::expression::{self, FromTree};
 use crate::miniscript::context::{ScriptContext, ScriptContextError};
 use crate::miniscript::satisfy::{Placeholder, Satisfaction, Witness};
 use crate::plan::AssetProvider;
-use crate::policy::{semantic, Liftable};
 use crate::prelude::*;
+use crate::r#abstract::{Abstract, Liftable};
 use crate::util::{varint_len, witness_to_scriptsig};
 use crate::{
     BareCtx, Error, ForEachKey, Miniscript, MiniscriptKey, Satisfier, ToPublicKey, TranslateErr,
@@ -165,7 +165,7 @@ impl<Pk: MiniscriptKey> fmt::Display for Bare<Pk> {
 }
 
 impl<Pk: MiniscriptKey> Liftable<Pk> for Bare<Pk> {
-    fn lift(&self) -> Result<semantic::Policy<Pk>, Error> { self.ms.lift() }
+    fn lift(&self) -> Result<Abstract<Pk>, Error> { self.ms.lift() }
 }
 
 impl_from_tree!(
@@ -362,9 +362,7 @@ impl<Pk: MiniscriptKey> fmt::Display for Pkh<Pk> {
 }
 
 impl<Pk: MiniscriptKey> Liftable<Pk> for Pkh<Pk> {
-    fn lift(&self) -> Result<semantic::Policy<Pk>, Error> {
-        Ok(semantic::Policy::Key(self.pk.clone()))
-    }
+    fn lift(&self) -> Result<Abstract<Pk>, Error> { Ok(Abstract::Key(self.pk.clone())) }
 }
 
 impl_from_tree!(
diff --git a/src/descriptor/key.rs b/src/descriptor/key.rs
index cd4c828e8..0fff619fb 100644
--- a/src/descriptor/key.rs
+++ b/src/descriptor/key.rs
@@ -1153,11 +1153,7 @@ mod test {
     #[cfg(feature = "serde")]
     use serde_test::{assert_tokens, Token};
 
-    use super::{
-        DescriptorKeyParseError, DescriptorMultiXKey, DescriptorPublicKey, DescriptorSecretKey,
-        MiniscriptKey, Wildcard,
-    };
-    use crate::prelude::*;
+    use super::*;
 
     #[test]
     fn parse_descriptor_key_errors() {
diff --git a/src/descriptor/segwitv0.rs b/src/descriptor/segwitv0.rs
index 0cc665468..24f99f4c9 100644
--- a/src/descriptor/segwitv0.rs
+++ b/src/descriptor/segwitv0.rs
@@ -16,8 +16,8 @@ use crate::expression::{self, FromTree};
 use crate::miniscript::context::{ScriptContext, ScriptContextError};
 use crate::miniscript::satisfy::{Placeholder, Satisfaction, Witness};
 use crate::plan::AssetProvider;
-use crate::policy::{semantic, Liftable};
 use crate::prelude::*;
+use crate::r#abstract::{Abstract, Liftable};
 use crate::util::varint_len;
 use crate::{
     Error, ForEachKey, Miniscript, MiniscriptKey, Satisfier, Segwitv0, ToPublicKey, TranslateErr,
@@ -220,7 +220,7 @@ pub enum WshInner<Pk: MiniscriptKey> {
 }
 
 impl<Pk: MiniscriptKey> Liftable<Pk> for Wsh<Pk> {
-    fn lift(&self) -> Result<semantic::Policy<Pk>, Error> {
+    fn lift(&self) -> Result<Abstract<Pk>, Error> {
         match self.inner {
             WshInner::SortedMulti(ref smv) => smv.lift(),
             WshInner::Ms(ref ms) => ms.lift(),
@@ -469,9 +469,7 @@ impl<Pk: MiniscriptKey> fmt::Display for Wpkh<Pk> {
 }
 
 impl<Pk: MiniscriptKey> Liftable<Pk> for Wpkh<Pk> {
-    fn lift(&self) -> Result<semantic::Policy<Pk>, Error> {
-        Ok(semantic::Policy::Key(self.pk.clone()))
-    }
+    fn lift(&self) -> Result<Abstract<Pk>, Error> { Ok(Abstract::Key(self.pk.clone())) }
 }
 
 impl_from_tree!(
diff --git a/src/descriptor/sh.rs b/src/descriptor/sh.rs
index 53b4107ce..6496a96da 100644
--- a/src/descriptor/sh.rs
+++ b/src/descriptor/sh.rs
@@ -20,8 +20,8 @@ use crate::expression::{self, FromTree};
 use crate::miniscript::context::ScriptContext;
 use crate::miniscript::satisfy::{Placeholder, Satisfaction};
 use crate::plan::AssetProvider;
-use crate::policy::{semantic, Liftable};
 use crate::prelude::*;
+use crate::r#abstract::{Abstract, Liftable};
 use crate::util::{varint_len, witness_to_scriptsig};
 use crate::{
     push_opcode_size, Error, ForEachKey, Legacy, Miniscript, MiniscriptKey, Satisfier, Segwitv0,
@@ -49,10 +49,10 @@ pub enum ShInner<Pk: MiniscriptKey> {
 }
 
 impl<Pk: MiniscriptKey> Liftable<Pk> for Sh<Pk> {
-    fn lift(&self) -> Result<semantic::Policy<Pk>, Error> {
+    fn lift(&self) -> Result<Abstract<Pk>, Error> {
         match self.inner {
             ShInner::Wsh(ref wsh) => wsh.lift(),
-            ShInner::Wpkh(ref pk) => Ok(semantic::Policy::Key(pk.as_inner().clone())),
+            ShInner::Wpkh(ref pk) => Ok(Abstract::Key(pk.as_inner().clone())),
             ShInner::SortedMulti(ref smv) => smv.lift(),
             ShInner::Ms(ref ms) => ms.lift(),
         }
diff --git a/src/descriptor/sortedmulti.rs b/src/descriptor/sortedmulti.rs
index 3bd5fa8e0..993837ca4 100644
--- a/src/descriptor/sortedmulti.rs
+++ b/src/descriptor/sortedmulti.rs
@@ -17,9 +17,10 @@ use crate::miniscript::limits::MAX_PUBKEYS_PER_MULTISIG;
 use crate::miniscript::satisfy::{Placeholder, Satisfaction};
 use crate::plan::AssetProvider;
 use crate::prelude::*;
+use crate::r#abstract::{Abstract, Liftable};
 use crate::{
-    errstr, expression, policy, script_num_size, Error, ForEachKey, Miniscript, MiniscriptKey,
-    Satisfier, ToPublicKey, TranslateErr, Translator,
+    errstr, expression, script_num_size, Error, ForEachKey, Miniscript, MiniscriptKey, Satisfier,
+    ToPublicKey, TranslateErr, Translator,
 };
 
 /// Contents of a "sortedmulti" descriptor
@@ -195,14 +196,11 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> SortedMultiVec<Pk, Ctx> {
     pub fn max_satisfaction_size(&self) -> usize { 1 + 73 * self.k }
 }
 
-impl<Pk: MiniscriptKey, Ctx: ScriptContext> policy::Liftable<Pk> for SortedMultiVec<Pk, Ctx> {
-    fn lift(&self) -> Result<policy::semantic::Policy<Pk>, Error> {
-        let ret = policy::semantic::Policy::Threshold(
+impl<Pk: MiniscriptKey, Ctx: ScriptContext> Liftable<Pk> for SortedMultiVec<Pk, Ctx> {
+    fn lift(&self) -> Result<Abstract<Pk>, Error> {
+        let ret = Abstract::Threshold(
             self.k,
-            self.pks
-                .iter()
-                .map(|k| policy::semantic::Policy::Key(k.clone()))
-                .collect(),
+            self.pks.iter().map(|k| Abstract::Key(k.clone())).collect(),
         );
         Ok(ret)
     }
diff --git a/src/descriptor/tr.rs b/src/descriptor/tr.rs
index 48deb63e0..ed94fdec0 100644
--- a/src/descriptor/tr.rs
+++ b/src/descriptor/tr.rs
@@ -16,9 +16,8 @@ use crate::expression::{self, FromTree};
 use crate::miniscript::satisfy::{Placeholder, Satisfaction, SchnorrSigType, Witness};
 use crate::miniscript::Miniscript;
 use crate::plan::AssetProvider;
-use crate::policy::semantic::Policy;
-use crate::policy::Liftable;
 use crate::prelude::*;
+use crate::r#abstract::{Abstract, Liftable};
 use crate::util::{varint_len, witness_size};
 use crate::{
     errstr, Error, ForEachKey, MiniscriptKey, Satisfier, ScriptContext, Tap, ToPublicKey,
@@ -617,11 +616,11 @@ fn split_once(inp: &str, delim: char) -> Option<(&str, &str)> {
 }
 
 impl<Pk: MiniscriptKey> Liftable<Pk> for TapTree<Pk> {
-    fn lift(&self) -> Result<Policy<Pk>, Error> {
-        fn lift_helper<Pk: MiniscriptKey>(s: &TapTree<Pk>) -> Result<Policy<Pk>, Error> {
+    fn lift(&self) -> Result<Abstract<Pk>, Error> {
+        fn lift_helper<Pk: MiniscriptKey>(s: &TapTree<Pk>) -> Result<Abstract<Pk>, Error> {
             match *s {
                 TapTree::Tree { ref left, ref right, height: _ } => {
-                    Ok(Policy::Threshold(1, vec![lift_helper(left)?, lift_helper(right)?]))
+                    Ok(Abstract::Threshold(1, vec![lift_helper(left)?, lift_helper(right)?]))
                 }
                 TapTree::Leaf(ref leaf) => leaf.lift(),
             }
@@ -633,12 +632,13 @@ impl<Pk: MiniscriptKey> Liftable<Pk> for TapTree<Pk> {
 }
 
 impl<Pk: MiniscriptKey> Liftable<Pk> for Tr<Pk> {
-    fn lift(&self) -> Result<Policy<Pk>, Error> {
+    fn lift(&self) -> Result<Abstract<Pk>, Error> {
         match &self.tree {
-            Some(root) => {
-                Ok(Policy::Threshold(1, vec![Policy::Key(self.internal_key.clone()), root.lift()?]))
-            }
-            None => Ok(Policy::Key(self.internal_key.clone())),
+            Some(root) => Ok(Abstract::Threshold(
+                1,
+                vec![Abstract::Key(self.internal_key.clone()), root.lift()?],
+            )),
+            None => Ok(Abstract::Key(self.internal_key.clone())),
         }
     }
 }
diff --git a/src/expression.rs b/src/expression.rs
index 7875284de..c452ec55f 100644
--- a/src/expression.rs
+++ b/src/expression.rs
@@ -260,7 +260,7 @@ where
 #[cfg(test)]
 mod tests {
 
-    use super::parse_num;
+    use super::*;
 
     #[test]
     fn test_parse_num() {
diff --git a/src/iter/mod.rs b/src/iter/mod.rs
index f8dbed939..a86a44c0f 100644
--- a/src/iter/mod.rs
+++ b/src/iter/mod.rs
@@ -69,9 +69,9 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> TreeLike for Arc<Miniscript<Pk, Ctx>
     }
 }
 
-impl<'a, Pk: MiniscriptKey> TreeLike for &'a policy::Concrete<Pk> {
+impl<'a, Pk: MiniscriptKey> TreeLike for &'a policy::concrete::Policy<Pk> {
     fn as_node(&self) -> Tree<Self> {
-        use policy::Concrete::*;
+        use policy::concrete::Policy::*;
         match *self {
             Unsatisfiable | Trivial | Key(_) | After(_) | Older(_) | Sha256(_) | Hash256(_)
             | Ripemd160(_) | Hash160(_) => Tree::Nullary,
@@ -82,9 +82,9 @@ impl<'a, Pk: MiniscriptKey> TreeLike for &'a policy::Concrete<Pk> {
     }
 }
 
-impl<'a, Pk: MiniscriptKey> TreeLike for Arc<policy::Concrete<Pk>> {
+impl<'a, Pk: MiniscriptKey> TreeLike for Arc<policy::concrete::Policy<Pk>> {
     fn as_node(&self) -> Tree<Self> {
-        use policy::Concrete::*;
+        use policy::concrete::Policy::*;
         match self.as_ref() {
             Unsatisfiable | Trivial | Key(_) | After(_) | Older(_) | Sha256(_) | Hash256(_)
             | Ripemd160(_) | Hash160(_) => Tree::Nullary,
diff --git a/src/lib.rs b/src/lib.rs
index 7b798915e..e9ac52d71 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -118,6 +118,7 @@ mod pub_macros;
 use internals::hex::exts::DisplayHex;
 pub use pub_macros::*;
 
+pub mod r#abstract;
 pub mod descriptor;
 pub mod expression;
 pub mod interpreter;
@@ -474,7 +475,7 @@ pub enum Error {
     /// Errors related to policy
     PolicyError(policy::concrete::PolicyError),
     /// Errors related to lifting
-    LiftError(policy::LiftError),
+    LiftError(crate::r#abstract::LiftError),
     /// Forward script context related errors
     ContextError(miniscript::context::ScriptContextError),
     /// Recursion depth exceeded when parsing policy/miniscript from string
@@ -644,8 +645,8 @@ where
 }
 
 #[doc(hidden)]
-impl From<policy::LiftError> for Error {
-    fn from(e: policy::LiftError) -> Error { Error::LiftError(e) }
+impl From<crate::r#abstract::LiftError> for Error {
+    fn from(e: crate::r#abstract::LiftError) -> Error { Error::LiftError(e) }
 }
 
 #[doc(hidden)]
diff --git a/src/macros.rs b/src/macros.rs
index 6d4b9156b..5658d8a27 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -15,7 +15,7 @@ macro_rules! ms_str {
 /// `policy_str!("wsh(c:or_i(pk({}),pk({})))", pk1, pk2)`
 #[cfg(all(feature = "compiler", test))]
 macro_rules! policy_str {
-    ($($arg:tt)*) => ($crate::policy::Concrete::from_str(&format!($($arg)*)).unwrap())
+    ($($arg:tt)*) => ($crate::policy::concrete::Policy::from_str(&format!($($arg)*)).unwrap())
 }
 
 /// Macro for implementing FromTree trait. This avoids copying all the Pk::Associated type bounds
diff --git a/src/miniscript/mod.rs b/src/miniscript/mod.rs
index 28349e427..8ed29f6c7 100644
--- a/src/miniscript/mod.rs
+++ b/src/miniscript/mod.rs
@@ -606,11 +606,10 @@ mod tests {
     use bitcoin::{self, secp256k1, Sequence};
     use sync::Arc;
 
-    use super::{Miniscript, ScriptContext, Segwitv0, Tap};
+    use super::*;
     use crate::miniscript::types::{self, ExtData, Property, Type};
     use crate::miniscript::Terminal;
-    use crate::policy::Liftable;
-    use crate::prelude::*;
+    use crate::r#abstract::Liftable;
     use crate::test_utils::{StrKeyTranslator, StrXOnlyKeyTranslator};
     use crate::{hex_script, ExtParams, Satisfier, ToPublicKey, TranslatePk};
 
diff --git a/src/policy/compiler.rs b/src/policy/compiler.rs
index 66284f046..d11857a87 100644
--- a/src/policy/compiler.rs
+++ b/src/policy/compiler.rs
@@ -16,7 +16,7 @@ use crate::miniscript::context::SigType;
 use crate::miniscript::limits::MAX_PUBKEYS_PER_MULTISIG;
 use crate::miniscript::types::{self, ErrorKind, ExtData, Property, Type};
 use crate::miniscript::ScriptContext;
-use crate::policy::Concrete;
+use crate::policy::concrete::Policy as Concrete;
 use crate::prelude::*;
 use crate::{policy, Miniscript, MiniscriptKey, Terminal};
 
@@ -1156,7 +1156,7 @@ mod tests {
 
     use super::*;
     use crate::miniscript::{Legacy, Segwitv0, Tap};
-    use crate::policy::Liftable;
+    use crate::r#abstract::Liftable;
     use crate::{script_num_size, ToPublicKey};
 
     type SPolicy = Concrete<String>;
diff --git a/src/policy/concrete.rs b/src/policy/concrete.rs
index 23305087d..1fa800141 100644
--- a/src/policy/concrete.rs
+++ b/src/policy/concrete.rs
@@ -10,14 +10,10 @@ use std::error;
 use bitcoin::{absolute, Sequence};
 #[cfg(feature = "compiler")]
 use {
-    crate::descriptor::TapTree,
-    crate::miniscript::ScriptContext,
-    crate::policy::compiler::CompilerError,
-    crate::policy::compiler::OrdF64,
-    crate::policy::{compiler, Concrete, Liftable, Semantic},
-    crate::Descriptor,
-    crate::Miniscript,
-    crate::Tap,
+    crate::descriptor::TapTree, crate::miniscript::ScriptContext, crate::policy::compiler,
+    crate::policy::compiler::CompilerError, crate::policy::compiler::OrdF64,
+    crate::policy::concrete::Policy as Concrete, crate::r#abstract::Abstract,
+    crate::r#abstract::Liftable, crate::Descriptor, crate::Miniscript, crate::Tap,
     core::cmp::Reverse,
 };
 
@@ -98,9 +94,9 @@ pub enum PolicyError {
     ZeroTime,
     /// `after` fragment can only have `n < 2^31`.
     TimeTooFar,
-    /// Semantic Policy Error: `And` `Or` fragments must take args: `k > 1`.
+    /// Abstract Policy Error: `And` `Or` fragments must take args: `k > 1`.
     InsufficientArgsforAnd,
-    /// Semantic policy error: `And` `Or` fragments must take args: `k > 1`.
+    /// Abstract policy error: `And` `Or` fragments must take args: `k > 1`.
     InsufficientArgsforOr,
     /// Entailment max terminals exceeded.
     EntailmentMaxTerminals,
@@ -140,10 +136,10 @@ impl fmt::Display for PolicyError {
             }
             PolicyError::ZeroTime => f.write_str("Time must be greater than 0; n > 0"),
             PolicyError::InsufficientArgsforAnd => {
-                f.write_str("Semantic Policy 'And' fragment must have at least 2 args ")
+                f.write_str("Abstract Policy 'And' fragment must have at least 2 args ")
             }
             PolicyError::InsufficientArgsforOr => {
-                f.write_str("Semantic Policy 'Or' fragment must have at least 2 args ")
+                f.write_str("Abstract Policy 'Or' fragment must have at least 2 args ")
             }
             PolicyError::EntailmentMaxTerminals => {
                 write!(f, "Policy entailment only supports {} terminals", ENTAILMENT_MAX_TERMINALS)
@@ -238,8 +234,8 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
             for key in concrete_keys.into_iter() {
                 if semantic_policy
                     .clone()
-                    .satisfy_constraint(&Semantic::Key(key.clone()), true)
-                    == Semantic::Trivial
+                    .satisfy_constraint(&Abstract::Key(key.clone()), true)
+                    == Abstract::Trivial
                 {
                     match key_prob_map.get(&Concrete::Key(key.clone())) {
                         Some(val) => {
@@ -557,7 +553,7 @@ impl<Pk: MiniscriptKey> ForEachKey<Pk> for Policy<Pk> {
 impl<Pk: MiniscriptKey> Policy<Pk> {
     /// Converts a policy using one kind of public key to another type of public key.
     ///
-    /// For example usage please see [`crate::policy::semantic::Policy::translate_pk`].
+    /// For example usage please see [`crate::abstract::Abstract::translate_pk`].
     pub fn translate_pk<Q, E, T>(&self, t: &mut T) -> Result<Policy<Q>, E>
     where
         T: Translator<Pk, Q, E>,
diff --git a/src/policy/mod.rs b/src/policy/mod.rs
index 138ec45b7..4583addb6 100644
--- a/src/policy/mod.rs
+++ b/src/policy/mod.rs
@@ -9,553 +9,10 @@
 //! The format represents EC public keys abstractly to allow wallets to replace
 //! these with BIP32 paths, pay-to-contract instructions, etc.
 //!
-use core::fmt;
-#[cfg(feature = "std")]
-use std::error;
 
 #[cfg(feature = "compiler")]
 pub mod compiler;
 pub mod concrete;
-pub mod semantic;
-
-pub use self::concrete::Policy as Concrete;
-pub use self::semantic::Policy as Semantic;
-use crate::descriptor::Descriptor;
-use crate::miniscript::{Miniscript, ScriptContext};
-use crate::sync::Arc;
-use crate::{Error, MiniscriptKey, Terminal};
 
 /// Policy entailment algorithm maximum number of terminals allowed.
-const ENTAILMENT_MAX_TERMINALS: usize = 20;
-
-/// Trait describing script representations which can be lifted into
-/// an abstract policy, by discarding information.
-///
-/// After Lifting all policies are converted into `KeyHash(Pk::HasH)` to
-/// maintain the following invariant(modulo resource limits):
-/// `Lift(Concrete) == Concrete -> Miniscript -> Script -> Miniscript -> Semantic`
-///
-/// Lifting from [`Miniscript`] or [`Descriptor`] can fail if the miniscript
-/// contains a timelock combination or if it contains a branch that exceeds
-/// resource limits.
-///
-/// Lifting from concrete policies can fail if the policy contains a timelock
-/// combination. It is possible that a concrete policy has some branches that
-/// exceed resource limits for any compilation but cannot detect such policies
-/// while lifting. Note that our compiler would not succeed for any such
-/// policies.
-pub trait Liftable<Pk: MiniscriptKey> {
-    /// Converts this object into an abstract policy.
-    fn lift(&self) -> Result<Semantic<Pk>, Error>;
-}
-
-/// Error occurring during lifting.
-#[derive(Copy, Clone, PartialEq, Eq, Debug)]
-pub enum LiftError {
-    /// Cannot lift policies that have a combination of height and timelocks.
-    HeightTimelockCombination,
-    /// Duplicate public keys.
-    BranchExceedResourceLimits,
-    /// Cannot lift raw descriptors.
-    RawDescriptorLift,
-}
-
-impl fmt::Display for LiftError {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        match *self {
-            LiftError::HeightTimelockCombination => {
-                f.write_str("Cannot lift policies that have a heightlock and timelock combination")
-            }
-            LiftError::BranchExceedResourceLimits => f.write_str(
-                "Cannot lift policies containing one branch that exceeds resource limits",
-            ),
-            LiftError::RawDescriptorLift => f.write_str("Cannot lift raw descriptors"),
-        }
-    }
-}
-
-#[cfg(feature = "std")]
-impl error::Error for LiftError {
-    fn cause(&self) -> Option<&dyn error::Error> {
-        use self::LiftError::*;
-
-        match self {
-            HeightTimelockCombination | BranchExceedResourceLimits | RawDescriptorLift => None,
-        }
-    }
-}
-
-impl<Pk: MiniscriptKey, Ctx: ScriptContext> Miniscript<Pk, Ctx> {
-    /// Lifting corresponds to conversion of a miniscript into a [`Semantic`]
-    /// policy for human readable or machine analysis. However, naively lifting
-    /// miniscripts can result in incorrect interpretations that don't
-    /// correspond to the underlying semantics when we try to spend them on
-    /// bitcoin network. This can occur if the miniscript contains:
-    /// 1. A combination of timelocks
-    /// 2. A spend that exceeds resource limits
-    pub fn lift_check(&self) -> Result<(), LiftError> {
-        if !self.within_resource_limits() {
-            Err(LiftError::BranchExceedResourceLimits)
-        } else if self.has_mixed_timelocks() {
-            Err(LiftError::HeightTimelockCombination)
-        } else {
-            Ok(())
-        }
-    }
-}
-
-impl<Pk: MiniscriptKey, Ctx: ScriptContext> Liftable<Pk> for Miniscript<Pk, Ctx> {
-    fn lift(&self) -> Result<Semantic<Pk>, Error> {
-        // check whether the root miniscript can have a spending path that is
-        // a combination of heightlock and timelock
-        self.lift_check()?;
-        self.as_inner().lift()
-    }
-}
-
-impl<Pk: MiniscriptKey, Ctx: ScriptContext> Liftable<Pk> for Terminal<Pk, Ctx> {
-    fn lift(&self) -> Result<Semantic<Pk>, Error> {
-        let ret = match *self {
-            Terminal::PkK(ref pk) | Terminal::PkH(ref pk) => Semantic::Key(pk.clone()),
-            Terminal::RawPkH(ref _pkh) => {
-                return Err(Error::LiftError(LiftError::RawDescriptorLift))
-            }
-            Terminal::After(t) => Semantic::After(t),
-            Terminal::Older(t) => Semantic::Older(t),
-            Terminal::Sha256(ref h) => Semantic::Sha256(h.clone()),
-            Terminal::Hash256(ref h) => Semantic::Hash256(h.clone()),
-            Terminal::Ripemd160(ref h) => Semantic::Ripemd160(h.clone()),
-            Terminal::Hash160(ref h) => Semantic::Hash160(h.clone()),
-            Terminal::False => Semantic::Unsatisfiable,
-            Terminal::True => Semantic::Trivial,
-            Terminal::Alt(ref sub)
-            | Terminal::Swap(ref sub)
-            | Terminal::Check(ref sub)
-            | Terminal::DupIf(ref sub)
-            | Terminal::Verify(ref sub)
-            | Terminal::NonZero(ref sub)
-            | Terminal::ZeroNotEqual(ref sub) => sub.node.lift()?,
-            Terminal::AndV(ref left, ref right) | Terminal::AndB(ref left, ref right) => {
-                Semantic::Threshold(2, vec![left.node.lift()?, right.node.lift()?])
-            }
-            Terminal::AndOr(ref a, ref b, ref c) => Semantic::Threshold(
-                1,
-                vec![
-                    Semantic::Threshold(2, vec![a.node.lift()?, b.node.lift()?]),
-                    c.node.lift()?,
-                ],
-            ),
-            Terminal::OrB(ref left, ref right)
-            | Terminal::OrD(ref left, ref right)
-            | Terminal::OrC(ref left, ref right)
-            | Terminal::OrI(ref left, ref right) => {
-                Semantic::Threshold(1, vec![left.node.lift()?, right.node.lift()?])
-            }
-            Terminal::Thresh(k, ref subs) => {
-                let semantic_subs: Result<_, Error> = subs.iter().map(|s| s.node.lift()).collect();
-                Semantic::Threshold(k, semantic_subs?)
-            }
-            Terminal::Multi(k, ref keys) | Terminal::MultiA(k, ref keys) => {
-                Semantic::Threshold(k, keys.iter().map(|k| Semantic::Key(k.clone())).collect())
-            }
-        }
-        .normalized();
-        Ok(ret)
-    }
-}
-
-impl<Pk: MiniscriptKey> Liftable<Pk> for Descriptor<Pk> {
-    fn lift(&self) -> Result<Semantic<Pk>, Error> {
-        match *self {
-            Descriptor::Bare(ref bare) => bare.lift(),
-            Descriptor::Pkh(ref pkh) => pkh.lift(),
-            Descriptor::Wpkh(ref wpkh) => wpkh.lift(),
-            Descriptor::Wsh(ref wsh) => wsh.lift(),
-            Descriptor::Sh(ref sh) => sh.lift(),
-            Descriptor::Tr(ref tr) => tr.lift(),
-        }
-    }
-}
-
-impl<Pk: MiniscriptKey> Liftable<Pk> for Semantic<Pk> {
-    fn lift(&self) -> Result<Semantic<Pk>, Error> { Ok(self.clone()) }
-}
-
-impl<Pk: MiniscriptKey> Liftable<Pk> for Concrete<Pk> {
-    fn lift(&self) -> Result<Semantic<Pk>, Error> {
-        // do not lift if there is a possible satisfaction
-        // involving combination of timelocks and heightlocks
-        self.check_timelocks()?;
-        let ret = match *self {
-            Concrete::Unsatisfiable => Semantic::Unsatisfiable,
-            Concrete::Trivial => Semantic::Trivial,
-            Concrete::Key(ref pk) => Semantic::Key(pk.clone()),
-            Concrete::After(t) => Semantic::After(t),
-            Concrete::Older(t) => Semantic::Older(t),
-            Concrete::Sha256(ref h) => Semantic::Sha256(h.clone()),
-            Concrete::Hash256(ref h) => Semantic::Hash256(h.clone()),
-            Concrete::Ripemd160(ref h) => Semantic::Ripemd160(h.clone()),
-            Concrete::Hash160(ref h) => Semantic::Hash160(h.clone()),
-            Concrete::And(ref subs) => {
-                let semantic_subs: Result<_, Error> = subs.iter().map(Liftable::lift).collect();
-                Semantic::Threshold(2, semantic_subs?)
-            }
-            Concrete::Or(ref subs) => {
-                let semantic_subs: Result<_, Error> =
-                    subs.iter().map(|(_p, sub)| sub.lift()).collect();
-                Semantic::Threshold(1, semantic_subs?)
-            }
-            Concrete::Threshold(k, ref subs) => {
-                let semantic_subs: Result<_, Error> = subs.iter().map(Liftable::lift).collect();
-                Semantic::Threshold(k, semantic_subs?)
-            }
-        }
-        .normalized();
-        Ok(ret)
-    }
-}
-impl<Pk: MiniscriptKey> Liftable<Pk> for Arc<Concrete<Pk>> {
-    fn lift(&self) -> Result<Semantic<Pk>, Error> { self.as_ref().lift() }
-}
-
-#[cfg(test)]
-mod tests {
-    use core::str::FromStr;
-
-    use bitcoin::Sequence;
-    #[cfg(feature = "compiler")]
-    use sync::Arc;
-
-    use super::super::miniscript::context::Segwitv0;
-    use super::super::miniscript::Miniscript;
-    use super::{Concrete, Liftable, Semantic};
-    #[cfg(feature = "compiler")]
-    use crate::descriptor::Tr;
-    use crate::prelude::*;
-    #[cfg(feature = "compiler")]
-    use crate::{descriptor::TapTree, Descriptor, Tap};
-
-    type ConcretePol = Concrete<String>;
-    type SemanticPol = Semantic<String>;
-
-    fn concrete_policy_rtt(s: &str) {
-        let conc = ConcretePol::from_str(s).unwrap();
-        let output = conc.to_string();
-        assert_eq!(s.to_lowercase(), output.to_lowercase());
-    }
-
-    fn semantic_policy_rtt(s: &str) {
-        let sem = SemanticPol::from_str(s).unwrap();
-        let output = sem.normalized().to_string();
-        assert_eq!(s.to_lowercase(), output.to_lowercase());
-    }
-
-    #[test]
-    fn test_timelock_validity() {
-        // only height
-        assert!(ConcretePol::from_str("after(100)").is_ok());
-        // only time
-        assert!(ConcretePol::from_str("after(1000000000)").is_ok());
-        // disjunction
-        assert!(ConcretePol::from_str("or(after(1000000000),after(100))").is_ok());
-        // conjunction
-        assert!(ConcretePol::from_str("and(after(1000000000),after(100))").is_err());
-        // thresh with k = 1
-        assert!(ConcretePol::from_str("thresh(1,pk(),after(1000000000),after(100))").is_ok());
-        // thresh with k = 2
-        assert!(ConcretePol::from_str("thresh(2,after(1000000000),after(100),pk())").is_err());
-    }
-    #[test]
-    fn policy_rtt_tests() {
-        concrete_policy_rtt("pk()");
-        concrete_policy_rtt("or(1@pk(),1@pk())");
-        concrete_policy_rtt("or(99@pk(),1@pk())");
-        concrete_policy_rtt("and(pk(),or(99@pk(),1@older(12960)))");
-
-        semantic_policy_rtt("pk()");
-        semantic_policy_rtt("or(pk(),pk())");
-        semantic_policy_rtt("and(pk(),pk())");
-
-        //fuzzer crashes
-        assert!(ConcretePol::from_str("thresh()").is_err());
-        assert!(SemanticPol::from_str("thresh(0)").is_err());
-        assert!(SemanticPol::from_str("thresh()").is_err());
-        concrete_policy_rtt("ripemd160()");
-    }
-
-    #[test]
-    fn compile_invalid() {
-        // Since the root Error does not support Eq type, we have to
-        // compare the string representations of the error
-        assert_eq!(
-            ConcretePol::from_str("thresh(2,pk(),thresh(0))")
-                .unwrap_err()
-                .to_string(),
-            "Threshold k must be greater than 0 and less than or equal to n 0<k<=n"
-        );
-        assert_eq!(
-            ConcretePol::from_str("thresh(2,pk(),thresh(0,pk()))")
-                .unwrap_err()
-                .to_string(),
-            "Threshold k must be greater than 0 and less than or equal to n 0<k<=n"
-        );
-        assert_eq!(
-            ConcretePol::from_str("and(pk())").unwrap_err().to_string(),
-            "And policy fragment must take 2 arguments"
-        );
-        assert_eq!(
-            ConcretePol::from_str("or(pk())").unwrap_err().to_string(),
-            "Or policy fragment must take 2 arguments"
-        );
-        assert_eq!(
-            ConcretePol::from_str("thresh(3,after(0),pk(),pk())")
-                .unwrap_err()
-                .to_string(),
-            "Time must be greater than 0; n > 0"
-        );
-
-        assert_eq!(
-            ConcretePol::from_str("thresh(2,older(2147483650),pk(),pk())")
-                .unwrap_err()
-                .to_string(),
-            "Relative/Absolute time must be less than 2^31; n < 2^31"
-        );
-    }
-
-    //https://github.com/apoelstra/rust-miniscript/issues/41
-    #[test]
-    fn heavy_nest() {
-        let policy_string = "thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk()))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))";
-        ConcretePol::from_str(policy_string).unwrap_err();
-    }
-
-    #[test]
-    fn lift_andor() {
-        let key_a: bitcoin::PublicKey =
-            "02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e"
-                .parse()
-                .unwrap();
-        let key_b: bitcoin::PublicKey =
-            "03b506a1dbe57b4bf48c95e0c7d417b87dd3b4349d290d2e7e9ba72c912652d80a"
-                .parse()
-                .unwrap();
-
-        let ms_str: Miniscript<bitcoin::PublicKey, Segwitv0> =
-            format!("andor(multi(1,{}),older(42),c:pk_k({}))", key_a.inner, key_b.inner)
-                .parse()
-                .unwrap();
-        assert_eq!(
-            Semantic::Threshold(
-                1,
-                vec![
-                    Semantic::Threshold(
-                        2,
-                        vec![
-                            Semantic::Key(key_a),
-                            Semantic::Older(Sequence::from_height(42))
-                        ]
-                    ),
-                    Semantic::Key(key_b)
-                ]
-            ),
-            ms_str.lift().unwrap()
-        );
-    }
-
-    #[test]
-    #[cfg(feature = "compiler")]
-    fn taproot_compile() {
-        // Trivial single-node compilation
-        let unspendable_key: String = "UNSPENDABLE".to_string();
-        {
-            let policy: Concrete<String> = policy_str!("thresh(2,pk(A),pk(B),pk(C),pk(D))");
-            let descriptor = policy.compile_tr(Some(unspendable_key.clone())).unwrap();
-
-            let ms_compilation: Miniscript<String, Tap> = ms_str!("multi_a(2,A,B,C,D)");
-            let tree: TapTree<String> = TapTree::Leaf(Arc::new(ms_compilation));
-            let expected_descriptor =
-                Descriptor::new_tr(unspendable_key.clone(), Some(tree)).unwrap();
-            assert_eq!(descriptor, expected_descriptor);
-        }
-
-        // Trivial multi-node compilation
-        {
-            let policy: Concrete<String> = policy_str!("or(and(pk(A),pk(B)),and(pk(C),pk(D)))");
-            let descriptor = policy.compile_tr(Some(unspendable_key.clone())).unwrap();
-
-            let left_ms_compilation: Arc<Miniscript<String, Tap>> =
-                Arc::new(ms_str!("and_v(v:pk(C),pk(D))"));
-            let right_ms_compilation: Arc<Miniscript<String, Tap>> =
-                Arc::new(ms_str!("and_v(v:pk(A),pk(B))"));
-
-            let left = TapTree::Leaf(left_ms_compilation);
-            let right = TapTree::Leaf(right_ms_compilation);
-            let tree = TapTree::combine(left, right);
-
-            let expected_descriptor =
-                Descriptor::new_tr(unspendable_key.clone(), Some(tree)).unwrap();
-            assert_eq!(descriptor, expected_descriptor);
-        }
-
-        {
-            // Invalid policy compilation (Duplicate PubKeys)
-            let policy: Concrete<String> = policy_str!("or(and(pk(A),pk(B)),and(pk(A),pk(D)))");
-            let descriptor = policy.compile_tr(Some(unspendable_key.clone()));
-
-            assert_eq!(descriptor.unwrap_err().to_string(), "Policy contains duplicate keys");
-        }
-
-        // Non-trivial multi-node compilation
-        {
-            let node_policies = [
-                "and(pk(A),pk(B))",
-                "and(pk(C),older(12960))",
-                "pk(D)",
-                "pk(E)",
-                "thresh(3,pk(F),pk(G),pk(H))",
-                "and(and(or(2@pk(I),1@pk(J)),or(1@pk(K),20@pk(L))),pk(M))",
-                "pk(N)",
-            ];
-
-            // Floating-point precision errors cause the minor errors
-            let node_probabilities: [f64; 7] =
-                [0.12000002, 0.28, 0.08, 0.12, 0.19, 0.18999998, 0.02];
-
-            let policy: Concrete<String> = policy_str!(
-                "{}",
-                &format!(
-                    "or(4@or(3@{},7@{}),6@thresh(1,or(4@{},6@{}),{},or(9@{},1@{})))",
-                    node_policies[0],
-                    node_policies[1],
-                    node_policies[2],
-                    node_policies[3],
-                    node_policies[4],
-                    node_policies[5],
-                    node_policies[6]
-                )
-            );
-            let descriptor = policy.compile_tr(Some(unspendable_key.clone())).unwrap();
-
-            let mut sorted_policy_prob = node_policies
-                .iter()
-                .zip(node_probabilities.iter())
-                .collect::<Vec<_>>();
-            sorted_policy_prob.sort_by(|a, b| (a.1).partial_cmp(&b.1).unwrap());
-            let sorted_policies = sorted_policy_prob
-                .into_iter()
-                .map(|(x, _prob)| x)
-                .collect::<Vec<_>>();
-
-            // Generate TapTree leaves compilations from the given sub-policies
-            let node_compilations = sorted_policies
-                .into_iter()
-                .map(|x| {
-                    let leaf_policy: Concrete<String> = policy_str!("{}", x);
-                    TapTree::Leaf(Arc::from(leaf_policy.compile::<Tap>().unwrap()))
-                })
-                .collect::<Vec<_>>();
-
-            // Arrange leaf compilations (acc. to probabilities) using huffman encoding into a TapTree
-            let tree = TapTree::combine(
-                TapTree::combine(node_compilations[4].clone(), node_compilations[5].clone()),
-                TapTree::combine(
-                    TapTree::combine(
-                        TapTree::combine(
-                            node_compilations[0].clone(),
-                            node_compilations[1].clone(),
-                        ),
-                        node_compilations[3].clone(),
-                    ),
-                    node_compilations[6].clone(),
-                ),
-            );
-
-            let expected_descriptor = Descriptor::new_tr("E".to_string(), Some(tree)).unwrap();
-            assert_eq!(descriptor, expected_descriptor);
-        }
-    }
-
-    #[test]
-    #[cfg(feature = "compiler")]
-    fn experimental_taproot_compile() {
-        let unspendable_key = "UNSPEND".to_string();
-
-        {
-            let pol = Concrete::<String>::from_str(
-                "thresh(7,pk(A),pk(B),pk(C),pk(D),pk(E),pk(F),pk(G),pk(H))",
-            )
-            .unwrap();
-            let desc = pol
-                .compile_tr_private_experimental(Some(unspendable_key.clone()))
-                .unwrap();
-            let expected_desc = Descriptor::Tr(
-                Tr::<String>::from_str(
-                    "tr(UNSPEND ,{
-                {
-                    {multi_a(7,B,C,D,E,F,G,H),multi_a(7,A,C,D,E,F,G,H)},
-                    {multi_a(7,A,B,D,E,F,G,H),multi_a(7,A,B,C,E,F,G,H)}
-                },
-                {
-                    {multi_a(7,A,B,C,D,F,G,H),multi_a(7,A,B,C,D,E,G,H)}
-                   ,{multi_a(7,A,B,C,D,E,F,H),multi_a(7,A,B,C,D,E,F,G)}
-                }})"
-                    .replace(&['\t', ' ', '\n'][..], "")
-                    .as_str(),
-                )
-                .unwrap(),
-            );
-            assert_eq!(desc, expected_desc);
-        }
-
-        {
-            let pol =
-                Concrete::<String>::from_str("thresh(3,pk(A),pk(B),pk(C),pk(D),pk(E))").unwrap();
-            let desc = pol
-                .compile_tr_private_experimental(Some(unspendable_key.clone()))
-                .unwrap();
-            let expected_desc = Descriptor::Tr(
-                Tr::<String>::from_str(
-                    "tr(UNSPEND,
-                    {{
-                        {multi_a(3,A,D,E),multi_a(3,A,C,E)},
-                        {multi_a(3,A,C,D),multi_a(3,A,B,E)}\
-                    },
-                    {
-                        {multi_a(3,A,B,D),multi_a(3,A,B,C)},
-                        {
-                            {multi_a(3,C,D,E),multi_a(3,B,D,E)},
-                            {multi_a(3,B,C,E),multi_a(3,B,C,D)}
-                    }}})"
-                        .replace(&['\t', ' ', '\n'][..], "")
-                        .as_str(),
-                )
-                .unwrap(),
-            );
-            assert_eq!(desc, expected_desc);
-        }
-    }
-}
-
-#[cfg(all(bench, feature = "compiler"))]
-mod benches {
-    use core::str::FromStr;
-
-    use test::{black_box, Bencher};
-
-    use super::{Concrete, Error};
-    use crate::descriptor::Descriptor;
-    use crate::prelude::*;
-    type TapDesc = Result<Descriptor<String>, Error>;
-
-    #[bench]
-    pub fn compile_large_tap(bh: &mut Bencher) {
-        let pol = Concrete::<String>::from_str(
-            "thresh(20,pk(A),pk(B),pk(C),pk(D),pk(E),pk(F),pk(G),pk(H),pk(I),pk(J),pk(K),pk(L),pk(M),pk(N),pk(O),pk(P),pk(Q),pk(R),pk(S),pk(T),pk(U),pk(V),pk(W),pk(X),pk(Y),pk(Z))",
-        )
-        .expect("parsing");
-        bh.iter(|| {
-            let pt: TapDesc = pol.compile_tr_private_experimental(Some("UNSPEND".to_string()));
-            black_box(pt).unwrap();
-        });
-    }
-}
+pub(crate) const ENTAILMENT_MAX_TERMINALS: usize = 20;