Spaces:
Sleeping
Sleeping
| // @ts-check | |
| /** | |
| * We must remap all the old bits to new bits for each set variant | |
| * Only arbitrary variants are considered as those are the only | |
| * ones that need to be re-sorted at this time | |
| * | |
| * An iterated process that removes and sets individual bits simultaneously | |
| * will not work because we may have a new bit that is also a later old bit | |
| * This means that we would be removing a previously set bit which we don't | |
| * want to do | |
| * | |
| * For example (assume `bN` = `1<<N`) | |
| * Given the "total" mapping `[[b1, b3], [b2, b4], [b3, b1], [b4, b2]]` | |
| * The mapping is "total" because: | |
| * 1. Every input and output is accounted for | |
| * 2. All combinations are unique | |
| * 3. No one input maps to multiple outputs and vice versa | |
| * And, given an offset with all bits set: | |
| * V = b1 | b2 | b3 | b4 | |
| * | |
| * Let's explore the issue with removing and setting bits simultaneously: | |
| * V & ~b1 | b3 = b2 | b3 | b4 | |
| * V & ~b2 | b4 = b3 | b4 | |
| * V & ~b3 | b1 = b1 | b4 | |
| * V & ~b4 | b2 = b1 | b2 | |
| * | |
| * As you can see, we end up with the wrong result. | |
| * This is because we're removing a bit that was previously set. | |
| * And, thus the final result is missing b3 and b4. | |
| * | |
| * Now, let's explore the issue with removing the bits first: | |
| * V & ~b1 = b2 | b3 | b4 | |
| * V & ~b2 = b3 | b4 | |
| * V & ~b3 = b4 | |
| * V & ~b4 = 0 | |
| * | |
| * And then setting the bits: | |
| * V | b3 = b3 | |
| * V | b4 = b3 | b4 | |
| * V | b1 = b1 | b3 | b4 | |
| * V | b2 = b1 | b2 | b3 | b4 | |
| * | |
| * We get the correct result because we're not removing any bits that were | |
| * previously set thus properly remapping the bits to the new order | |
| * | |
| * To collect this into a single operation that can be done simultaneously | |
| * we must first create a mask for the old bits that are set and a mask for | |
| * the new bits that are set. Then we can remove the old bits and set the new | |
| * bits simultaneously in a "single" operation like so: | |
| * OldMask = b1 | b2 | b3 | b4 | |
| * NewMask = b3 | b4 | b1 | b2 | |
| * | |
| * So this: | |
| * V & ~oldMask | newMask | |
| * | |
| * Expands to this: | |
| * V & ~b1 & ~b2 & ~b3 & ~b4 | b3 | b4 | b1 | b2 | |
| * | |
| * Which becomes this: | |
| * b1 | b2 | b3 | b4 | |
| * | |
| * Which is the correct result! | |
| * | |
| * @param {bigint} num | |
| * @param {[bigint, bigint][]} mapping | |
| */ | |
| export function remapBitfield(num, mapping) { | |
| // Create masks for the old and new bits that are set | |
| let oldMask = 0n | |
| let newMask = 0n | |
| for (let [oldBit, newBit] of mapping) { | |
| if (num & oldBit) { | |
| oldMask = oldMask | oldBit | |
| newMask = newMask | newBit | |
| } | |
| } | |
| // Remove all old bits | |
| // Set all new bits | |
| return (num & ~oldMask) | newMask | |
| } | |