Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 27
0.00% covered (danger)
0.00%
0 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
Jetpack_Constrained_Array_Rounding
0.00% covered (danger)
0.00%
0 / 27
0.00% covered (danger)
0.00%
0 / 5
90
0.00% covered (danger)
0.00%
0 / 1
 get_rounded_constrained_array
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
12
 get_int_floor_array
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
6
 adjust_constrained_array
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
6
 cmp_desc_fraction
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 cmp_asc_index
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2
3/**
4 * Lets you round the numeric elements of an array to integers while preserving their sum.
5 *
6 * Usage:
7 *
8 * Jetpack_Constrained_Array_Rounding::get_rounded_constrained_array( $bound_array )
9 * if a specific sum doesn't need to be specified for the bound array
10 *
11 * Jetpack_Constrained_Array_Rounding::get_rounded_constrained_array( $bound_array, $sum )
12 * If the sum of $bound_array must equal $sum after rounding.
13 *
14 * If $sum is less than the sum of the floor of the elements of the array, the class defaults to using the sum of the array elements.
15 */
16class Jetpack_Constrained_Array_Rounding {
17    /**
18     * Get the rounded constrained array.
19     *
20     * @param array $bound_array - the array we're rounding.
21     * @param int   $sum - the sum of the array.
22     *
23     * @return array
24     */
25    public static function get_rounded_constrained_array( $bound_array, $sum = false ) {
26        // Convert associative arrays before working with them and convert them back before returning the values
27        $keys        = array_keys( $bound_array );
28        $bound_array = array_values( $bound_array );
29
30        $bound_array_int = self::get_int_floor_array( $bound_array );
31
32        $lower_sum = array_sum( wp_list_pluck( $bound_array_int, 'floor' ) );
33        if ( ! $sum || ( $sum < $lower_sum ) ) {
34            // If value of sum is not supplied or is invalid, calculate the sum that the returned array is constrained to match
35            $sum = array_sum( $bound_array );
36        }
37        $diff_sum = $sum - $lower_sum;
38
39        self::adjust_constrained_array( $bound_array_int, $diff_sum );
40
41        $bound_array_fin = wp_list_pluck( $bound_array_int, 'floor' );
42        return array_combine( $keys, $bound_array_fin );
43    }
44
45    /**
46     * Get int floor of array values.
47     *
48     * @param array $bound_array - the array we're getting floor values for.
49     *
50     * @return array
51     */
52    private static function get_int_floor_array( $bound_array ) {
53        $bound_array_int_floor = array();
54        foreach ( $bound_array as $i => $value ) {
55            $bound_array_int_floor[ $i ] = array(
56                'floor'    => (int) floor( $value ),
57                'fraction' => $value - floor( $value ),
58                'index'    => $i,
59            );
60        }
61
62        return $bound_array_int_floor;
63    }
64
65    /**
66     * Adjust the constrained array.
67     *
68     * @param array $bound_array_int - the array we're adjusting.
69     * @param int   $adjustment - how much we're adjusting the array.
70     */
71    private static function adjust_constrained_array( &$bound_array_int, $adjustment ) {
72        usort( $bound_array_int, array( self::class, 'cmp_desc_fraction' ) );
73
74        $start  = 0;
75        $end    = $adjustment - 1;
76        $length = count( $bound_array_int );
77
78        for ( $i = $start; $i <= $end; $i++ ) {
79            ++$bound_array_int[ $i % $length ]['floor'];
80        }
81
82        usort( $bound_array_int, array( self::class, 'cmp_asc_index' ) );
83    }
84
85    /**
86     * Compare fraction values of two arrays.
87     *
88     * @param array $a - the first array we're comparing.
89     * @param array $b - the second array we're comparing.
90     *
91     * @return int
92     */
93    private static function cmp_desc_fraction( $a, $b ) {
94        return $b['fraction'] <=> $a['fraction'];
95    }
96
97    /**
98     * Compare index values of two arrays.
99     *
100     * @param array $a - the first array.
101     * @param array $b - the second array.
102     *
103     * @return int
104     */
105    private static function cmp_asc_index( $a, $b ) {
106        return $a['index'] <=> $b['index'];
107    }
108}