pub fn distribute_surplus_by_scaling_incoming_transfer_values(&mut self,candidate_to_distribute:CandidateIndex,merge_same_tv:bool)  {
        let votes : Rules::Tally = self.tally(candidate_to_distribute);
        let surplus: Rules::Tally  = votes.clone()-self.quota.clone();
        let votes_to_distribute : Vec<(TransferValue,(Rules::Tally,VotesWithSameTransferValue,PortionOfReasonBeingDoneThisCount))> =
            if merge_same_tv { self.papers[candidate_to_distribute.0].extract_all_ballots_separated_by_transfer_value() } // sorted highest TV to lowest
            else {
                if let Some(custom_sorter) = Rules::sort_subcounts_by_count() {
                    /*let transcript = Transcript::<Rules::Tally>{
                        rules: "".to_string(),
                        quota: QuotaInfo {
                            papers: BallotPaperCount(0),
                            vacancies: NumberOfCandidates(0),
                            quota: Rules::Tally::zero(),
                        },
                        counts: vec![],
                        elected: vec![]
                    };*/
                    self.papers[candidate_to_distribute.0].extract_all_ballots_separated_by_key(Some(custom_sorter),&self.transcript)
                } else {
                    self.papers[candidate_to_distribute.0].extract_all_ballots_separated_by_key(None,&self.transcript)
                }
                //let custom_sort_function : Option<Box<(dyn FnMut(<<Rules as PreferenceDistributionRules>::SplitByNumber as HowSplitByCountNumber>::KeyToDivide, <<Rules as PreferenceDistributionRules>::SplitByNumber as HowSplitByCountNumber>::KeyToDivide) -> std::cmp::Ordering + 'static)>> = Rules::sort_subcounts_by_count().map(|f|Box::new(|a1:<<Rules as PreferenceDistributionRules>::SplitByNumber as HowSplitByCountNumber>::KeyToDivide,a2:<<Rules as PreferenceDistributionRules>::SplitByNumber as HowSplitByCountNumber>::KeyToDivide|f(&self.transcript,a1,a2)));
                //self.papers[candidate_to_distribute.0].extract_all_ballots_separated_by_key(&custom_sort_function)
            }; // sorted by key
        let mut partially_distributed = vec![];
        let mut total_value_of_exhausted_votes = BigRational::zero();
        let continuing_candidates_when_distribution_done = self.continuing_candidates_sorted_by_tally.len();
        for (tv,(step_tally,ballots,prov)) in votes_to_distribute {
            let distributed = DistributedVotes::distribute(&ballots.votes,&self.continuing_candidates,self.num_candidates);
            let exhausted_value = tv.mul(distributed.exhausted);
            total_value_of_exhausted_votes+=exhausted_value.clone();
            partially_distributed.push((tv,step_tally,ballots,prov,distributed,exhausted_value));
        }
        let general_tv_denom : BigRational = if Rules::transfer_value_method().denom_is_just_continuing()  { Rules::convert_tally_to_rational(votes.clone())-total_value_of_exhausted_votes.clone() } else { Rules::convert_tally_to_rational(votes.clone()) } ;
        let quota : BigRational = Rules::convert_tally_to_rational(self.quota.clone());
        let special_factor_excluded : Option<BigRational> = if Rules::transfer_value_method().denom_is_just_continuing() || total_value_of_exhausted_votes<=quota || Rules::count_set_aside_due_to_transfer_value_limit_as_rounding() { None } else { Some((total_value_of_exhausted_votes.clone()-quota.clone())/total_value_of_exhausted_votes.clone()) }; // (AV-Q)/Av
        let original_worth_ratio = Rules::convert_tally_to_rational(surplus.clone())/Rules::convert_tally_to_rational(votes.clone());
        let surplus_rational = Rules::convert_tally_to_rational(surplus.clone());
        //println!("TV based on surplus {} = {}-{} divided by {} = {}-{}",surplus_rational,votes,self.quota,general_tv_denom,votes,total_value_of_exhausted_votes);
        let general_tv = if general_tv_denom<=surplus_rational { TransferValue::one() } else { TransferValue(surplus_rational/general_tv_denom) };
        //println!("quota {} exhausted {} special factor excluded {:?} TV {}",self.quota,total_value_of_exhausted_votes,special_factor_excluded,general_tv);
        let mut current_remaining_tally_for_candidate_being_distributed : BigRational = Rules::convert_tally_to_rational(votes.clone());
        let mut togo = partially_distributed.len();
        for (tv,step_tally,ballots,provenance,distributed,_exhausted_value) in partially_distributed {
            togo-=1;
            let is_final_step = togo==0;
            // println!("Parcelling out {} votes with TV {} over {} ballots",original_worth,transfer_value,tv_denom);
            let original_worth = Rules::convert_tally_to_rational(step_tally)*original_worth_ratio.clone();
            current_remaining_tally_for_candidate_being_distributed-=original_worth;
            let before : Rules::Tally = self.tally(candidate_to_distribute);
            let after : Rules::Tally = Rules::convert_rational_to_tally_after_applying_transfer_value(current_remaining_tally_for_candidate_being_distributed.clone());
            self.tallys[candidate_to_distribute.0] = after.clone();
            let original_worth = before-after;
            let distributed = if continuing_candidates_when_distribution_done == self.continuing_candidates_sorted_by_tally.len() {distributed} else { DistributedVotes::distribute(&ballots.votes,&self.continuing_candidates,self.num_candidates) }; // recompute if the continuing candidates list changed.
            let transfer_value = TransferValue(tv.0*general_tv.0.clone());
            let continuing_ballots = ballots.num_ballots-distributed.exhausted;
            self.parcel_out_votes_with_given_transfer_value(transfer_value.clone(),distributed,Some(self.current_count),original_worth,special_factor_excluded.is_some() || !Rules::transfer_value_method().denom_is_just_continuing(),false,special_factor_excluded.as_ref());
            self.in_this_count.created_transfer_value=Some(TransferValueCreation{
                surplus: surplus.clone(),
                votes : votes.clone(),
                excluded_exhausted_tally: Some(StringSerializedRational(total_value_of_exhausted_votes.clone())),
                original_transfer_value: provenance.transfer_value.clone(),
                multiplied_transfer_value : Some(general_tv.clone()),
                ballots_considered : ballots.num_ballots,
                continuing_ballots,
                transfer_value,
                source: Rules::transfer_value_method(),
            });
            self.end_of_count_step(ReasonForCount::ExcessDistribution(candidate_to_distribute), provenance, is_final_step);

        }
    }
/// Distribute a surplus as multiple parcels, with a general ratio with denominator based on votes rather than ballots (typically surplus/votes, possibly taking exhausted votes into account somehow)
/// Then multiply this ratio by the transfer value that everything came with.
Distribute a surplus as multiple parcels, with a general ratio with denominator based on votes rather than ballots (typically surplus/votes, possibly taking exhausted votes into account somehow)
Then multiply this ratio by the transfer value that everything came with.
Distribute a surplus as multiple parcels with a general ratio with denominator based on votes rather than ballots ( typically surplus / votes possibly taking exhausted votes into account somehow ) Then multiply this ratio by the transfer value that everything came with .
pub fn distribute_surplus_by_scaling_incoming_transfer_values(&mut self,candidate_to_distribute:CandidateIndex,merge_same_tv:bool)  {
        let votes : Rules::Tally = self.tally(candidate_to_distribute);
        let surplus: Rules::Tally  = votes.clone()-self.quota.clone();
        let votes_to_distribute : Vec<(TransferValue,(Rules::Tally,VotesWithSameTransferValue,PortionOfReasonBeingDoneThisCount))> =
            if merge_same_tv { self.papers[candidate_to_distribute.0].extract_all_ballots_separated_by_transfer_value() } // sorted highest TV to lowest
            else {
                if let Some(custom_sorter) = Rules::sort_subcounts_by_count() {
                    /*let transcript = Transcript::<Rules::Tally>{
                        rules: "".to_string(),
                        quota: QuotaInfo {
                            papers: BallotPaperCount(0),
                            vacancies: NumberOfCandidates(0),
                            quota: Rules::Tally::zero(),
                        },
                        counts: vec![],
                        elected: vec![]
                    };*/
                    self.papers[candidate_to_distribute.0].extract_all_ballots_separated_by_key(Some(custom_sorter),&self.transcript)
                } else {
                    self.papers[candidate_to_distribute.0].extract_all_ballots_separated_by_key(None,&self.transcript)
                }
                //let custom_sort_function : Option<Box<(dyn FnMut(<<Rules as PreferenceDistributionRules>::SplitByNumber as HowSplitByCountNumber>::KeyToDivide, <<Rules as PreferenceDistributionRules>::SplitByNumber as HowSplitByCountNumber>::KeyToDivide) -> std::cmp::Ordering + 'static)>> = Rules::sort_subcounts_by_count().map(|f|Box::new(|a1:<<Rules as PreferenceDistributionRules>::SplitByNumber as HowSplitByCountNumber>::KeyToDivide,a2:<<Rules as PreferenceDistributionRules>::SplitByNumber as HowSplitByCountNumber>::KeyToDivide|f(&self.transcript,a1,a2)));
                //self.papers[candidate_to_distribute.0].extract_all_ballots_separated_by_key(&custom_sort_function)
            }; // sorted by key
        let mut partially_distributed = vec![];
        let mut total_value_of_exhausted_votes = BigRational::zero();
        let continuing_candidates_when_distribution_done = self.continuing_candidates_sorted_by_tally.len();
        for (tv,(step_tally,ballots,prov)) in votes_to_distribute {
            let distributed = DistributedVotes::distribute(&ballots.votes,&self.continuing_candidates,self.num_candidates);
            let exhausted_value = tv.mul(distributed.exhausted);
            total_value_of_exhausted_votes+=exhausted_value.clone();
            partially_distributed.push((tv,step_tally,ballots,prov,distributed,exhausted_value));
        }
        let general_tv_denom : BigRational = if Rules::transfer_value_method().denom_is_just_continuing()  { Rules::convert_tally_to_rational(votes.clone())-total_value_of_exhausted_votes.clone() } else { Rules::convert_tally_to_rational(votes.clone()) } ;
        let quota : BigRational = Rules::convert_tally_to_rational(self.quota.clone());
        let special_factor_excluded : Option<BigRational> = if Rules::transfer_value_method().denom_is_just_continuing() || total_value_of_exhausted_votes<=quota || Rules::count_set_aside_due_to_transfer_value_limit_as_rounding() { None } else { Some((total_value_of_exhausted_votes.clone()-quota.clone())/total_value_of_exhausted_votes.clone()) }; // (AV-Q)/Av
        let original_worth_ratio = Rules::convert_tally_to_rational(surplus.clone())/Rules::convert_tally_to_rational(votes.clone());
        let surplus_rational = Rules::convert_tally_to_rational(surplus.clone());
        //println!("TV based on surplus {} = {}-{} divided by {} = {}-{}",surplus_rational,votes,self.quota,general_tv_denom,votes,total_value_of_exhausted_votes);
        let general_tv = if general_tv_denom<=surplus_rational { TransferValue::one() } else { TransferValue(surplus_rational/general_tv_denom) };
        //println!("quota {} exhausted {} special factor excluded {:?} TV {}",self.quota,total_value_of_exhausted_votes,special_factor_excluded,general_tv);
        let mut current_remaining_tally_for_candidate_being_distributed : BigRational = Rules::convert_tally_to_rational(votes.clone());
        let mut togo = partially_distributed.len();
        for (tv,step_tally,ballots,provenance,distributed,_exhausted_value) in partially_distributed {
            togo-=1;
            let is_final_step = togo==0;
            // println!("Parcelling out {} votes with TV {} over {} ballots",original_worth,transfer_value,tv_denom);
            let original_worth = Rules::convert_tally_to_rational(step_tally)*original_worth_ratio.clone();
            current_remaining_tally_for_candidate_being_distributed-=original_worth;
            let before : Rules::Tally = self.tally(candidate_to_distribute);
            let after : Rules::Tally = Rules::convert_rational_to_tally_after_applying_transfer_value(current_remaining_tally_for_candidate_being_distributed.clone());
            self.tallys[candidate_to_distribute.0] = after.clone();
            let original_worth = before-after;
            let distributed = if continuing_candidates_when_distribution_done == self.continuing_candidates_sorted_by_tally.len() {distributed} else { DistributedVotes::distribute(&ballots.votes,&self.continuing_candidates,self.num_candidates) }; // recompute if the continuing candidates list changed.
            let transfer_value = TransferValue(tv.0*general_tv.0.clone());
            let continuing_ballots = ballots.num_ballots-distributed.exhausted;
            self.parcel_out_votes_with_given_transfer_value(transfer_value.clone(),distributed,Some(self.current_count),original_worth,special_factor_excluded.is_some() || !Rules::transfer_value_method().denom_is_just_continuing(),false,special_factor_excluded.as_ref());
            self.in_this_count.created_transfer_value=Some(TransferValueCreation{
                surplus: surplus.clone(),
                votes : votes.clone(),
                excluded_exhausted_tally: Some(StringSerializedRational(total_value_of_exhausted_votes.clone())),
                original_transfer_value: provenance.transfer_value.clone(),
                multiplied_transfer_value : Some(general_tv.clone()),
                ballots_considered : ballots.num_ballots,
                continuing_ballots,
                transfer_value,
                source: Rules::transfer_value_method(),
            });
            self.end_of_count_step(ReasonForCount::ExcessDistribution(candidate_to_distribute), provenance, is_final_step);
        }
    }
pub fn distribute_surplus_by_scaling_incoming_transfer_values ( & mut self , candidate_to_distribute : CandidateIndex , merge_same_tv : bool ) { let votes : Rules :: Tally = self . tally ( candidate_to_distribute ) ; let surplus : Rules :: Tally = votes . clone ( ) - self . quota . clone ( ) ; let votes_to_distribute : Vec < ( TransferValue , ( Rules :: Tally , VotesWithSameTransferValue , PortionOfReasonBeingDoneThisCount ) ) > = if merge_same_tv { self . papers [ candidate_to_distribute . 0 ] . extract_all_ballots_separated_by_transfer_value ( ) } // sorted highest TV to lowest else { if let Some ( custom_sorter ) = Rules :: sort_subcounts_by_count ( ) { /*let transcript = Transcript::<Rules::Tally>{
                        rules: "".to_string(),
                        quota: QuotaInfo {
                            papers: BallotPaperCount(0),
                            vacancies: NumberOfCandidates(0),
                            quota: Rules::Tally::zero(),
                        },
                        counts: vec![],
                        elected: vec![]
                    };*/ self . papers [ candidate_to_distribute . 0 ] . extract_all_ballots_separated_by_key ( Some ( custom_sorter ) , & self . transcript ) } else { self . papers [ candidate_to_distribute . 0 ] . extract_all_ballots_separated_by_key ( None , & self . transcript ) } //let custom_sort_function : Option<Box<(dyn FnMut(<<Rules as PreferenceDistributionRules>::SplitByNumber as HowSplitByCountNumber>::KeyToDivide, <<Rules as PreferenceDistributionRules>::SplitByNumber as HowSplitByCountNumber>::KeyToDivide) -> std::cmp::Ordering + 'static)>> = Rules::sort_subcounts_by_count().map(|f|Box::new(|a1:<<Rules as PreferenceDistributionRules>::SplitByNumber as HowSplitByCountNumber>::KeyToDivide,a2:<<Rules as PreferenceDistributionRules>::SplitByNumber as HowSplitByCountNumber>::KeyToDivide|f(&self.transcript,a1,a2))); //self.papers[candidate_to_distribute.0].extract_all_ballots_separated_by_key(&custom_sort_function) } ; // sorted by key let mut partially_distributed = vec ! [ ] ; let mut total_value_of_exhausted_votes = BigRational :: zero ( ) ; let continuing_candidates_when_distribution_done = self . continuing_candidates_sorted_by_tally . len ( ) ; for ( tv , ( step_tally , ballots , prov ) ) in votes_to_distribute { let distributed = DistributedVotes :: distribute ( & ballots . votes , & self . continuing_candidates , self . num_candidates ) ; let exhausted_value = tv . mul ( distributed . exhausted ) ; total_value_of_exhausted_votes += exhausted_value . clone ( ) ; partially_distributed . push ( ( tv , step_tally , ballots , prov , distributed , exhausted_value ) ) ; } let general_tv_denom : BigRational = if Rules :: transfer_value_method ( ) . denom_is_just_continuing ( ) { Rules :: convert_tally_to_rational ( votes . clone ( ) ) - total_value_of_exhausted_votes . clone ( ) } else { Rules :: convert_tally_to_rational ( votes . clone ( ) ) } ; let quota : BigRational = Rules :: convert_tally_to_rational ( self . quota . clone ( ) ) ; let special_factor_excluded : Option < BigRational > = if Rules :: transfer_value_method ( ) . denom_is_just_continuing ( ) || total_value_of_exhausted_votes <= quota || Rules :: count_set_aside_due_to_transfer_value_limit_as_rounding ( ) { None } else { Some ( ( total_value_of_exhausted_votes . clone ( ) - quota . clone ( ) ) / total_value_of_exhausted_votes . clone ( ) ) } ; // (AV-Q)/Av let original_worth_ratio = Rules :: convert_tally_to_rational ( surplus . clone ( ) ) / Rules :: convert_tally_to_rational ( votes . clone ( ) ) ; let surplus_rational = Rules :: convert_tally_to_rational ( surplus . clone ( ) ) ; //println!("TV based on surplus {} = {}-{} divided by {} = {}-{}",surplus_rational,votes,self.quota,general_tv_denom,votes,total_value_of_exhausted_votes); let general_tv = if general_tv_denom <= surplus_rational { TransferValue :: one ( ) } else { TransferValue ( surplus_rational / general_tv_denom ) } ; //println!("quota {} exhausted {} special factor excluded {:?} TV {}",self.quota,total_value_of_exhausted_votes,special_factor_excluded,general_tv); let mut current_remaining_tally_for_candidate_being_distributed : BigRational = Rules :: convert_tally_to_rational ( votes . clone ( ) ) ; let mut togo = partially_distributed . len ( ) ; for ( tv , step_tally , ballots , provenance , distributed , _exhausted_value ) in partially_distributed { togo -= 1 ; let is_final_step = togo == 0 ; // println!("Parcelling out {} votes with TV {} over {} ballots",original_worth,transfer_value,tv_denom); let original_worth = Rules :: convert_tally_to_rational ( step_tally ) * original_worth_ratio . clone ( ) ; current_remaining_tally_for_candidate_being_distributed -= original_worth ; let before : Rules :: Tally = self . tally ( candidate_to_distribute ) ; let after : Rules :: Tally = Rules :: convert_rational_to_tally_after_applying_transfer_value ( current_remaining_tally_for_candidate_being_distributed . clone ( ) ) ; self . tallys [ candidate_to_distribute . 0 ] = after . clone ( ) ; let original_worth = before - after ; let distributed = if continuing_candidates_when_distribution_done == self . continuing_candidates_sorted_by_tally . len ( ) { distributed } else { DistributedVotes :: distribute ( & ballots . votes , & self . continuing_candidates , self . num_candidates ) } ; // recompute if the continuing candidates list changed. let transfer_value = TransferValue ( tv . 0 * general_tv . 0 . clone ( ) ) ; let continuing_ballots = ballots . num_ballots - distributed . exhausted ; self . parcel_out_votes_with_given_transfer_value ( transfer_value . clone ( ) , distributed , Some ( self . current_count ) , original_worth , special_factor_excluded . is_some ( ) || ! Rules :: transfer_value_method ( ) . denom_is_just_continuing ( ) , false , special_factor_excluded . as_ref ( ) ) ; self . in_this_count . created_transfer_value = Some ( TransferValueCreation { surplus : surplus . clone ( ) , votes : votes . clone ( ) , excluded_exhausted_tally : Some ( StringSerializedRational ( total_value_of_exhausted_votes . clone ( ) ) ) , original_transfer_value : provenance . transfer_value . clone ( ) , multiplied_transfer_value : Some ( general_tv . clone ( ) ) , ballots_considered : ballots . num_ballots , continuing_ballots , transfer_value , source : Rules :: transfer_value_method ( ) , } ) ; self . end_of_count_step ( ReasonForCount :: ExcessDistribution ( candidate_to_distribute ) , provenance , is_final_step ) ; } }
Distribute a surplus as multiple parcels, with a general ratio with denominator based on votes rather than ballots (typically surplus/votes, possibly taking exhausted votes into account somehow)
Then multiply this ratio by the transfer value that everything came with.
Distribute a surplus as multiple parcels with a general ratio with denominator based on votes rather than ballots ( typically surplus / votes possibly taking exhausted votes into account somehow ) Then multiply this ratio by the transfer value that everything came with .
&mut self
candidate_to_distribute
CandidateIndex
merge_same_tv
bool
&mut self
candidate_to_distribute
CandidateIndex
merge_same_tv
bool