Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1441 ariadna 1
<?php
2
 
3
namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering;
4
 
5
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
6
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
7
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
8
 
9
class BitWise
10
{
11
    use ArrayEnabled;
12
 
13
    const SPLIT_DIVISOR = 2 ** 24;
14
 
15
    /**
16
     * Split a number into upper and lower portions for full 32-bit support.
17
     *
18
     * @return int[]
19
     */
20
    private static function splitNumber(float|int $number): array
21
    {
22
        return [(int) floor($number / self::SPLIT_DIVISOR), (int) fmod($number, self::SPLIT_DIVISOR)];
23
    }
24
 
25
    /**
26
     * BITAND.
27
     *
28
     * Returns the bitwise AND of two integer values.
29
     *
30
     * Excel Function:
31
     *        BITAND(number1, number2)
32
     *
33
     * @param null|array|bool|float|int|string $number1 Or can be an array of values
34
     * @param null|array|bool|float|int|string $number2 Or can be an array of values
35
     *
36
     * @return array|int|string If an array of numbers is passed as an argument, then the returned result will also be an array
37
     *            with the same dimensions
38
     */
39
    public static function BITAND(null|array|bool|float|int|string $number1, null|array|bool|float|int|string $number2): array|string|int|float
40
    {
41
        if (is_array($number1) || is_array($number2)) {
42
            return self::evaluateArrayArguments([self::class, __FUNCTION__], $number1, $number2);
43
        }
44
 
45
        try {
46
            $number1 = self::validateBitwiseArgument($number1);
47
            $number2 = self::validateBitwiseArgument($number2);
48
        } catch (Exception $e) {
49
            return $e->getMessage();
50
        }
51
        $split1 = self::splitNumber($number1);
52
        $split2 = self::splitNumber($number2);
53
 
54
        return self::SPLIT_DIVISOR * ($split1[0] & $split2[0]) + ($split1[1] & $split2[1]);
55
    }
56
 
57
    /**
58
     * BITOR.
59
     *
60
     * Returns the bitwise OR of two integer values.
61
     *
62
     * Excel Function:
63
     *        BITOR(number1, number2)
64
     *
65
     * @param null|array|bool|float|int|string $number1 Or can be an array of values
66
     * @param null|array|bool|float|int|string $number2 Or can be an array of values
67
     *
68
     * @return array|int|string If an array of numbers is passed as an argument, then the returned result will also be an array
69
     *            with the same dimensions
70
     */
71
    public static function BITOR(null|array|bool|float|int|string $number1, null|array|bool|float|int|string $number2): array|string|int|float
72
    {
73
        if (is_array($number1) || is_array($number2)) {
74
            return self::evaluateArrayArguments([self::class, __FUNCTION__], $number1, $number2);
75
        }
76
 
77
        try {
78
            $number1 = self::validateBitwiseArgument($number1);
79
            $number2 = self::validateBitwiseArgument($number2);
80
        } catch (Exception $e) {
81
            return $e->getMessage();
82
        }
83
 
84
        $split1 = self::splitNumber($number1);
85
        $split2 = self::splitNumber($number2);
86
 
87
        return self::SPLIT_DIVISOR * ($split1[0] | $split2[0]) + ($split1[1] | $split2[1]);
88
    }
89
 
90
    /**
91
     * BITXOR.
92
     *
93
     * Returns the bitwise XOR of two integer values.
94
     *
95
     * Excel Function:
96
     *        BITXOR(number1, number2)
97
     *
98
     * @param null|array|bool|float|int|string $number1 Or can be an array of values
99
     * @param null|array|bool|float|int|string $number2 Or can be an array of values
100
     *
101
     * @return array|int|string If an array of numbers is passed as an argument, then the returned result will also be an array
102
     *            with the same dimensions
103
     */
104
    public static function BITXOR(null|array|bool|float|int|string $number1, null|array|bool|float|int|string $number2): array|string|int|float
105
    {
106
        if (is_array($number1) || is_array($number2)) {
107
            return self::evaluateArrayArguments([self::class, __FUNCTION__], $number1, $number2);
108
        }
109
 
110
        try {
111
            $number1 = self::validateBitwiseArgument($number1);
112
            $number2 = self::validateBitwiseArgument($number2);
113
        } catch (Exception $e) {
114
            return $e->getMessage();
115
        }
116
 
117
        $split1 = self::splitNumber($number1);
118
        $split2 = self::splitNumber($number2);
119
 
120
        return self::SPLIT_DIVISOR * ($split1[0] ^ $split2[0]) + ($split1[1] ^ $split2[1]);
121
    }
122
 
123
    /**
124
     * BITLSHIFT.
125
     *
126
     * Returns the number value shifted left by shift_amount bits.
127
     *
128
     * Excel Function:
129
     *        BITLSHIFT(number, shift_amount)
130
     *
131
     * @param null|array|bool|float|int|string $number Or can be an array of values
132
     * @param null|array|bool|float|int|string $shiftAmount Or can be an array of values
133
     *
134
     * @return array|float|string If an array of numbers is passed as an argument, then the returned result will also be an array
135
     *            with the same dimensions
136
     */
137
    public static function BITLSHIFT(null|array|bool|float|int|string $number, null|array|bool|float|int|string $shiftAmount): array|string|float
138
    {
139
        if (is_array($number) || is_array($shiftAmount)) {
140
            return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $shiftAmount);
141
        }
142
 
143
        try {
144
            $number = self::validateBitwiseArgument($number);
145
            $shiftAmount = self::validateShiftAmount($shiftAmount);
146
        } catch (Exception $e) {
147
            return $e->getMessage();
148
        }
149
 
150
        $result = floor($number * (2 ** $shiftAmount));
151
        if ($result > 2 ** 48 - 1) {
152
            return ExcelError::NAN();
153
        }
154
 
155
        return $result;
156
    }
157
 
158
    /**
159
     * BITRSHIFT.
160
     *
161
     * Returns the number value shifted right by shift_amount bits.
162
     *
163
     * Excel Function:
164
     *        BITRSHIFT(number, shift_amount)
165
     *
166
     * @param null|array|bool|float|int|string $number Or can be an array of values
167
     * @param null|array|bool|float|int|string $shiftAmount Or can be an array of values
168
     *
169
     * @return array|float|string If an array of numbers is passed as an argument, then the returned result will also be an array
170
     *            with the same dimensions
171
     */
172
    public static function BITRSHIFT(null|array|bool|float|int|string $number, null|array|bool|float|int|string $shiftAmount): array|string|float
173
    {
174
        if (is_array($number) || is_array($shiftAmount)) {
175
            return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $shiftAmount);
176
        }
177
 
178
        try {
179
            $number = self::validateBitwiseArgument($number);
180
            $shiftAmount = self::validateShiftAmount($shiftAmount);
181
        } catch (Exception $e) {
182
            return $e->getMessage();
183
        }
184
 
185
        $result = floor($number / (2 ** $shiftAmount));
186
        if ($result > 2 ** 48 - 1) { // possible because shiftAmount can be negative
187
            return ExcelError::NAN();
188
        }
189
 
190
        return $result;
191
    }
192
 
193
    /**
194
     * Validate arguments passed to the bitwise functions.
195
     */
196
    private static function validateBitwiseArgument(mixed $value): float
197
    {
198
        $value = self::nullFalseTrueToNumber($value);
199
 
200
        if (is_numeric($value)) {
201
            $value = (float) $value;
202
            if ($value == floor($value)) {
203
                if (($value > 2 ** 48 - 1) || ($value < 0)) {
204
                    throw new Exception(ExcelError::NAN());
205
                }
206
 
207
                return floor($value);
208
            }
209
 
210
            throw new Exception(ExcelError::NAN());
211
        }
212
 
213
        throw new Exception(ExcelError::VALUE());
214
    }
215
 
216
    /**
217
     * Validate arguments passed to the bitwise functions.
218
     */
219
    private static function validateShiftAmount(mixed $value): int
220
    {
221
        $value = self::nullFalseTrueToNumber($value);
222
 
223
        if (is_numeric($value)) {
224
            if (abs($value + 0) > 53) {
225
                throw new Exception(ExcelError::NAN());
226
            }
227
 
228
            return (int) $value;
229
        }
230
 
231
        throw new Exception(ExcelError::VALUE());
232
    }
233
 
234
    /**
235
     * Many functions accept null/false/true argument treated as 0/0/1.
236
     */
237
    private static function nullFalseTrueToNumber(mixed &$number): mixed
238
    {
239
        if ($number === null) {
240
            $number = 0;
241
        } elseif (is_bool($number)) {
242
            $number = (int) $number;
243
        }
244
 
245
        return $number;
246
    }
247
}