diff --git a/module/move/willbe/src/entity/features.rs b/module/move/willbe/src/entity/features.rs index 3099f8fbd9..a292b131b1 100644 --- a/module/move/willbe/src/entity/features.rs +++ b/module/move/willbe/src/entity/features.rs @@ -2,6 +2,7 @@ mod private { use crate::*; use std::collections::{ BTreeSet, HashSet }; + use error_tools::err; // aaa : for Petro : don't use cargo_metadata and Package directly, use facade // aaa : ✅ use error_tools::for_app::{ bail, Result }; @@ -67,7 +68,7 @@ mod private .cloned() .collect(); - if esimate_with( filtered_features.len(), power, with_all_features, with_none_features, enabled_features, package.features().len() ) > variants_cap as usize + if estimate_with( filtered_features.len(), power, with_all_features, with_none_features, enabled_features, package.features().len() ) > variants_cap as usize { bail!( "Feature powerset longer then cap." ) } @@ -100,46 +101,54 @@ mod private Ok( features_powerset ) } - - fn esimate_with( filtered_length : usize, power : usize, with_all : bool, with_none : bool, enabled : &[ String ], unfiltred_length : usize ) -> usize + /// Calculate estimate for `features_powerset.length` + pub fn estimate_with + ( + n : usize, + power : usize, + with_all_features : bool, + with_none_features : bool, + enabled_features : &[ String ], + total_features : usize + ) + -> usize { - let mut e = esimate( filtered_length, power); - if !enabled.is_empty() && with_none - { - e += 1; - } - if with_all && power + enabled.len() >= unfiltred_length - { - e += 1; - } - e - } + let mut estimate = 0; + let mut binom = 1; + let power = power.min( n ); - fn esimate( filtered_length : usize, power : usize ) -> usize - { - let mut r = 0; - for p in 1..power + for k in 0..=power { - r += factorial( filtered_length ) / (factorial(p) * factorial( filtered_length - p ) ); + estimate += binom; + binom = binom * ( n - k ) / ( k + 1 ); } - r - } - fn factorial( n : usize ) -> usize - { - return if n == 1 - { - 1 - } - else + if with_all_features { estimate += 1; } + if with_none_features { estimate += 1; } + + if !enabled_features.is_empty() { - n * factorial(n - 1) + let len = enabled_features.len(); + let combinations = ( 0..=len.min( total_features ) ).map( | k | + { + let mut binom = 1; + for i in 0..k + { + binom = binom * ( len - i ) / ( i + 1 ); + } + binom + }).sum::< usize >(); + estimate += combinations; } + + estimate } + } crate::mod_interface! { /// Features protected use features_powerset; + protected use estimate_with; } diff --git a/module/move/willbe/tests/inc/entity/features.rs b/module/move/willbe/tests/inc/entity/features.rs index 3ee575389a..dd4db5bff8 100644 --- a/module/move/willbe/tests/inc/entity/features.rs +++ b/module/move/willbe/tests/inc/entity/features.rs @@ -6,6 +6,7 @@ use the_module::features::features_powerset; use std::collections::HashMap; use serde::Deserialize; use the_module::workspace::WorkspacePackage; +use willbe::features::estimate_with; /// Constructs a mock `Package` with specified features for testing. fn mock_package( features : Vec< ( &str, Vec< &str > ) > ) -> WorkspacePackage @@ -254,4 +255,14 @@ fn case_6() assert!( result.contains( &vec![ "f2".to_string() ].into_iter().collect()) ); assert_eq!( result.len(), 2 ); +} + +#[ test ] +fn estimate() +{ + assert_eq!( estimate_with( 5, 2, false, false, &[], 0 ), 16 ); + assert_eq!( estimate_with( 5, 2, true, false, &[], 0 ), 17 ); + assert_eq!( estimate_with( 5, 2, false, true, &[], 0 ), 17 ); + assert_eq!( estimate_with( 5, 2, false, false, &[ "feature1".to_string(), "feature2".to_string() ], 2 ), 20 ); + assert_eq!( estimate_with( 5, 2, true, true, &[ "feature1".to_string(), "feature2".to_string() ], 2 ), 22 ); } \ No newline at end of file