| 1 | // @ts-check
|
|---|
| 2 |
|
|---|
| 3 | /**
|
|---|
| 4 | * We must remap all the old bits to new bits for each set variant
|
|---|
| 5 | * Only arbitrary variants are considered as those are the only
|
|---|
| 6 | * ones that need to be re-sorted at this time
|
|---|
| 7 | *
|
|---|
| 8 | * An iterated process that removes and sets individual bits simultaneously
|
|---|
| 9 | * will not work because we may have a new bit that is also a later old bit
|
|---|
| 10 | * This means that we would be removing a previously set bit which we don't
|
|---|
| 11 | * want to do
|
|---|
| 12 | *
|
|---|
| 13 | * For example (assume `bN` = `1<<N`)
|
|---|
| 14 | * Given the "total" mapping `[[b1, b3], [b2, b4], [b3, b1], [b4, b2]]`
|
|---|
| 15 | * The mapping is "total" because:
|
|---|
| 16 | * 1. Every input and output is accounted for
|
|---|
| 17 | * 2. All combinations are unique
|
|---|
| 18 | * 3. No one input maps to multiple outputs and vice versa
|
|---|
| 19 | * And, given an offset with all bits set:
|
|---|
| 20 | * V = b1 | b2 | b3 | b4
|
|---|
| 21 | *
|
|---|
| 22 | * Let's explore the issue with removing and setting bits simultaneously:
|
|---|
| 23 | * V & ~b1 | b3 = b2 | b3 | b4
|
|---|
| 24 | * V & ~b2 | b4 = b3 | b4
|
|---|
| 25 | * V & ~b3 | b1 = b1 | b4
|
|---|
| 26 | * V & ~b4 | b2 = b1 | b2
|
|---|
| 27 | *
|
|---|
| 28 | * As you can see, we end up with the wrong result.
|
|---|
| 29 | * This is because we're removing a bit that was previously set.
|
|---|
| 30 | * And, thus the final result is missing b3 and b4.
|
|---|
| 31 | *
|
|---|
| 32 | * Now, let's explore the issue with removing the bits first:
|
|---|
| 33 | * V & ~b1 = b2 | b3 | b4
|
|---|
| 34 | * V & ~b2 = b3 | b4
|
|---|
| 35 | * V & ~b3 = b4
|
|---|
| 36 | * V & ~b4 = 0
|
|---|
| 37 | *
|
|---|
| 38 | * And then setting the bits:
|
|---|
| 39 | * V | b3 = b3
|
|---|
| 40 | * V | b4 = b3 | b4
|
|---|
| 41 | * V | b1 = b1 | b3 | b4
|
|---|
| 42 | * V | b2 = b1 | b2 | b3 | b4
|
|---|
| 43 | *
|
|---|
| 44 | * We get the correct result because we're not removing any bits that were
|
|---|
| 45 | * previously set thus properly remapping the bits to the new order
|
|---|
| 46 | *
|
|---|
| 47 | * To collect this into a single operation that can be done simultaneously
|
|---|
| 48 | * we must first create a mask for the old bits that are set and a mask for
|
|---|
| 49 | * the new bits that are set. Then we can remove the old bits and set the new
|
|---|
| 50 | * bits simultaneously in a "single" operation like so:
|
|---|
| 51 | * OldMask = b1 | b2 | b3 | b4
|
|---|
| 52 | * NewMask = b3 | b4 | b1 | b2
|
|---|
| 53 | *
|
|---|
| 54 | * So this:
|
|---|
| 55 | * V & ~oldMask | newMask
|
|---|
| 56 | *
|
|---|
| 57 | * Expands to this:
|
|---|
| 58 | * V & ~b1 & ~b2 & ~b3 & ~b4 | b3 | b4 | b1 | b2
|
|---|
| 59 | *
|
|---|
| 60 | * Which becomes this:
|
|---|
| 61 | * b1 | b2 | b3 | b4
|
|---|
| 62 | *
|
|---|
| 63 | * Which is the correct result!
|
|---|
| 64 | *
|
|---|
| 65 | * @param {bigint} num
|
|---|
| 66 | * @param {[bigint, bigint][]} mapping
|
|---|
| 67 | */
|
|---|
| 68 | export function remapBitfield(num, mapping) {
|
|---|
| 69 | // Create masks for the old and new bits that are set
|
|---|
| 70 | let oldMask = 0n
|
|---|
| 71 | let newMask = 0n
|
|---|
| 72 | for (let [oldBit, newBit] of mapping) {
|
|---|
| 73 | if (num & oldBit) {
|
|---|
| 74 | oldMask = oldMask | oldBit
|
|---|
| 75 | newMask = newMask | newBit
|
|---|
| 76 | }
|
|---|
| 77 | }
|
|---|
| 78 |
|
|---|
| 79 | // Remove all old bits
|
|---|
| 80 | // Set all new bits
|
|---|
| 81 | return (num & ~oldMask) | newMask
|
|---|
| 82 | }
|
|---|