@@ -77,13 +77,35 @@ fn calc_final_eligible_rankings(
7777 . collect ( )
7878}
7979
80+ fn calc_final_ranking_consensus_per_review ( rankings : & [ impl Borrow < VeteranRankingRow > ] ) -> Decimal {
81+ let rankings_majority = Decimal :: from ( rankings. len ( ) ) / Decimal :: from ( 2 ) ;
82+ let ranks = rankings. iter ( ) . counts_by ( |r| r. borrow ( ) . score ( ) ) ;
83+
84+ match ( ranks. get ( & FilteredOut ) , ranks. get ( & Excellent ) , ranks. get ( & Good ) ) {
85+ ( Some ( filtered_out) , _, _) if Decimal :: from ( * filtered_out) >= rankings_majority => {
86+ Decimal :: from ( * filtered_out) / Decimal :: from ( rankings. len ( ) )
87+ }
88+ ( _, Some ( excellent) , _) if Decimal :: from ( * excellent) > rankings_majority => {
89+ Decimal :: from ( * excellent) / Decimal :: from ( rankings. len ( ) )
90+ }
91+ ( _, Some ( excellent) , Some ( good) ) => {
92+ ( Decimal :: from ( * excellent) + Decimal :: from ( * good) ) / Decimal :: from ( rankings. len ( ) )
93+ }
94+ ( _, _, Some ( good) ) => {
95+ Decimal :: from ( * good) / Decimal :: from ( rankings. len ( ) )
96+ }
97+ _ => Decimal :: ONE ,
98+ }
99+ }
100+
80101pub fn calculate_veteran_advisors_incentives (
81102 veteran_rankings : & [ VeteranRankingRow ] ,
82103 total_rewards : Rewards ,
83104 rewards_thresholds : EligibilityThresholds ,
84105 reputation_thresholds : EligibilityThresholds ,
85106 rewards_mod_args : Vec < ( Decimal , Decimal ) > ,
86107 reputation_mod_args : Vec < ( Decimal , Decimal ) > ,
108+ minimum_consensus : Decimal ,
87109) -> HashMap < VeteranAdvisorId , VeteranAdvisorIncentive > {
88110 let final_rankings_per_review = veteran_rankings
89111 . iter ( )
@@ -92,6 +114,13 @@ pub fn calculate_veteran_advisors_incentives(
92114 . map ( |( review, rankings) | ( review, calc_final_ranking_per_review ( & rankings) ) )
93115 . collect :: < BTreeMap < _ , _ > > ( ) ;
94116
117+ let final_rankings_consensus_per_review = veteran_rankings
118+ . iter ( )
119+ . into_group_map_by ( |ranking| ranking. review_id ( ) )
120+ . into_iter ( )
121+ . map ( |( review, rankings) | ( review, calc_final_ranking_consensus_per_review ( & rankings) ) )
122+ . collect :: < BTreeMap < _ , _ > > ( ) ;
123+
95124 let rankings_per_vca = veteran_rankings
96125 . iter ( )
97126 . counts_by ( |ranking| ranking. vca . clone ( ) ) ;
@@ -103,7 +132,7 @@ pub fn calculate_veteran_advisors_incentives(
103132 . get ( & ranking. review_id ( ) )
104133 . unwrap ( )
105134 . is_positive ( )
106- == ranking. score ( ) . is_positive ( )
135+ == ranking. score ( ) . is_positive ( ) || * final_rankings_consensus_per_review . get ( & ranking . review_id ( ) ) . unwrap ( ) < minimum_consensus
107136 } )
108137 . counts_by ( |ranking| ranking. vca . clone ( ) ) ;
109138
@@ -156,6 +185,8 @@ mod tests {
156185 const VCA_1 : & str = "vca1" ;
157186 const VCA_2 : & str = "vca2" ;
158187 const VCA_3 : & str = "vca3" ;
188+ const SIMPLE_MINIMUM_CONSENSUS : Decimal = dec ! ( . 5 ) ;
189+ const QUALIFIED_MINIMUM_CONSENSUS : Decimal = dec ! ( . 7 ) ;
159190
160191 struct RandomIterator ;
161192 impl Iterator for RandomIterator {
@@ -231,6 +262,7 @@ mod tests {
231262 . into_iter ( )
232263 . zip ( REPUTATION_DISAGREEMENT_MODIFIERS . into_iter ( ) )
233264 . collect ( ) ,
265+ SIMPLE_MINIMUM_CONSENSUS ,
234266 ) ;
235267 assert ! ( results. get( VCA_1 ) . is_none( ) ) ;
236268 let res = results. get ( VCA_2 ) . unwrap ( ) ;
@@ -260,6 +292,7 @@ mod tests {
260292 . into_iter ( )
261293 . zip ( REPUTATION_DISAGREEMENT_MODIFIERS . into_iter ( ) )
262294 . collect ( ) ,
295+ SIMPLE_MINIMUM_CONSENSUS ,
263296 ) ;
264297 let res1 = results. get ( VCA_1 ) . unwrap ( ) ;
265298 assert_eq ! ( res1. reputation, 1 ) ;
@@ -283,12 +316,12 @@ mod tests {
283316 ( Rewards :: new ( 8 , 1 ) , Rewards :: ONE , Rewards :: ONE ) ,
284317 ( Rewards :: new ( 9 , 1 ) , Rewards :: new ( 125 , 2 ) , Rewards :: ONE ) ,
285318 ] ;
286- for ( agreement , reward_modifier, reputation_modifier) in inputs {
319+ for ( vca3_agreement , reward_modifier, reputation_modifier) in inputs {
287320 let rankings = ( 0 ..100 )
288321 . flat_map ( |i| {
289322 let vcas =
290323 vec ! [ VCA_1 . to_owned( ) , VCA_2 . to_owned( ) , VCA_3 . to_owned( ) ] . into_iter ( ) ;
291- let ( good, filtered_out) = if Rewards :: from ( i) < agreement * Rewards :: from ( 100 )
324+ let ( good, filtered_out) = if Rewards :: from ( i) < vca3_agreement * Rewards :: from ( 100 )
292325 {
293326 ( 3 , 0 )
294327 } else {
@@ -297,7 +330,7 @@ mod tests {
297330 gen_dummy_rankings ( i. to_string ( ) , 0 , good, filtered_out, vcas) . into_iter ( )
298331 } )
299332 . collect :: < Vec < _ > > ( ) ;
300- let results = calculate_veteran_advisors_incentives (
333+ let results_simple_consensus = calculate_veteran_advisors_incentives (
301334 & rankings,
302335 total_rewards,
303336 1 ..=200 ,
@@ -310,21 +343,58 @@ mod tests {
310343 . into_iter ( )
311344 . zip ( REPUTATION_DISAGREEMENT_MODIFIERS . into_iter ( ) )
312345 . collect ( ) ,
346+ SIMPLE_MINIMUM_CONSENSUS ,
313347 ) ;
314- let expected_reward_portion = agreement * Rewards :: from ( 100 ) * reward_modifier;
315- dbg ! ( expected_reward_portion) ;
316- dbg ! ( agreement, reward_modifier, reputation_modifier) ;
317- let expected_rewards = total_rewards
318- / ( Rewards :: from ( 125 * 2 ) + expected_reward_portion)
319- * expected_reward_portion;
320- let res = results. get ( VCA_3 ) . unwrap ( ) ;
348+ let vca3_expected_reward_portion_simple_consensus = vca3_agreement * Rewards :: from ( 100 ) * reward_modifier;
349+ dbg ! ( vca3_expected_reward_portion_simple_consensus) ;
350+ dbg ! ( vca3_agreement, reward_modifier, reputation_modifier) ;
351+ let vca3_expected_rewards_simple_consensus = total_rewards
352+ / ( Rewards :: from ( 125 * 2 ) + vca3_expected_reward_portion_simple_consensus)
353+ * vca3_expected_reward_portion_simple_consensus;
354+ let res_vca3_simple_consensus = results_simple_consensus. get ( VCA_3 ) . unwrap ( ) ;
355+ assert_eq ! (
356+ res_vca3_simple_consensus. reputation,
357+ ( Rewards :: from( 100 ) * vca3_agreement * reputation_modifier)
358+ . to_u64( )
359+ . unwrap( )
360+ ) ;
361+ assert ! ( are_close( res_vca3_simple_consensus. rewards, vca3_expected_rewards_simple_consensus) ) ;
362+
363+
364+ let results_qualified_consensus = calculate_veteran_advisors_incentives (
365+ & rankings,
366+ total_rewards,
367+ 1 ..=200 ,
368+ 1 ..=200 ,
369+ THRESHOLDS
370+ . into_iter ( )
371+ . zip ( REWARDS_DISAGREEMENT_MODIFIERS . into_iter ( ) )
372+ . collect ( ) ,
373+ THRESHOLDS
374+ . into_iter ( )
375+ . zip ( REPUTATION_DISAGREEMENT_MODIFIERS . into_iter ( ) )
376+ . collect ( ) ,
377+ QUALIFIED_MINIMUM_CONSENSUS ,
378+ ) ;
379+
380+ let vca3_expected_reward_portion_qualified_consensus = Rewards :: from ( 100 ) * dec ! ( 1.25 ) ; // low consensus so max reward modifier, agreement ratio doesn't count as all and rankings are all eligible
381+ dbg ! ( vca3_expected_reward_portion_qualified_consensus) ;
382+ dbg ! ( vca3_agreement, reward_modifier, reputation_modifier) ;
383+
384+ let vca3_expected_rewards_qualified_consensus = total_rewards
385+ / ( Rewards :: from ( 125 * 2 ) + vca3_expected_reward_portion_qualified_consensus)
386+ * vca3_expected_reward_portion_qualified_consensus; // 1/3 of the reward
387+
388+ let res_vca3_qualified_consensus = results_qualified_consensus. get ( VCA_3 ) . unwrap ( ) ;
389+
390+
321391 assert_eq ! (
322- res . reputation,
323- ( Rewards :: from( 100 ) * agreement * reputation_modifier )
392+ res_vca3_qualified_consensus . reputation,
393+ ( Rewards :: from( 100 ) ) // all assessment are valid since consensus is low (2/3 < 0.7 )
324394 . to_u64( )
325395 . unwrap( )
326396 ) ;
327- assert ! ( are_close( res . rewards, expected_rewards ) ) ;
397+ assert ! ( are_close( res_vca3_qualified_consensus . rewards, vca3_expected_rewards_qualified_consensus ) ) ;
328398 }
329399 }
330400}
0 commit comments