Passed
Pull Request — master (#4466)
by Owen
15:22
created

Ceiling::argumentsOk()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
eloc 5
dl 0
loc 10
rs 10
c 0
b 0
f 0
ccs 0
cts 0
cp 0
cc 3
nc 3
nop 2
crap 12
1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\Calculation\MathTrig;
4
5
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
6
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
7
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
8
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
9
10
class Ceiling
11
{
12
    use ArrayEnabled;
13
14
    /**
15
     * CEILING.
16
     *
17
     * Returns number rounded up, away from zero, to the nearest multiple of significance.
18
     *        For example, if you want to avoid using pennies in your prices and your product is
19
     *        priced at $4.42, use the formula =CEILING(4.42,0.05) to round prices up to the
20
     *        nearest nickel.
21
     *
22
     * Excel Function:
23
     *        CEILING(number[,significance])
24
     *
25
     * @param array<mixed>|float $number the number you want the ceiling
26
     *                      Or can be an array of values
27
     * @param array<mixed>|float $significance the multiple to which you want to round
28
     *                      Or can be an array of values
29
     *
30
     * @return array<mixed>|float|string Rounded Number, or a string containing an error
31
     *         If an array of numbers is passed as an argument, then the returned result will also be an array
32
     *            with the same dimensions
33
     */
34 35
    public static function ceiling($number, $significance = null)
35
    {
36 35
        if (is_array($number) || is_array($significance)) {
37 5
            return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $significance);
38
        }
39
40 35
        if ($significance === null) {
41 5
            self::floorCheck1Arg();
42
        }
43
44
        try {
45 32
            $number = Helpers::validateNumericNullBool($number);
46 30
            $significance = Helpers::validateNumericNullSubstitution($significance, ($number < 0) ? -1 : 1);
47 3
        } catch (Exception $e) {
48 3
            return $e->getMessage();
49
        }
50
51 29
        return self::argumentsOk((float) $number, (float) $significance);
52
    }
53
54
    /**
55
     * CEILING.MATH.
56
     *
57
     * Round a number down to the nearest integer or to the nearest multiple of significance.
58
     *
59
     * Excel Function:
60
     *        CEILING.MATH(number[,significance[,mode]])
61
     *
62
     * @param mixed $number Number to round
63
     *                      Or can be an array of values
64
     * @param mixed $significance Significance
65
     *                      Or can be an array of values
66
     * @param array<mixed>|int $mode direction to round negative numbers
67
     *                      Or can be an array of values
68
     *
69
     * @return array<mixed>|float|string Rounded Number, or a string containing an error
70
     *         If an array of numbers is passed as an argument, then the returned result will also be an array
71
     *            with the same dimensions
72
     */
73 28
    public static function math(mixed $number, mixed $significance = null, $mode = 0, bool $checkSigns = false): array|string|float
74
    {
75 28
        if (is_array($number) || is_array($significance) || is_array($mode)) {
76 5
            return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $significance, $mode);
77
        }
78
79
        try {
80 28
            $number = Helpers::validateNumericNullBool($number);
81 26
            $significance = Helpers::validateNumericNullSubstitution($significance, ($number < 0) ? -1 : 1);
82 26
            $mode = Helpers::validateNumericNullSubstitution($mode, null);
83 2
        } catch (Exception $e) {
84 2
            return $e->getMessage();
85
        }
86
87 26
        if (empty($significance * $number)) {
88 5
            return 0.0;
89
        }
90 21
        if ($checkSigns) {
91 6
            if (($number > 0 && $significance < 0) || ($number < 0 && $significance > 0)) {
92
                return ExcelError::VALUE();
93
            }
94 15
        }
95
        if (self::ceilingMathTest((float) $significance, (float) $number, (int) $mode)) {
96
            return floor($number / $significance) * $significance;
97
        }
98
99
        return ceil($number / $significance) * $significance;
100
    }
101
102
    /**
103
     * CEILING.PRECISE.
104
     *
105
     * Rounds number up, away from zero, to the nearest multiple of significance.
106
     *
107
     * Excel Function:
108
     *        CEILING.PRECISE(number[,significance])
109
     *
110
     * @param mixed $number the number you want to round
111
     *                      Or can be an array of values
112
     * @param array<mixed>|float $significance the multiple to which you want to round
113
     *                      Or can be an array of values
114 22
     *
115
     * @return array<mixed>|float|string Rounded Number, or a string containing an error
116 22
     *         If an array of numbers is passed as an argument, then the returned result will also be an array
117 5
     *            with the same dimensions
118
     */
119
    public static function precise(mixed $number, $significance = 1): array|string|float
120
    {
121 22
        if (is_array($number) || is_array($significance)) {
122 20
            return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $significance);
123 3
        }
124 3
125
        try {
126
            $number = Helpers::validateNumericNullBool($number);
127 19
            $significance = Helpers::validateNumericNullSubstitution($significance, null);
128 1
        } catch (Exception $e) {
129
            return $e->getMessage();
130 18
        }
131
132 18
        if (!$significance) {
133
            return 0.0;
134
        }
135
        $result = $number / abs($significance);
136
137
        return ceil($result) * $significance * (($significance < 0) ? -1 : 1);
138 21
    }
139
140 21
    /**
141
     * CEILING.ODS, pseudo-function - CEILING as implemented in ODS.
142
     *
143
     * ODS Function (theoretical):
144
     *        CEILING.ODS(number[,significance[,mode]])
145
     *
146 29
     * @param mixed $number Number to round
147
     * @param mixed $significance Significance
148 29
     * @param array<mixed>|int $mode direction to round negative numbers
149 3
     *
150
     * @return array<mixed>|float|string Rounded Number, or a string containing an error
151 26
     */
152 23
    public static function mathOds(mixed $number, mixed $significance = null, $mode = 0): array|string|float
153
    {
154
        return self::math($number, $significance, $mode, true);
155 3
    }
156
157
    /**
158 5
     * Let CEILINGMATH complexity pass Scrutinizer.
159
     */
160 5
    private static function ceilingMathTest(float $significance, float $number, int $mode): bool
161 5
    {
162 3
        return ($significance < 0) || ($number < 0 && !empty($mode));
163
    }
164
165
    /**
166
     * Avoid Scrutinizer problems concerning complexity.
167
     */
168
    private static function argumentsOk(float $number, float $significance): float|string
169
    {
170
        if (empty($number * $significance)) {
171
            return 0.0;
172
        }
173
        if (Helpers::returnSign($number) == Helpers::returnSign($significance)) {
174
            return ceil($number / $significance) * $significance;
175
        }
176
177
        return ExcelError::NAN();
178
    }
179
180
    private static function floorCheck1Arg(): void
181
    {
182
        $compatibility = Functions::getCompatibilityMode();
183
        if ($compatibility === Functions::COMPATIBILITY_EXCEL) {
184
            throw new Exception('Excel requires 2 arguments for CEILING');
185
        }
186
    }
187
}
188