Passed
Push — master ( 45ad8f...87d750 )
by Fabio
12:16 queued 07:28
created

TBitHelper::colorBitShift()   B

Complexity

Conditions 8
Paths 5

Size

Total Lines 22
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 8
eloc 17
c 1
b 0
f 0
nc 5
nop 3
dl 0
loc 22
rs 8.4444
1
<?php
2
/**
3
 * TBitHelper class file
4
 *
5
 * @author Brad Anderson <[email protected]>
6
 * @link https://github.com/pradosoft/prado
7
 * @license https://github.com/pradosoft/prado/blob/master/LICENSE
8
 */
9
10
namespace Prado\Util;
11
12
use Prado\Exceptions\TInvalidDataValueException;
13
14
/**
15
 * TBitHelper class.
16
 *
17
 * This class contains static functions for bit-wise and byte operations, like color
18
 * bit shifting, unsigned right bit shift, mirroring the order of bits, flipping
19
 * endian, and formatting floats into and from smaller float representations like
20
 * half floats (Fp16, Bf16) and mini floats (Fp8).  It also can check for negative
21
 * floats including negative zero.
22
 *
23
 * Shifting bits for color accuracy requires repeating the bits rather than
24
 * just adding extra 0/1 bits.  {@link colorBitShift} properly adds and removes bits
25
 * to an integer color value by replicating the bits for new bits.
26
 *
27
 * There are specific floating point conversion methods for converting float to:
28
 *   - Fp16 with {@link floatToFp16} and back with {@link fp16ToFloat}.
29
 *   - Bf16 with {@link floatToBf16} and back with {@link bf16ToFloat}.
30
 *   - Fp8-e5m2 with {@link floatToFp8Range} and back with {@link fp8RangeToFloat}.
31
 *   - Fp8-e4m3 with {@link floatToFp8Precision} and back with {@link fp8PrecisionToFloat}.
32
 * These functions use the general conversion functions {@link floatToFpXX} and
33
 * {@link fpXXToFloat} where the number of bits for the exponent and mantissa are
34
 * parameters. For example, 24 bit floats or 14 bit floats can be created.
35
 *
36
 * {@link mirrorBits} can mirror arbitrary runs of bits in an integer.  There is
37
 * quick mirroring for specific exponents of two: {@link mirrorByte} for 8 bits,
38
 * {@link mirrorShort} for 16 bits, {@link mirrorLong} for 32 bits, and, on 64 bit
39
 * instances of PHP, {@link mirrorLongLong} for 64 bits.
40
 *
41
 * There are endian byte reversal functions: {@link flipEndianShort}, {@link flipEndianLong},
42
 * and, on 64 bit instances of PHP, {@link flipEndianLongLong}.
43
 *
44
 * {@link bitCount} calculates the number of bits required to represent a specific
45
 * number. 255 return 8 bits, 256 returns 9 bits.
46
 *
47
 * {@link isNegativeFloat} is used to determine if a float has the negative bit
48
 * set.  It will return true on any negative float number, including negative zero.
49
 * {@link isNegativeZero} can check if a float is a negative zero.  PHP cannot normally
50
 * check for negative zero float and requires these special functions to so.
51
 *
52
 * The Levels and Masks are for O(1) time bit reversals of 8, 16, 32, and 64 bit integers.
53
 * The TBitHelper class automatically adjusts itself for 32 or 64 bit PHP environments.
54
 *
55
 * When quickly mirroring bits or switching endian, the high bits are also converted
56
 * like the low bits.  E.g. When mirroring a Byte, all bytes in the integer are
57
 * individually mirrored in place. When converting a Short, each short in the integer
58
 * will be converted in place. In the instance of a Long, for 64 bit systems will
59
 * convert both Longs -in place- in its LongLong (64 bit) unit integer type.
60
 * Converting LongLong is only supported in 64 bit PHP environments.
61
 *
62
 * @author Brad Anderson <[email protected]>
63
 * @since 4.2.3
64
 */
65
class TBitHelper
66
{
67
	public const Level1 = (PHP_INT_SIZE >= 8) ? 0x5555555555555555 : 0x55555555;
68
	public const NLevel1 = ~self::Level1;
69
	public const Mask1 = (PHP_INT_SIZE >= 8) ? 0x7FFFFFFFFFFFFFFF : 0x7FFFFFFF;
70
	public const Level2 = (PHP_INT_SIZE >= 8) ? 0x3333333333333333 : 0x33333333;
71
	public const NLevel2 = ~self::Level2;
72
	public const Mask2 = self::Mask1 >> 1;
73
	public const Level3 = (PHP_INT_SIZE >= 8) ? 0x0F0F0F0F0F0F0F0F : 0x0F0F0F0F;
74
	public const NLevel3 = ~self::Level3;
75
	public const Mask3 = self::Mask1 >> 3;
76
	public const Level4 = (PHP_INT_SIZE >= 8) ? 0x00FF00FF00FF00FF : 0x00FF00FF;
77
	public const NLevel4 = ~self::Level4;
78
	public const Mask4 = self::Mask1 >> 7;
79
	public const Level5 = (PHP_INT_SIZE >= 8) ? 0x0000FFFF0000FFFF : 0x0000FFFF;
80
	public const NLevel5 = ~self::Level5;
81
	public const Mask5 = self::Mask1 >> 15;
82
	public const Level6 = (PHP_INT_SIZE >= 8) ? 0x00000000FFFFFFFF : -1;
83
	public const NLevel6 = ~self::Level6;
84
	public const Mask6 = self::Mask1 >> 31;
85
86
	/**
87
	 * Motorola is Big Endian with the Most Significant Byte first whereas Intel uses
88
	 * Little Endian with the Least Significant Byte first.  This mainly only affects
89
	 * the binary reading and writing of data types that are 2 bytes or larger.
90
	 * @return bool Is the PHP environment in Big Endian Motorola Byte format.
91
	 */
92
	public static function isSystemBigEndian(): bool
93
	{
94
		static $bigEndian = null;
95
		if ($bigEndian === null) {
96
			$bigEndian = unpack('S', "\x00\x01")[1] === 1;
97
		}
98
		return $bigEndian;
99
	}
100
101
	/**
102
	 * @return bool Is the PHP environment 64 bit and supports the 64 bit LongLong type.
103
	 */
104
	public static function hasLongLong(): bool
105
	{
106
		return PHP_INT_SIZE >= 8;
107
	}
108
109
	/**
110
	 * This returns true with all negative floats, including -0.0.  Normally "$float < 0"
111
	 * will not include -0.0, where this function does include -0.0.
112
	 * @param float $value The float to check for being negative.
113
	 * @return bool Is a negative float.
114
	 */
115
	public static function isNegativeFloat(float $value): bool
116
	{
117
		return $value < 0 || $value === -0.0 && (ord(pack('G', $value)) & 0x80) !== 0;
118
	}
119
120
	/**
121
	 * This returns true with negative zero (-0.0).  Checking for negative zero floats
122
	 * requires this special function because PHP cannot be directly check for negative
123
	 * zero due to '-0.0 === 0.0'.
124
	 * @param float $value The float to check for being negative.
125
	 * @return bool Is a negative zero float.
126
	 */
127
	public static function isNegativeZero(float $value): bool
128
	{
129
		return $value === -0.0 && (ord(pack('G', $value)) & 0x80) !== 0;
130
	}
131
132
	/**
133
	 * Encodes a PHP float into an N-bit floating point number (in an integer) representation.
134
	 * This function can be configured with arbitrary number of Exponent Bits, Mantissa Bits,
135
	 * Exponent Bias, and IEEE Conformance (for subnormal numbers, INF, -INF, and NAN).
136
	 * The total number of floating point bits to be parsed is "$exponentBits + $mantissaBits + 1".
137
	 *
138
	 * With default parameter values, this functions as floatToFp16.
139
	 * @param float $value The PHP float to encode.
140
	 * @param int $exponentBits The number of bits used for the exponent, default: null for 5.
141
	 * @param int $mantissaBits The number of bits used for the mantissa, default: null for 10.
142
	 * @param null|int $exponentBias The bias to apply to the exponent. If null, it defaults to
143
	 *    half the maximum exponent value.  Default: null.
144
	 * @param bool $IEEEConformance Whether to follow the IEEE 754 standard for special values
145
	 *    (NaN, INF, -INF, and subnormal). Default true
146
	 * @throws TInvalidDataValueException on bad floating point configuration values.
147
	 * @return int The a short form float representation of the float $value.
148
	 */
149
	public static function floatToFpXX(float $value, ?int $exponentBits = null, ?int $mantissaBits = null, ?int $exponentBias = null, bool $IEEEConformance = true): int
150
	{
151
		$exponentBits = ($exponentBits === null) ? 5 : $exponentBits;
152
		$mantissaBits = ($mantissaBits === null) ? 10 : $mantissaBits;
153
		$exponentMaxValue = (1 << $exponentBits) - 1;
154
		$exponentBias = ($exponentBias === null) ? $exponentMaxValue >> 1 : $exponentBias;
155
		if ($exponentBits <= 0 || $mantissaBits <= 0 || ($exponentBits + $mantissaBits + 1) > PHP_INT_SIZE * 8 || $exponentBias < 0 || $exponentBias > $exponentMaxValue) {
156
			throw new TInvalidDataValueException('bithelper_bad_fp_format', $exponentBits, $mantissaBits, $exponentBias, PHP_INT_SIZE * 8);
157
		}
158
		$sign = self::isNegativeFloat($value) ? 1 : 0;
159
		$value = abs($value);
160
		$exponent = 0;
161
		$mantissa = 0;
162
163
		if ($IEEEConformance && is_nan($value)) {
164
			$exponent = $exponentMaxValue;
165
			$mantissa = (1 << $mantissaBits) - 1;
166
		} elseif ($IEEEConformance && (is_infinite($value) || $value >= pow(2, ($exponentMaxValue - 1) - $exponentBias) * (1 << $mantissaBits))) {
167
			$exponent = $exponentMaxValue;
168
		} elseif ($value == 0) {
169
			$mantissa = 0;
170
		} else {
171
			$exponent = floor(log($value, 2)) + $exponentBias;
172
			if ($exponent <= 0) {
173
				$mantissa = round($value / pow(2, 1 - $exponentBias - $mantissaBits));
174
				$exponent = 0;
175
			} elseif ($exponent >= $exponentMaxValue) {
176
				$exponent = $exponentMaxValue;
177
				$mantissa = 0;
178
			} else {
179
				$totalMantissaValues = (1 << $mantissaBits);
180
				$mantissa = round(($value / pow(2, $exponent - $exponentBias) - 1.0) * $totalMantissaValues);
181
				if ($mantissa === $totalMantissaValues) {
182
					$exponent++;
183
					$mantissa = 0;
184
				}
185
			}
186
		}
187
		$fpXX = ($sign << ($exponentBits + $mantissaBits)) | ($exponent << $mantissaBits) | $mantissa;
188
		return $fpXX;
189
	}
190
191
	/**
192
	 * This encodes a PHP float into a Fp16 (1 bit sign, 5 bits exponent, 10 bits mantissa) float.
193
	 * @param float $value The float to encode.
194
	 * @param null|int $exponentBias The bias to apply to the exponent. If null, it defaults to
195
	 *    half the maximum exponent value.  Default: null.
196
	 * @return int The encoded 2 byte Fp16 float.
197
	 */
198
	public static function floatToFp16(float $value, ?int $exponentBias = null): int
199
	{
200
		return self::floatToFpXX($value, 5, 10, $exponentBias);
201
	}
202
203
	/**
204
	 * This encodes a PHP float into a Bf16 (1 bit sign, 8 bits exponent, 7 bits mantissa)
205
	 * float.  This preserves the range of typical 4 byte floats but drops 2 bytes of
206
	 * precision from 23 bits to 7 bits.
207
	 * @param float $value The float to encode.
208
	 * @param null|int $exponentBias The bias to apply to the exponent. If null, it defaults to
209
	 *    half the maximum exponent value.  Default: null.
210
	 * @return int The encoded 2 byte Bf16 float.
211
	 */
212
	public static function floatToBf16(float $value, ?int $exponentBias = null): int
213
	{
214
		return self::floatToFpXX($value, 8, 7, $exponentBias);
215
	}
216
217
	/**
218
	 * This encodes a PHP float into an FP8 (1 bit sign, 5 bits exponent, 2 bits mantissa) float.
219
	 * The FP8 E5M2 format is for lower precision and higher range.
220
	 * @param float $value The float to encode.
221
	 * @param null|int $exponentBias The bias to apply to the exponent. If null, it defaults to
222
	 *    half the maximum exponent value.  Default: null.
223
	 * @return int The encoded 1 byte FP8-E5M2 float.
224
	 */
225
	public static function floatToFp8Range(float $value, ?int $exponentBias = null): int
226
	{
227
		return self::floatToFpXX($value, 5, 2, $exponentBias);
228
	}
229
230
	/**
231
	 * This encodes a PHP float into an FP8 (1 bit sign, 4 bits exponent, 3 bits mantissa) float.
232
	 * The FP8 E4M3 format is for higher precision and lower range.
233
	 * @param float $value The float to encode.
234
	 * @param null|int $exponentBias The bias to apply to the exponent. If null, it defaults to
235
	 *    half the maximum exponent value.  Default: null.
236
	 * @return int The encoded 1 byte FP8-E4M3 float.
237
	 */
238
	public static function floatToFp8Precision(float $value, ?int $exponentBias = null): int
239
	{
240
		return self::floatToFpXX($value, 4, 3, $exponentBias);
241
	}
242
243
	/**
244
	 * Decodes an N-bit floating point encoded as an integer to a PHP floating-point number.
245
	 * This function can be configured with arbitrary number of Exponent Bits, Mantissa Bits,
246
	 * Exponent Bias, and IEEE Conformance (for subnormal numbers, INF, -INF, and NAN).
247
	 * The total number of floating point bits to be parsed is "$exponentBits + $mantissaBits + 1".
248
	 *
249
	 * With default parameter values, this functions as fp16ToFloat.
250
	 * @param int $fpXX The encoded N-bit floating point number.
251
	 * @param int $exponentBits The number of bits used for the exponent, default: null for 5.
252
	 * @param int $mantissaBits The number of bits used for the mantissa, default: null for 10.
253
	 * @param null|int $exponentBias The bias to apply to the exponent. If null, it defaults to
254
	 *    half the maximum exponent value.  Default: null.
255
	 * @param bool $IEEEConformance Whether to follow the IEEE 754 standard for special values
256
	 *    (NaN, INF, -INF, and subnormal). Default true
257
	 * @throws TInvalidDataValueException on bad floating point configuration values.
258
	 * @return float The PHP float of the encoded $fpXX float.
259
	 */
260
	public static function fpXXToFloat(int $fpXX, ?int $exponentBits = null, ?int $mantissaBits = null, ?int $exponentBias = null, bool $IEEEConformance = true): float
261
	{
262
		$exponentBits = ($exponentBits === null) ? 5 : $exponentBits;
263
		$mantissaBits = ($mantissaBits === null) ? 10 : $mantissaBits;
264
		$exponentMaxValue = (1 << $exponentBits) - 1;
265
		if ($exponentBits <= 0 || $mantissaBits <= 0 || ($exponentBits + $mantissaBits + 1) > PHP_INT_SIZE * 8 ||
266
			($exponentBias !== null && ($exponentBias < 0 || $exponentBias > $exponentMaxValue))) {
267
			throw new TInvalidDataValueException('bithelper_bad_fp_format', $exponentBits, $mantissaBits, $exponentBias, PHP_INT_SIZE * 8);
268
		}
269
		$exponentBias = ($exponentBias === null) ? $exponentMaxValue >> 1 : $exponentBias;
270
		$sign = ($fpXX >> ($exponentBits + $mantissaBits)) & 0x1;
271
		$exponent = ($fpXX >> $mantissaBits) & $exponentMaxValue;
272
		$mantissa = $fpXX & ((1 << $mantissaBits) - 1);
273
		if ($IEEEConformance && $exponent == 0) { // subnormal numbers.
274
			$value = $mantissa * pow(2, 1 - $exponentBias - $mantissaBits);
275
		} elseif ($IEEEConformance && $exponent == $exponentMaxValue) {
276
			$value = ($mantissa == 0) ? INF : NAN;
277
		} else {
278
			$value = pow(2, $exponent - $exponentBias) * (1.0 + ($mantissa / (1 << $mantissaBits)));
279
		}
280
		if ($sign) {
281
			$value = -$value;
282
		}
283
		return $value;
284
	}
285
286
	/**
287
	 * This decodes a Fp16 (5 bits exponent, 10 bits mantissa) encoded float into a PHP Float.
288
	 * @param int $fp16 the Fp16 encoded float.
289
	 * @param null|int $exponentBias The bias to apply to the exponent. If null, it defaults to
290
	 *    half the maximum exponent value.  Default: null.
291
	 * @return float The Fp16 float decoded as a PHP float.
292
	 */
293
	public static function fp16ToFloat(int $fp16, ?int $exponentBias = null): float
294
	{
295
		return self::fpXXToFloat($fp16, 5, 10, $exponentBias);
296
	}
297
298
	/**
299
	 * This decodes a Bf16 (8 bits exponent, 7 bits mantissa) encoded float into a PHP
300
	 * Float.
301
	 * @param int $bf16 the BF16 encoded float.
302
	 * @param null|int $exponentBias The bias to apply to the exponent. If null, it defaults to
303
	 *    half the maximum exponent value.  Default: null.
304
	 * @return float The Bf16 float decoded as a PHP float.
305
	 */
306
	public static function bf16ToFloat(int $bf16, ?int $exponentBias = null): float
307
	{
308
		return self::fpXXToFloat($bf16, 8, 7, $exponentBias);
309
	}
310
311
	/**
312
	 * This decodes a FP8 (5 bits exponent, 2 bits mantissa) encoded float into a PHP Float.
313
	 * @param int $fp8 the FP8-E5M2 encoded float.
314
	 * @param null|int $exponentBias The bias to apply to the exponent. If null, it defaults to
315
	 *    half the maximum exponent value.  Default: null.
316
	 * @return float The FP8-E5M2 float decoded as a PHP float.
317
	 */
318
	public static function fp8RangeToFloat(int $fp8, ?int $exponentBias = null): float
319
	{
320
		return self::fpXXToFloat($fp8, 5, 2, $exponentBias);
321
	}
322
323
	/**
324
	 * This decodes a FP8 (4 bits exponent, 3 bits mantissa) encoded float into a PHP Float.
325
	 * @param int $fp8 the FP8-E4M3 encoded float.
326
	 * @param null|int $exponentBias The bias to apply to the exponent. If null, it defaults to
327
	 *    half the maximum exponent value.  Default: null.
328
	 * @return float The FP8-E4M3 float decoded as a PHP float.
329
	 */
330
	public static function fp8PrecisionToFloat(int $fp8, ?int $exponentBias = null): float
331
	{
332
		return self::fpXXToFloat($fp8, 4, 3, $exponentBias);
333
	}
334
335
	/**
336
	 * This calculates the number of bits required to represent a given number.
337
	 * eg. If there are 256 colors, then the maximum representable number in 8 bits
338
	 * is 255.  A $value of 255 returns 8 bits, and 256 returns 9 bits, to represent
339
	 * the number.
340
	 * @param int $value The number to calculate the bits required to represent it.
341
	 * @return int The number of bits required to represent $n
342
	 */
343
	public static function bitCount(int $value): int
344
	{
345
		if ($value === 0) {
346
			return 0;
347
		} elseif ($value < 0) {	// Negative numbers need one more bit.
348
			$value = (-$value) << 1;
349
		}
350
		if ($value < 0) {
351
			return PHP_INT_SIZE * 8;
352
		}
353
		return (int) ceil(log($value + 1, 2));
354
	}
355
356
	/**
357
	 * This method shifts color bits.  When removing bits, they are simply dropped.
358
	 * When adding bits, it replicates the existing bits for new bits to create the
359
	 * most accurate higher bit representation of the color.
360
	 * @param int $value The color value to expand or contract bits.
361
	 * @param int $inBits The number of bits of the input value.
362
	 * @param int $outBits The number of bits of the output value.
363
	 * @return int The $value shifted to $outBits in size.
364
	 * @throw TInvalidDataValueException when the $inBits or $outBits are less than
365
	 *   1 or greater than the Max Int Size for this PHP implementation.
366
	 */
367
	public static function colorBitShift(int $value, int $inBits, int $outBits): int
368
	{
369
		if ($inBits < 1 || $inBits > PHP_INT_SIZE * 8) {
370
			throw new TInvalidDataValueException("bithelper_invalid_color_in", $inBits);
371
		}
372
		if ($outBits < 1 || $outBits > PHP_INT_SIZE * 8) {
373
			throw new TInvalidDataValueException("bithelper_invalid_color_out", $outBits);
374
		}
375
		$dif = $outBits - $inBits;
376
		if ($dif > 0) {
377
			$return = $value;
378
			do {
379
				$dd = min($inBits, $dif);
380
				$return = ($return << $dd) | ($value >> ($inBits - $dd));
381
				$dif -= $dd;
382
			} while ($dif > 0);
383
			return $return;
384
		} elseif ($dif < 0) {
385
			$dif = -$dif;
386
			return ($value >> $dif) & (PHP_INT_MAX >> ($dif - 1));
387
		}
388
		return $value;
389
	}
390
391
	/**
392
	 * This does a right bit shift but the signed bit is not replicated in the high
393
	 * bit (with a bit-and).
394
	 * In normal PHP right bit shift, the signed bit is what make up any new bit in
395
	 * the shift.
396
	 * @param int $value The integer to bit shift.
397
	 * @param int $bits How much to shift the bits right.  Positive is right shift,
398
	 *   Negative is left shift.
399
	 * @return int The shifted integer without the high bit repeating.
400
	 */
401
	public static function unsignedShift(int $value, int $bits): int
402
	{
403
		if ($bits > 0) {
404
			return ($value >> $bits) & (PHP_INT_MAX >> ($bits - 1));
405
		} elseif ($bits < 0) {
406
			return $value << -$bits;
407
		} else {
408
			return $value;
409
		}
410
	}
411
412
	/**
413
	 * This mirrors $nbit bits from $value. For example, 0b100 becomes 0b001 @ $nbit = 3
414
	 * and 0x0100 become 0x0010 @ $nbit = 4.
415
	 * @param int $value The bits to reverse.
416
	 * @param int $nbit The number of bits to reverse.
417
	 * @throws TInvalidDataValueException when $nbits is over the maximum size of a PHP int.
418
	 * @return int reversed bits of $value.
419
	 */
420
	public static function mirrorBits(int $value, int $nbit): int
421
	{
422
		if ($nbit > PHP_INT_SIZE * 8) {
423
			throw new TInvalidDataValueException('bithelper_bad_mirror_bits', $nbit, PHP_INT_SIZE * 8);
424
		}
425
		for ($i = 0, $result = 0; $i < $nbit; $i++) {
426
			$result <<= 1;
427
			$result |= $value & 1;
428
			$value >>= 1;
429
		}
430
		return $result;
431
	}
432
433
	/**
434
	 * This quickly mirrors the 8 bits in each byte of $n.
435
	 * @param int $n The integer to mirror the bits of each byte.
436
	 * @return int reversed 8 bits of $n.
437
	 */
438
	public static function mirrorByte(int $n): int
439
	{
440
		$n = ((($n & self::NLevel1) >> 1) & self::Mask1) | (($n & self::Level1) << 1);
441
		$n = ((($n & self::NLevel2) >> 2) & self::Mask2) | (($n & self::Level2) << 2);
442
		return ((($n & self::NLevel3) >> 4) & self::Mask3) | (($n & self::Level3) << 4);
443
	}
444
445
	/**
446
	 * This quickly mirrors the 16 bits in each [2 byte] short of $n.
447
	 * @param int $n The integer to mirror the bits of each short.
448
	 * @return int reversed 16 bits of $n.
449
	 */
450
	public static function mirrorShort(int $n): int
451
	{
452
		$n = ((($n & self::NLevel1) >> 1) & self::Mask1) | (($n & self::Level1) << 1);
453
		$n = ((($n & self::NLevel2) >> 2) & self::Mask2) | (($n & self::Level2) << 2);
454
		$n = ((($n & self::NLevel3) >> 4) & self::Mask3) | (($n & self::Level3) << 4);
455
		return ((($n & self::NLevel4) >> 8) & self::Mask4) | (($n & self::Level4) << 8);
456
457
	}
458
459
	/**
460
	 * This quickly mirrors the 32 bits in each [4 byte] long of $n.
461
	 * @param int $n The integer to mirror the bits of each long.
462
	 * @return int reversed 32 bits of $n.
463
	 */
464
	public static function mirrorLong(int $n): int
465
	{
466
		$n = ((($n & self::NLevel1) >> 1) & self::Mask1) | (($n & self::Level1) << 1);
467
		$n = ((($n & self::NLevel2) >> 2) & self::Mask2) | (($n & self::Level2) << 2);
468
		$n = ((($n & self::NLevel3) >> 4) & self::Mask3) | (($n & self::Level3) << 4);
469
		$n = ((($n & self::NLevel4) >> 8) & self::Mask4) | (($n & self::Level4) << 8);
470
		return ((($n & self::NLevel5) >> 16) & self::Mask5) | (($n & self::Level5) << 16);
471
	}
472
473
	/**
474
	 * This quickly mirrors the 64 bits of $n.  This only works with 64 bit PHP systems.
475
	 * For speed, there is no check to validate that the system is 64 bit PHP.  You
476
	 * must do the validation if/when needed with method {@link hasLongLong}.
477
	 * @param int $n The 8 byte integer to mirror the bits of.
478
	 * @return int reversed 64 bits of $n.
479
	 */
480
	public static function mirrorLongLong(int $n): int
481
	{
482
		$n = ((($n & self::NLevel1) >> 1) & self::Mask1) | (($n & self::Level1) << 1);
483
		$n = ((($n & self::NLevel2) >> 2) & self::Mask2) | (($n & self::Level2) << 2);
484
		$n = ((($n & self::NLevel3) >> 4) & self::Mask3) | (($n & self::Level3) << 4);
485
		$n = ((($n & self::NLevel4) >> 8) & self::Mask4) | (($n & self::Level4) << 8);
486
		$n = ((($n & self::NLevel5) >> 16) & self::Mask5) | (($n & self::Level5) << 16);
487
		return ((($n & self::NLevel6) >> 32) & self::Mask6) | (($n & self::Level6) << 32);
488
	}
489
490
	/**
491
	 * This quickly flips the endian in each [2 byte] short of $n.
492
	 * @param int $n The 2 byte short to reverse the endian.
493
	 * @return int reversed endian of $n.
494
	 */
495
	public static function flipEndianShort(int $n): int
496
	{
497
		return ((($n & self::NLevel4) >> 8) & self::Mask4) | (($n & self::Level4) << 8);
498
	}
499
500
	/**
501
	 * This quickly flips the endian in each [4 byte] long of $n.
502
	 * @param int $n The 4 byte long to reverse the endian.
503
	 * @return int The reversed endian of $n.
504
	 */
505
	public static function flipEndianLong(int $n): int
506
	{
507
		$n = ((($n & self::NLevel4) >> 8) & self::Mask4) | (($n & self::Level4) << 8);
508
		return ((($n & self::NLevel5) >> 16) & self::Mask5) | (($n & self::Level5) << 16);
509
	}
510
511
	/**
512
	 * This quickly fligs the  endian of an 8 byte integer.  This only works with 64
513
	 * bit PHP systems. 32 bit systems will treat the bit field as floats and invariably
514
	 * fail.
515
	 *
516
	 * For speed, there is no check to validate that the system is 64 bit PHP.  You
517
	 * must do the validation if/when needed with method {@link hasLongLong}.
518
	 * @param int $n The 8 byte long long to reverse the endian.
519
	 * @return int reversed 8 bytes endian of $n.
520
	 */
521
	public static function flipEndianLongLong(int $n): int
522
	{
523
		$n = ((($n & self::NLevel4) >> 8) & self::Mask4) | (($n & self::Level4) << 8);
524
		$n = ((($n & self::NLevel5) >> 16) & self::Mask5) | (($n & self::Level5) << 16);
525
		return ((($n & self::NLevel6) >> 32) & self::Mask6) | (($n & self::Level6) << 32);
526
	}
527
}
528