Passed
Push — master ( 2d1f4e...e6aacf )
by
unknown
26:57 queued 19:21
created

Ceiling::argumentsOk()   B

Complexity

Conditions 7
Paths 3

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 7

Importance

Changes 0
Metric Value
eloc 9
c 0
b 0
f 0
dl 0
loc 15
ccs 9
cts 9
cp 1
rs 8.8333
cc 7
nc 3
nop 2
crap 7
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 39
    public static function ceiling($number, $significance = null)
35
    {
36 39
        if (is_array($number) || is_array($significance)) {
37 5
            return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $significance);
38
        }
39
40 39
        if ($significance === null) {
41 5
            self::floorCheck1Arg();
42
        }
43
44
        try {
45 36
            $number = Helpers::validateNumericNullBool($number);
46 34
            $significance = Helpers::validateNumericNullSubstitution($significance, ($number < 0) ? -1 : 1);
47 3
        } catch (Exception $e) {
48 3
            return $e->getMessage();
49
        }
50
51 33
        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 30
    public static function math(mixed $number, mixed $significance = null, $mode = 0, bool $checkSigns = false): array|string|float
74
    {
75 30
        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 30
            $number = Helpers::validateNumericNullBool($number);
81 28
            $significance = Helpers::validateNumericNullSubstitution($significance, ($number < 0) ? -1 : 1);
82 28
            $mode = Helpers::validateNumericNullSubstitution($mode, null);
83 2
        } catch (Exception $e) {
84 2
            return $e->getMessage();
85
        }
86
87 28
        if (empty($significance * $number)) {
88 5
            return 0.0;
89
        }
90 23
        if ($checkSigns) {
91 2
            if (($number > 0 && $significance < 0) || ($number < 0 && $significance > 0)) {
92 2
                return ExcelError::NAN();
93
            }
94
        }
95 23
        if (self::ceilingMathTest((float) $significance, (float) $number, (int) $mode)) {
96 6
            return floor($number / $significance) * $significance;
97
        }
98
99 17
        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
     *
115
     * @return array<mixed>|float|string Rounded Number, or a string containing an error
116
     *         If an array of numbers is passed as an argument, then the returned result will also be an array
117
     *            with the same dimensions
118
     */
119 24
    public static function precise(mixed $number, $significance = 1): array|string|float
120
    {
121 24
        if (is_array($number) || is_array($significance)) {
122 5
            return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $significance);
123
        }
124
125
        try {
126 24
            $number = Helpers::validateNumericNullBool($number);
127 22
            $significance = Helpers::validateNumericNullSubstitution($significance, null);
128 3
        } catch (Exception $e) {
129 3
            return $e->getMessage();
130
        }
131
132 21
        if (!$significance) {
133 1
            return 0.0;
134
        }
135 20
        $result = $number / abs($significance);
136
137 20
        return ceil($result) * $significance * (($significance < 0) ? -1 : 1);
138
    }
139
140
    /**
141
     * CEILING.ODS, pseudo-function - CEILING as implemented in ODS.
142
     *
143
     * ODS Function (theoretical):
144
     *        CEILING.ODS(number[,significance[,mode]])
145
     *
146
     * @param mixed $number Number to round
147
     * @param mixed $significance Significance
148
     * @param array<mixed>|int $mode direction to round negative numbers
149
     *
150
     * @return array<mixed>|float|string Rounded Number, or a string containing an error
151
     */
152 2
    public static function mathOds(mixed $number, mixed $significance = null, $mode = 0): array|string|float
153
    {
154 2
        return self::math($number, $significance, $mode, true);
155
    }
156
157
    /**
158
     * Let CEILINGMATH complexity pass Scrutinizer.
159
     */
160 23
    private static function ceilingMathTest(float $significance, float $number, int $mode): bool
161
    {
162 23
        return ($significance < 0) || ($number < 0 && !empty($mode));
163
    }
164
165
    /**
166
     * Avoid Scrutinizer problems concerning complexity.
167
     */
168 33
    private static function argumentsOk(float $number, float $significance): float|string
169
    {
170 33
        if (empty($number * $significance)) {
171 3
            return 0.0;
172
        }
173 30
        $signSig = Helpers::returnSign($significance);
174 30
        $signNum = Helpers::returnSign($number);
175
        if (
176 30
            ($signSig === 1 && ($signNum === 1 || Functions::getCompatibilityMode() !== Functions::COMPATIBILITY_GNUMERIC))
177 30
            || ($signSig === -1 && $signNum === -1)
178
        ) {
179 29
            return ceil($number / $significance) * $significance;
180
        }
181
182 2
        return ExcelError::NAN();
183
    }
184
185 5
    private static function floorCheck1Arg(): void
186
    {
187 5
        $compatibility = Functions::getCompatibilityMode();
188 5
        if ($compatibility === Functions::COMPATIBILITY_EXCEL) {
189 3
            throw new Exception('Excel requires 2 arguments for CEILING');
190
        }
191
    }
192
}
193