Advertisement
Guest User

numberSet.ts

a guest
Jul 10th, 2024
109
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1.  
  2. /*
  3.  * This is pretty complicated and otherwise uncommented so I feel as though I owe a bit of an explanation here.
  4.  * We want an intrinsically sorted set that can contain ANY valid JS number. So we first convert the number into bytes,
  5.  * by sharing an ArrayBuffer between a Uint8Array and Float64Array. Then we use the first 7 bytes, going from MSB to
  6.  * LSB, to traverse through a tree until we find the "ByteSet" that defines the values within the set for the
  7.  * last 8 bits. An optimization is also implemented where if it is known that all bytes after the one represented by
  8.  * the current node being traversed are 0, the presence of that exact number within the set is stored in that node
  9.  * and NO children are made. This takes advantage of the fact that integers in IEEE754 will typically end in long runs
  10.  * of 0.
  11.  */
  12.  
  13. class ByteSet {
  14.  
  15.     private readonly data;
  16.     constructor() {
  17.         this.data = new Uint8Array(32);
  18.     }
  19.  
  20.     has(index: number): boolean {
  21.         const flag = 1 << (index & 7);
  22.         return (this.data[index >> 3] & flag) !== 0;
  23.     }
  24.  
  25.     add(index: number): void {
  26.         this.set(index, true);
  27.     }
  28.  
  29.     remove(index: number): void {
  30.         this.set(index, false);
  31.     }
  32.  
  33.     private set(index: number, value: boolean): void {
  34.         const flag = 1 << (index & 7);
  35.         index >>= 3;
  36.         if (value) {
  37.             this.data[index] |= flag;
  38.         } else {
  39.             this.data[index] &= (~flag);
  40.         }
  41.     }
  42.  
  43.     *iterator(buf8: Uint8Array, buf64: Float64Array, offset: number): Generator<number> {
  44.         let head: number = -1;
  45.         let base: number;
  46.         let flag: number;
  47.         while ((++head) < 32) {
  48.             flag = this.data[head];
  49.             if (flag === 0) continue;
  50.             base = head << 3;
  51.             for (let i=0; i < 8; i++) {
  52.                 if (flag & (1 << i)) {
  53.                     buf8[offset] = (base | i);
  54.                     yield buf64[0];
  55.                 }
  56.             }
  57.         }
  58.     }
  59.  
  60. }
  61.  
  62. const IS_BIG_ENDIAN: boolean = (() => {
  63.     const ab = new ArrayBuffer(2);
  64.     const u8 = new Uint8Array(ab);
  65.     const u16 = new Uint16Array(ab);
  66.     u8[0] = 0xAA;
  67.     u8[1] = 0xBB;
  68.     return u16[0] === 0xAABB;
  69. })();
  70.  
  71. const TRAVERSAL_DIRECTION: 1 | -1 = IS_BIG_ENDIAN ? 1 : -1;
  72. const TRAVERSAL_START: 0 | 7 = IS_BIG_ENDIAN ? 0 : 7;
  73. const TRAVERSAL_END: 0 | 7 = IS_BIG_ENDIAN ? 7 : 0;
  74.  
  75. class NumberSetNode {
  76.  
  77.     private value: boolean;
  78.     private readonly children: { [index: number]: NumberSetNode | ByteSet };
  79.     constructor() {
  80.         this.value = false;
  81.         this.children = {};
  82.     }
  83.  
  84.     protected has0(bytes: Uint8Array, index: number, atWhichAllZero: number): boolean {
  85.         if (index === atWhichAllZero) return this.value;
  86.         const determinant: number = bytes[index];
  87.         const child = this.children[determinant];
  88.         if (typeof child === "undefined") return false;
  89.         index += TRAVERSAL_DIRECTION;
  90.         if (index === TRAVERSAL_END) {
  91.             return (child as ByteSet).has(bytes[index]);
  92.         } else {
  93.             return (child as NumberSetNode).has0(bytes, index, atWhichAllZero);
  94.         }
  95.     }
  96.  
  97.     protected add0(bytes: Uint8Array, index: number, atWhichAllZero: number): void {
  98.         if (index === atWhichAllZero) {
  99.             this.value = true;
  100.             return;
  101.         }
  102.         const determinant: number = bytes[index];
  103.         let child: NumberSetNode | ByteSet = this.children[determinant];
  104.  
  105.         index += TRAVERSAL_DIRECTION;
  106.         const isTail: boolean = (index === TRAVERSAL_END);
  107.         if (typeof child === "undefined") {
  108.             child = isTail ? new ByteSet() : new NumberSetNode();
  109.             this.children[determinant] = child;
  110.         }
  111.  
  112.         if (isTail) {
  113.             (child as ByteSet).add(bytes[index]);
  114.         } else {
  115.             (child as NumberSetNode).add0(bytes, index, atWhichAllZero);
  116.         }
  117.     }
  118.  
  119.     protected remove0(bytes: Uint8Array, index: number, atWhichAllZero: number): void {
  120.         if (index === atWhichAllZero) {
  121.             this.value = false;
  122.             return;
  123.         }
  124.         const determinant: number = bytes[index];
  125.         const child: NumberSetNode | ByteSet = this.children[determinant];
  126.         if (typeof child === "undefined") return;
  127.  
  128.         index += TRAVERSAL_DIRECTION;
  129.         if (index === TRAVERSAL_END) {
  130.             (child as ByteSet).remove(bytes[index]);
  131.         } else {
  132.             (child as NumberSetNode).remove0(bytes, index, atWhichAllZero);
  133.         }
  134.     }
  135.  
  136.     protected *iterator(buf8: Uint8Array, buf64: Float64Array, offset: number): Generator<number> {
  137.         if (this.value) {
  138.             IS_BIG_ENDIAN ? buf8.fill(0, offset, 8) : buf8.fill(0, 0, offset + 1);
  139.             yield buf64[0];
  140.         }
  141.  
  142.         const next: number = offset + TRAVERSAL_DIRECTION;
  143.         const isTail: boolean = next === TRAVERSAL_END;
  144.         let child: NumberSetNode | ByteSet;
  145.         for (let k of Object.keys(this.children)) {
  146.             buf8[offset] = parseInt(k);
  147.             child = this.children[k as unknown as number];
  148.             if (isTail) {
  149.                 for (let n of (child as ByteSet).iterator(buf8, buf64, next)) yield n;
  150.             } else {
  151.                 for (let n of (child as NumberSetNode).iterator(buf8, buf64, next)) yield n;
  152.             }
  153.         }
  154.     }
  155.  
  156. }
  157.  
  158. export class NumberSet extends NumberSetNode implements Iterable<number> {
  159.  
  160.     private readonly buf8: Uint8Array;
  161.     private readonly buf64: Float64Array;
  162.     private mayHaveNegativeValues: boolean = false;
  163.  
  164.     constructor() {
  165.         super();
  166.         const buf = new ArrayBuffer(8);
  167.         this.buf8 = new Uint8Array(buf);
  168.         this.buf64 = new Float64Array(buf);
  169.     }
  170.  
  171.     private parameterize(n: number): [ Uint8Array, number, number ] {
  172.         this.buf64[0] = n;
  173.         let atWhichAllZero: number = TRAVERSAL_END + TRAVERSAL_DIRECTION;
  174.         let i: number = atWhichAllZero;
  175.         while (i !== TRAVERSAL_START) {
  176.             i -= TRAVERSAL_DIRECTION;
  177.             if (this.buf8[i] !== 0) break;
  178.             atWhichAllZero = i;
  179.         }
  180.         return [ this.buf8, TRAVERSAL_START, atWhichAllZero ];
  181.     }
  182.  
  183.     has(n: number): boolean {
  184.         return this.has0(...this.parameterize(n));
  185.     }
  186.  
  187.     add(n: number): void {
  188.         this.mayHaveNegativeValues |= (n < 0);
  189.         return this.add0(...this.parameterize(n));
  190.     }
  191.  
  192.     remove(n: number): void {
  193.         return this.remove0(...this.parameterize(n));
  194.     }
  195.  
  196.     [Symbol.iterator](): Iterator<number> {
  197.         return this.iterator(this.buf8, this.buf64, TRAVERSAL_START);
  198.     }
  199.  
  200. }
  201.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement