Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Make arbitrary variant sorting deterministic
- Loading branch information
1 parent
12dac7d
commit 428f560
Showing
4 changed files
with
185 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
// @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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters