Passed
Pull Request — master (#4468)
by Owen
10:02
created

Ceiling::ceiling()   A

Complexity

Conditions 6
Paths 7

Size

Total Lines 18
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 6

Importance

Changes 0
Metric Value
cc 6
eloc 10
nc 7
nop 2
dl 0
loc 18
ccs 10
cts 10
cp 1
crap 6
rs 9.2222
c 0
b 0
f 0
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|float $number the number you want the ceiling
26
     *                      Or can be an array of values
27
     * @param array|float $significance the multiple to which you want to round
28
     *                      Or can be an array of values
29
     *
30
     * @return array|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|int $mode direction to round negative numbers
67
     *                      Or can be an array of values
68
     *
69
     * @return array|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): 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 (self::ceilingMathTest((float) $significance, (float) $number, (int) $mode)) {
91 6
            return floor($number / $significance) * $significance;
92
        }
93
94 15
        return ceil($number / $significance) * $significance;
95
    }
96
97
    /**
98
     * CEILING.PRECISE.
99
     *
100
     * Rounds number up, away from zero, to the nearest multiple of significance.
101
     *
102
     * Excel Function:
103
     *        CEILING.PRECISE(number[,significance])
104
     *
105
     * @param mixed $number the number you want to round
106
     *                      Or can be an array of values
107
     * @param array|float $significance the multiple to which you want to round
108
     *                      Or can be an array of values
109
     *
110
     * @return array|float|string Rounded Number, or a string containing an error
111
     *         If an array of numbers is passed as an argument, then the returned result will also be an array
112
     *            with the same dimensions
113
     */
114 22
    public static function precise(mixed $number, $significance = 1): array|string|float
115
    {
116 22
        if (is_array($number) || is_array($significance)) {
117 5
            return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $significance);
118
        }
119
120
        try {
121 22
            $number = Helpers::validateNumericNullBool($number);
122 20
            $significance = Helpers::validateNumericNullSubstitution($significance, null);
123 3
        } catch (Exception $e) {
124 3
            return $e->getMessage();
125
        }
126
127 19
        if (!$significance) {
128 1
            return 0.0;
129
        }
130 18
        $result = $number / abs($significance);
131
132 18
        return ceil($result) * $significance * (($significance < 0) ? -1 : 1);
133
    }
134
135
    /**
136
     * Let CEILINGMATH complexity pass Scrutinizer.
137
     */
138 21
    private static function ceilingMathTest(float $significance, float $number, int $mode): bool
139
    {
140 21
        return ($significance < 0) || ($number < 0 && !empty($mode));
141
    }
142
143
    /**
144
     * Avoid Scrutinizer problems concerning complexity.
145
     */
146 29
    private static function argumentsOk(float $number, float $significance): float|string
147
    {
148 29
        if (empty($number * $significance)) {
149 3
            return 0.0;
150
        }
151 26
        if (Helpers::returnSign($number) == Helpers::returnSign($significance)) {
152 23
            return ceil($number / $significance) * $significance;
153
        }
154
155 3
        return ExcelError::NAN();
156
    }
157
158 5
    private static function floorCheck1Arg(): void
159
    {
160 5
        $compatibility = Functions::getCompatibilityMode();
161 5
        if ($compatibility === Functions::COMPATIBILITY_EXCEL) {
162 3
            throw new Exception('Excel requires 2 arguments for CEILING');
163
        }
164
    }
165
}
166