-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathBitMethods.cs
More file actions
600 lines (507 loc) · 25 KB
/
Copy pathBitMethods.cs
File metadata and controls
600 lines (507 loc) · 25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
/* BIT MANIPULATION NOTES!!
Here are some notes for writing bit-manip stuff
In general you first create a mask, then then apply the desired operator between value and mask.
Setting bits can be done by using an OR operator
Unsetting bits can be done by using a combination of an AND and a NOT (By this i mean AND value with a mask where all bits are set except the one you want to unset)
Flipping/Inverting bits can be done by using an XOR operator
Bit selection can be done using an AND operator. Create mask with desired bits all set, then AND it with value
Counting number of bits set is identically equivalent to calculaing the Hamming weight of the binary string.
I dont know if there is a way of setting up the argument list to accept byte->ulong and sbyte->long, ive just put ulong as there is no issue ever occuring with its bit value.
*/
namespace BinaryMethods
{
//So basically im fine with this for unsigned integers. This all works well and is good
//Issue is i have no clue what to do for signed integers...
//If youre using signed ints, there are many issues that can occur
//For example if you take an Int32, XOR with 0x80_00_00_00 (ie top bit set), it expects long output as 0x80... is a long, NOT an int.
//So instead you have to not it (~)
//Might be worth a rethink, or equivalently just always use unsigned
public static class BitMethods
{
#region Count Bits
/// <summary>
/// Count the number of bits set to 1 in an unsigned integer.
/// </summary>
/// <param name="value">Value to be checked.</param>
/// <param name="maxBit">Highest bit to check. 0 indexed.</param>
/// <returns>Number of bits set.</returns>
public static byte CountBitsSet(this ulong value, int maxBit = 63)
{
//An explanation of how this works can be found in https://stackoverflow.com/questions/109023/how-to-count-the-number-of-set-bits-in-a-32-bit-integer
if (maxBit < 1 || maxBit > 63)
{
throw new ArgumentException("Invalid number of bits chosen. Value must be between 1 and 63.", "maxBit");
}
else if (maxBit != 63)
{
//If maxBit is not 63, then we only want to count the bits up to that bit. To do so, we basically select bits 0 to maxBit
//Could use method below but more efficient to just do the mask here ourselved (to avoid the if statements)
value &= AllBitsSet(maxBit);
}
ulong result = value - ((value >> 1) & 0x5555555555555555UL);
result = (result & 0x3333333333333333UL) + ((result >> 2) & 0x3333333333333333UL);
//unchecked means dont flag overflow!
return (byte)(unchecked(((result + (result >> 4)) & 0xF0F0F0F0F0F0F0FUL) * 0x101010101010101UL) >> 56);
}
/// <summary>
/// Count the number of bits set to 1 in a signed integer.
/// </summary>
/// <param name="value">Value to be checked.</param>
/// <param name="maxBit">Highest bit to check. 0 indexed.</param>
/// <returns>Number of bits set.</returns>
public static byte CountBitsSet(this long value, int maxBit = 63)
{
if (maxBit < 1 || maxBit > 63)
{
throw new ArgumentException("Invalid number of bits chosen. Value must be between 1 and 63.", "maxBit");
}
else if (maxBit != 63)
{
value &= AllBitsSetSigned(maxBit);
}
long result = value - ((value >> 1) & 0x5555555555555555L);
result = (result & 0x3333333333333333L) + ((result >> 2) & 0x3333333333333333L);
//unchecked means dont flag overflow!
return (byte)(unchecked(((result + (result >> 4)) & 0xF0F0F0F0F0F0F0FL) * 0x101010101010101L) >> 56);
}
#endregion
#region Reverse Bits
/// <summary>
/// Reverses all bits in an unsigned integer.
/// </summary>
/// <param name="value">Value to be reversed.</param>
/// <returns>Unsigned integer representing value with all bits reversed (note that reversed and flipped are NOT the same.)</returns>
public static ulong ReverseBits(this ulong value, int maxBit = 63)
{
if (maxBit < 1 || maxBit > 63)
{
throw new ArgumentException("Invalid number of bits chosen. Value must be between 1 and 63.", "maxBit");
}
ulong revNum = 0, temp;
for (int i = 0; i < maxBit; i++)
{
//Check ith bit. If its set, set the maxBit-i-1 th bit
temp = value & (1UL << i);
if (temp != 0)
{
revNum |= 1UL << (maxBit - i - 1);
}
}
return revNum;
}
/// <summary>
/// Reverses all bits in a signed integer.
/// </summary>
/// <param name="value">Value to be reversed.</param>
/// <returns>Unsigned integer representing value with all bits reversed (note that reversed and flipped are NOT the same.)</returns>
public static long ReverseBits(this long value, int maxBit = 63)
{
if (maxBit < 1 || maxBit > 63)
{
throw new ArgumentException("Invalid number of bits chosen. Value must be between 1 and 63.", "maxBit");
}
long revNum = 0, temp;
for (int i = 0; i < maxBit; i++)
{
//Check ith bit. If its set, set the maxBit-i-1 th bit
temp = value & (1L << i);
if (temp != 0)
{
revNum |= 1L << (maxBit - i - 1);
}
}
return revNum;
}
#endregion
#region Select Bits
/// <summary>
/// Select a number of bits from an unsigned integer.
/// </summary>
/// <param name="value">Value to select bits from.</param>
/// <param name="startBit">0 based index of the start bit.</param>
/// <param name="endBit">0 based index of the end bit. This is inclusive, ie this bit will be included.</param>
/// <returns>Unsigned integer containing selected bits, shifted such that startBit is bit 0.</returns>
public static ulong SelectBits(this ulong value, int startBit = 0, int endBit = 63)
{
if (endBit < 0 || endBit > 63)
{
throw new ArgumentException("Invalid number of bits chosen. Value must be between 0 and 63.", "endBit");
}
else if (startBit < 0 || startBit > 63)
{
throw new ArgumentException("Invalid number of bits chosen. Value must be between 0 and 63.", "startBit");
}
else if (endBit <= startBit)
{
throw new ArgumentException("End bit was less than or equal to start bit.", "endBit");
}
//Left shift by startBit, & with 0b111... where there are endBit-startBit 1s
//1 << n gives 0b100...00 wither bit n+1 is set. So subtract 1 to get n bits set.
ulong mask = AllBitsSet(endBit - startBit);
return (value >> startBit) & mask;
}
/// <summary>
/// Select a number of bits from a signed integer.
/// </summary>
/// <param name="value">Value to select bits from.</param>
/// <param name="startBit">0 based index of the start bit.</param>
/// <param name="endBit">0 based index of the end bit. This is inclusive, ie this bit will be included.</param>
/// <returns>Signed integer containing selected bits, shifted such that startBit is bit 0.</returns>
public static long SelectBits(this long value, int startBit = 0, int endBit = 63)
{
if (endBit < 0 || endBit > 63)
{
throw new ArgumentException("Invalid number of bits chosen. Value must be between 0 and 63.", "endBit");
}
else if (startBit < 0 || startBit > 63)
{
throw new ArgumentException("Invalid number of bits chosen. Value must be between 0 and 63.", "startBit");
}
else if (endBit <= startBit)
{
throw new ArgumentException("End bit was less than or equal to start bit.", "endBit");
}
//Left shift by startBit, & with 0b111... where there are endBit-startBit 1s
long mask = AllBitsSetSigned(endBit - startBit);
return (value >> startBit) & mask;
}
#endregion
#region All Bits Set
/// <summary>
/// Gets an unsigned integer value with all bits up to maxBit set (0 indexed).
/// </summary>
/// <param name="maxBit">Index of maximum bit to be set (0 indexed).</param>
/// <returns>Unsigned long integer with all bits upto maxBit set.</returns>
public static ulong AllBitsSet(int maxBit)
{
if (maxBit < 0 || maxBit > 63)
{
throw new ArgumentException("Invalid number of bits. Parameter numBits must be between 0 and 63.", "maxBit");
}
else if(maxBit == 63)
{
return ulong.MaxValue;
}
else
{
return (1UL << maxBit + 1) - 1;
}
}
/// <summary>
/// Gets a signed integer value with all bits up to maxBit set (0 indexed).
/// </summary>
/// <param name="maxBit">Index of maximum bit to be set (0 indexed).</param>
/// <returns>Signed long integer with all bits upto maxBit set.</returns>
public static long AllBitsSetSigned(int maxBit)
{
if (maxBit < 0 || maxBit > 63)
{
throw new ArgumentException("Invalid number of bits. Parameter numBits must be between 0 and 63.", "maxBit");
}
else if (maxBit == 63)
{
return -1;
}
else
{
return (1L << maxBit + 1) - 1;
}
}
#endregion
#region Set Bits
/// <summary>
/// Sets a selection of bits within an unsigned integer.
/// </summary>
/// <param name="value">Value to have bits set.</param>
/// <param name="bitsToSet">Selection of bits to set. Bit indicies are 0 indexed.</param>
/// <returns>Value with the chosen bits set (or equivalently set to be 1).</returns>
public static ulong SetBits(this ulong value, byte[] bitsToSet)
{
//TODO: check that all bitsToSet are unique and that they are between 0 and 63
foreach (byte bit in bitsToSet)
{
value |= (1UL << bit); //this sets the nth bit to be 1
}
return value;
}
/// <summary>
/// Sets a selection of bits within a signed integer.
/// </summary>
/// <param name="value">Value to have bits set.</param>
/// <param name="bitsToSet">Selection of bits to set. Bit indicies are 0 indexed.</param>
/// <returns>Value with the chosen bits set (or equivalently set to be 1).</returns>
public static long SetBits(this long value, byte[] bitsToSet)
{
foreach (byte bit in bitsToSet)
{
value |= (1L << bit); //this sets the nth bit to be 1
}
return value;
}
/// <summary>
/// Sets bits between start and end values within a given unsigned integer.
/// </summary>
/// <param name="value">Value to have bits set.</param>
/// <param name="startBit">0 based index of the start bit.</param>
/// <param name="endBit">0 based index of the end bit. This is inclusive, ie this bit will be included.</param>
/// <returns>Unsigned integer with the chosen bits set (or equivalently set to be 1).</returns>
public static ulong SetBits(this ulong value, int startBit = 0, int endBit = 63)
{
if (endBit < 0 || endBit > 63)
{
throw new ArgumentException("Invalid number of bits chosen. Value must be between 0 and 63.", "endBit");
}
else if (startBit < 0 || startBit > 63)
{
throw new ArgumentException("Invalid number of bits chosen. Value must be between 0 and 63.", "startBit");
}
else if (endBit <= startBit)
{
throw new ArgumentException("End bit was less than or equal to start bit.", "endBit");
}
ulong mask = AllBitsSet(endBit - startBit) << startBit;
return value | mask;
}
/// <summary>
/// Sets bits between start and end values within a given unsigned integer.
/// </summary>
/// <param name="value">Value to have bits set.</param>
/// <param name="startBit">0 based index of the start bit.</param>
/// <param name="endBit">0 based index of the end bit. This is inclusive, ie this bit will be included.</param>
/// <returns>Unsigned integer with the chosen bits set (or equivalently set to be 1).</returns>
public static long SetBits(this long value, int startBit = 0, int endBit = 63)
{
if (endBit < 0 || endBit > 63)
{
throw new ArgumentException("Invalid number of bits chosen. Value must be between 0 and 63.", "endBit");
}
else if (startBit < 0 || startBit > 63)
{
throw new ArgumentException("Invalid number of bits chosen. Value must be between 0 and 63.", "startBit");
}
else if (endBit <= startBit)
{
throw new ArgumentException("End bit was less than or equal to start bit.", "endBit");
}
long mask = AllBitsSetSigned(endBit - startBit) << startBit;
return value | mask;
}
#endregion
#region Unset Bits
/// <summary>
/// Unsets a selection of bits within an unsigned integer value.
/// </summary>
/// <param name="value">Unsigned integer to have bits set.</param>
/// <param name="bitsToSet">Selection of bits to unset. Bit indicies are 0 indexed.</param>
/// <returns>Unsigned integer with the chosen bits unset (or equivalently set to be 0).</returns>
public static ulong UnsetBits(this ulong value, byte[] bitsToSet)
{
//TODO: check that all bitsToSet are unique and that they are between 0 and 63
foreach (byte bit in bitsToSet)
{
value &= ~(1UL << bit); //this unsets the nth bit
}
return value;
}
/// <summary>
/// Unsets a selection of bits within a signed integer.
/// </summary>
/// <param name="value">Signed integer to have bits set.</param>
/// <param name="bitsToSet">Selection of bits to unset. Bit indicies are 0 indexed.</param>
/// <returns>Signed integer with the chosen bits unset (or equivalently set to be 0).</returns>
public static long UnsetBits(this long value, byte[] bitsToSet)
{
//TODO: check that all bitsToSet are unique and that they are between 0 and 63
foreach (byte bit in bitsToSet)
{
value &= ~(1L << bit); //this unsets the nth bit
}
return value;
}
/// <summary>
/// Unsets all bits between start and end bits of an unsigned integer (inclusive, 0 indexed).
/// </summary>
/// <param name="value">value to have chosen bits unset.</param>
/// <param name="startBit">0 based index of the start bit.</param>
/// <param name="endBit">0 based index of the end bit. This is inclusive, ie this bit will be included.</param>
/// <returns>Unsigned integer with bits between start and end bit unset.</returns>
public static ulong UnsetBits(this ulong value, int startBit = 0, int endBit = 63)
{
if (endBit < 0 || endBit > 63)
{
throw new ArgumentException("Invalid number of bits chosen. Value must be between 0 and 63.", "endBit");
}
else if (startBit < 0 || startBit > 63)
{
throw new ArgumentException("Invalid number of bits chosen. Value must be between 0 and 63.", "startBit");
}
else if (endBit <= startBit)
{
throw new ArgumentException("End bit was less than or equal to start bit.", "endBit");
}
ulong mask = AllBitsSet(endBit - startBit) << startBit;
return value & ~mask;
}
/// <summary>
/// Unsets all bits between start and end bits of a signed integer (inclusive, 0 indexed).
/// </summary>
/// <param name="value">value to have chosen bits unset.</param>
/// <param name="startBit">0 based index of the start bit.</param>
/// <param name="endBit">0 based index of the end bit. This is inclusive, ie this bit will be included.</param>
/// <returns>Signed integer with bits between start and end bit unset.</returns>
public static long UnsetBits(this long value, int startBit = 0, int endBit = 63)
{
if (endBit < 0 || endBit > 63)
{
throw new ArgumentException("Invalid number of bits chosen. Value must be between 0 and 63.", "endBit");
}
else if (startBit < 0 || startBit > 63)
{
throw new ArgumentException("Invalid number of bits chosen. Value must be between 0 and 63.", "startBit");
}
else if (endBit <= startBit)
{
throw new ArgumentException("End bit was less than or equal to start bit.", "endBit");
}
long mask = AllBitsSetSigned(endBit - startBit) << startBit;
return value & ~mask;
}
#endregion
#region Invert Bits
/// <summary>
/// Inverts / flips chosen bits in an unsigned integer.
/// </summary>
/// <param name="value">Unsigned integer to have bits inverted.</param>
/// <param name="bitsToInvert">Bits to invert.</param>
/// <returns>Unsigned integer with chosen bits inverted (ie, 0->1 and 1->0).</returns>
public static ulong InvertBits(this ulong value, byte[] bitsToInvert)
{
//TODO: check that all bitsToSet are unique and that they are between 0 and 63
ulong mask = SetBits(0UL, bitsToInvert);
return value ^ mask;
}
/// <summary>
/// Inverts / flips chosen bits in a signed integer.
/// </summary>
/// <param name="value">Signed integer to have bits inverted.</param>
/// <param name="bitsToInvert">Bits to invert.</param>
/// <returns>Signed integer with chosen bits inverted (ie, 0->1 and 1->0).</returns>
public static long InvertBits(this long value, byte[] bitsToInvert)
{
//TODO: check that all bitsToSet are unique and that they are between 0 and 63
long mask = SetBits(0, bitsToInvert);
return value ^ mask;
}
/// <summary>
/// Inverts / flips bits between start and end bits of an unsigned integer (inclusive, 0 indexed).
/// </summary>
/// <param name="value">Unsigned integer to have bits invert.</param>
/// <param name="startBit">0 based index of the start bit.</param>
/// <param name="endBit">0 based index of the end bit. This is inclusive, ie this bit will be included.</param>
/// <returns>Unsigned integer with chosen bits inverted (ie, 0->1 and 1->0).</returns>
public static ulong InvertBits(this ulong value, int startBit = 0, int endBit = 63)
{
if (endBit < 0 || endBit > 63)
{
throw new ArgumentException("Invalid number of bits chosen. Value must be between 0 and 63.", "endBit");
}
else if (startBit < 0 || startBit > 63)
{
throw new ArgumentException("Invalid number of bits chosen. Value must be between 0 and 63.", "startBit");
}
else if (endBit <= startBit)
{
throw new ArgumentException("End bit was less than or equal to start bit.", "endBit");
}
//Invert bits between start and end bits
//Create mask with bits set between start and end bits, then XOR it with value
ulong mask = AllBitsSet(endBit - startBit) << startBit;
return value ^ mask;
}
/// <summary>
/// Inverts / flips bits between start and end bits of a signed integer (inclusive, 0 indexed).
/// </summary>
/// <param name="value">Signed integer to have bits invert.</param>
/// <param name="startBit">0 based index of the start bit.</param>
/// <param name="endBit">0 based index of the end bit. This is inclusive, ie this bit will be included.</param>
/// <returns>Signed integer with chosen bits inverted (ie, 0->1 and 1->0).</returns>
public static long InvertBits(this long value, int startBit = 0, int endBit = 63)
{
if (endBit < 0 || endBit > 63)
{
throw new ArgumentException("Invalid number of bits chosen. Value must be between 0 and 63.", "endBit");
}
else if (startBit < 0 || startBit > 63)
{
throw new ArgumentException("Invalid number of bits chosen. Value must be between 0 and 63.", "startBit");
}
else if (endBit <= startBit)
{
throw new ArgumentException("End bit was less than or equal to start bit.", "endBit");
}
//Invert bits between start and end bits
//Create mask with bits set between start and end bits, then XOR it with value
long mask = AllBitsSetSigned(endBit - startBit) << startBit;
return value ^ mask;
}
#endregion
#region Hamming Distance
/// <summary>
/// Calculates the Hamming Distance between two unsigned integers. This is defined as the number of differing bits.
/// </summary>
/// <param name="value1">First value.</param>
/// <param name="value2">Second value.</param>
/// <returns>Returns the Hamming Distance, which is the number of bits set in value1 XOR value2.</returns>
public static byte HammingDistance(ulong value1, ulong value2)
{
return CountBitsSet(value1 ^ value2);
}
/// <summary>
/// Calculates the Hamming Distance between two signed integers. This is defined as the number of differing bits.
/// </summary>
/// <param name="value1">First value.</param>
/// <param name="value2">Second value.</param>
/// <returns>Returns the Hamming Distance, which is the number of bits set in value1 XOR value2.</returns>
public static byte HammingDistance(long value1, long value2)
{
return CountBitsSet(value1 ^ value2);
}
#endregion
#region Bit Length
/// <summary>
/// Determines the number of bits required to represent the given value in binary, excluding leading zeroes.
/// </summary>
/// <param name="value">Chosen integer value.</param>
/// <returns>Returns number of bits required to represent value.</returns>
public static byte BitLength(this ulong value)
{
//determine number of bits required to represent value
//i think a good way to do this is detemrmine how many left shifts are needed
byte output = 0;
while (value >> output != 0)
{
output += 1;
}
return output;
}
/// <summary>
/// Determines the number of bits required to represent the given value in binary, excluding leading zeroes.
/// </summary>
/// <param name="value">Chosen integer value.</param>
/// <returns>Returns number of bits required to represent value.</returns>
public static byte BitLength(this long value)
{
byte output = 0;
while (value >> output != 0)
{
output += 1;
}
return output;
}
#endregion
}
}