1414
1515//! Abstract Policies
1616
17+ use std:: collections:: HashSet ;
1718use std:: str:: FromStr ;
1819use std:: { fmt, str} ;
1920
@@ -60,7 +61,7 @@ pub enum Policy<Pk: MiniscriptKey> {
6061/// Compute the Policy difference between two policies.
6162/// This is useful when trying to find out the conditions
6263/// under which the two policies are different.
63- #[ derive( Debug , Clone ) ]
64+ #[ derive( Debug , Clone , PartialEq , Eq , PartialOrd , Ord ) ]
6465pub struct PolicyDiff < Pk : MiniscriptKey > {
6566 /// The first policy
6667 pub a : Vec < Policy < Pk > > ,
@@ -85,21 +86,49 @@ impl<Pk: MiniscriptKey> PolicyDiff<Pk> {
8586
8687 /// Create a new PolicyDiff in the
8788 pub fn new ( a : Policy < Pk > , b : Policy < Pk > ) -> Self {
88- match ( a. normalized ( ) , b. normalized ( ) ) {
89- ( x, y) if x == y => Self :: _new ( vec ! [ ] , vec ! [ ] ) ,
90- ( Policy :: Threshold ( k1, subs1) , Policy :: Threshold ( k2, subs2) )
91- if k1 == k2 && subs1. len ( ) == subs2. len ( ) =>
92- {
93- let mut a = Self :: _new ( vec ! [ ] , vec ! [ ] ) ;
94-
95- for ( sub_a, sub_b) in subs1. into_iter ( ) . zip ( subs2. into_iter ( ) ) {
96- let sub_diff = Self :: _new ( vec ! [ sub_a] , vec ! [ sub_b] ) ;
97- a. combine ( sub_diff) ;
89+ pub fn new_helper < Pk : MiniscriptKey > ( a : Policy < Pk > , b : Policy < Pk > ) -> PolicyDiff < Pk > {
90+ match ( a, b) {
91+ ( ref x, ref y) if x == y => PolicyDiff :: _new ( vec ! [ ] , vec ! [ ] ) ,
92+ ( Policy :: Threshold ( k1, subs1) , Policy :: Threshold ( k2, subs2) ) => {
93+ if k1 == k2 && subs1. len ( ) == subs2. len ( ) {
94+ let mut ind_a = HashSet :: new ( ) ;
95+ let mut ind_b = HashSet :: new ( ) ;
96+ for i in 0 ..subs1. len ( ) {
97+ let sub_a = & subs1[ i] ;
98+ let b_pos = subs2. iter ( ) . position ( |sub_b| sub_a == sub_b) ;
99+ match b_pos {
100+ Some ( j) => {
101+ ind_a. insert ( i) ;
102+ ind_b. insert ( j) ;
103+ }
104+ None => { }
105+ }
106+ }
107+ let diff_a: Vec < _ > = subs1
108+ . into_iter ( )
109+ . enumerate ( )
110+ . filter ( |( i, _x) | !ind_a. contains ( i) )
111+ . map ( |( _i, p) | p)
112+ . collect ( ) ;
113+ let diff_b = subs2
114+ . into_iter ( )
115+ . enumerate ( )
116+ . filter ( |( i, _x) | !ind_b. contains ( i) )
117+ . map ( |( _i, p) | p)
118+ . collect :: < Vec < _ > > ( ) ;
119+ PolicyDiff :: _new ( diff_a, diff_b)
120+ } else {
121+ PolicyDiff :: _new (
122+ vec ! [ Policy :: Threshold ( k1, subs1) ] ,
123+ vec ! [ Policy :: Threshold ( k2, subs2) ] ,
124+ )
125+ }
98126 }
99- a
127+ ( x , y ) => PolicyDiff :: _new ( vec ! [ x ] , vec ! [ y ] ) ,
100128 }
101- ( x, y) => Self :: _new ( vec ! [ x] , vec ! [ y] ) ,
102129 }
130+
131+ new_helper ( a. normalized ( ) , b. normalized ( ) )
103132 }
104133}
105134
@@ -214,6 +243,67 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
214243 }
215244 }
216245
246+ fn _pprint_diff ( & self , other : & Policy < Pk > , prefix : String , last : bool ) {
247+ match ( self , other) {
248+ ( x, y) if x == y => x. _pprint_tree ( prefix, last) ,
249+ ( Policy :: Threshold ( k1, subs1) , Policy :: Threshold ( k2, subs2) )
250+ if k1 == k2 && subs1. len ( ) == subs2. len ( ) =>
251+ {
252+ let prefix_current = if last { "`-- " } else { "|-- " } ;
253+
254+ println ! ( "{}{}{}" , prefix, prefix_current, self . term_name( ) ) ;
255+
256+ let prefix_child = if last { " " } else { "| " } ;
257+ let prefix = prefix + prefix_child;
258+
259+ let last_child = subs1. len ( ) - 1 ;
260+
261+ // Hashsets to maintain which children of subs are matched
262+ // onto children from others
263+ let mut ind_a = HashSet :: new ( ) ;
264+ let mut ind_b = HashSet :: new ( ) ;
265+ for i in 0 ..subs1. len ( ) {
266+ let sub_a = & subs1[ i] ;
267+ let b_pos = subs2. iter ( ) . position ( |sub_b| sub_a == sub_b) ;
268+ match b_pos {
269+ Some ( j) => {
270+ ind_a. insert ( i) ;
271+ ind_b. insert ( j) ;
272+ }
273+ None => { }
274+ }
275+ }
276+ let mut j = 0 ;
277+ for ( i, sub_a) in subs1. iter ( ) . enumerate ( ) {
278+ // The position in subs2
279+ if ind_a. contains ( & i) {
280+ sub_a. _pprint_tree ( prefix. to_string ( ) , last) ;
281+ } else {
282+ while ind_b. contains ( & j) {
283+ j = j + 1 ;
284+ }
285+ sub_a. _pprint_diff ( & subs2[ j] , prefix. to_string ( ) , i == last_child) ;
286+ }
287+ }
288+ }
289+ ( x, y) => {
290+ let red = "\x1b [0;31m" ;
291+ let nc = "\x1b [0m" ;
292+ let green = "\x1b [0;32m" ;
293+ print ! ( "{}" , green) ;
294+ x. _pprint_tree ( prefix. clone ( ) , last) ;
295+ print ! ( "{}{}" , nc, red) ;
296+ y. _pprint_tree ( prefix, last) ;
297+ print ! ( "{}" , nc) ;
298+ }
299+ }
300+ }
301+
302+ /// Pretty Print a tree
303+ pub fn pprint_diff ( & self , other : & Policy < Pk > ) {
304+ self . _pprint_diff ( other, "" . to_string ( ) , true ) ;
305+ }
306+
217307 /// This function computes whether the current policy entails the second one.
218308 /// A |- B means every satisfaction of A is also a satisfaction of B.
219309 /// This implementation will run slow for larger policies but should be sufficient for
@@ -863,6 +953,34 @@ mod tests {
863953 assert_eq ! ( policy. minimum_n_keys( ) , Some ( 0 ) ) ;
864954 }
865955
956+ #[ test]
957+ fn policy_diff ( ) {
958+ let pol1 = StringPolicy :: from_str ( "or(pkh(A),pkh(C))" ) . unwrap ( ) ;
959+ let pol2 = StringPolicy :: from_str ( "or(pkh(B),pkh(C))" ) . unwrap ( ) ;
960+ let diff = PolicyDiff :: new ( pol1. clone ( ) , pol2. clone ( ) ) ;
961+ assert_eq ! (
962+ diff,
963+ PolicyDiff :: new(
964+ StringPolicy :: from_str( "pkh(A)" ) . unwrap( ) ,
965+ StringPolicy :: from_str( "pkh(B)" ) . unwrap( )
966+ )
967+ ) ;
968+ // Uncomment for pprint
969+ // pol1.pprint_diff(&pol2);
970+
971+ let pol1 = StringPolicy :: from_str ( "or(pkh(A),pkh(C))" ) . unwrap ( ) ;
972+ // change the order
973+ let pol2 = StringPolicy :: from_str ( "or(pkh(C),and(pkh(B),older(9)))" ) . unwrap ( ) ;
974+ let diff = PolicyDiff :: new ( pol1. clone ( ) , pol2. clone ( ) ) ;
975+ assert_eq ! (
976+ diff,
977+ PolicyDiff :: new(
978+ StringPolicy :: from_str( "pkh(A)" ) . unwrap( ) ,
979+ StringPolicy :: from_str( "and(pkh(B),older(9))" ) . unwrap( )
980+ )
981+ ) ;
982+ }
983+
866984 #[ test]
867985 fn entailment_liquid_test ( ) {
868986 //liquid policy
0 commit comments