90 lines
2.7 KiB
JavaScript
90 lines
2.7 KiB
JavaScript
// @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
|
|
*/ "use strict";
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
Object.defineProperty(exports, "remapBitfield", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return remapBitfield;
|
|
}
|
|
});
|
|
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;
|
|
}
|