Passed
Push — master ( ebdd21...08d0fa )
by Fabio
05:21
created

TBitHelper::floatToBf16()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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