/**
 * Compressed Bitvector
 * 
 * Implements Pătrașcu's compressed bitvector (Lemma 2.1) with rank/select support.
 * Uses lg(n choose m) + O(n/lg^c n) bits of space for a bitvector with m 1s.
 */

import { RankSelectStructure } from './RankSelect.js';

export class CompressedBitvector {
  constructor(bits, length = null) {
    // bits can be Array, Uint8Array, or already a Uint32Array
    this.length = length || bits.length;
    
    // Pack bits into Uint32Array (32 bits per word)
    const numWords = Math.ceil(this.length / 32);
    this.bitvector = new Uint32Array(numWords);
    
    // Count 1s
    this.onesCount = 0;
    
    // Pack bits
    for (let i = 0; i < this.length; i++) {
      const wordIndex = Math.floor(i / 32);
      const bitIndex = i % 32;
      
      let bit;
      if (Array.isArray(bits)) {
        bit = bits[i] ? 1 : 0;
      } else if (bits instanceof Uint8Array || bits instanceof Uint32Array) {
        bit = (bits[i] & 1);
      } else {
        throw new Error('Unsupported bits type');
      }
      
      if (bit === 1) {
        this.bitvector[wordIndex] |= (1 << bitIndex);
        this.onesCount++;
      }
    }
    
    // Build rank/select structures
    this.rankSelect = new RankSelectStructure(this.bitvector, this.length);
  }

  static fromPacked(bitvector, length) {
    const instance = Object.create(CompressedBitvector.prototype);
    instance.length = length;
    instance.bitvector = bitvector;
    instance.rankSelect = new RankSelectStructure(bitvector, length);
    instance.onesCount = length ? instance.rankSelect.rank1(length - 1) : 0;
    return instance;
  }

  static fromPackedWithRank(bitvector, length, blockSize, blockRank) {
    const instance = Object.create(CompressedBitvector.prototype);
    instance.length = length;
    instance.bitvector = bitvector;
    instance.rankSelect = RankSelectStructure.fromPacked(bitvector, length, blockSize, blockRank);
    instance.onesCount = length ? instance.rankSelect.rank1(length - 1) : 0;
    return instance;
  }

  /**
   * Get bit at position i
   */
  access(i) {
    return this.rankSelect.access(i);
  }

  /**
   * Rank query: count 1s up to position i (inclusive)
   */
  rank1(i) {
    return this.rankSelect.rank1(i);
  }

  /**
   * Rank query: count 0s up to position i (inclusive)
   */
  rank0(i) {
    return this.rankSelect.rank0(i);
  }

  /**
   * Select query: find position of k-th 1 (1-indexed)
   */
  select1(k) {
    return this.rankSelect.select1(k);
  }

  /**
   * Select query: find position of k-th 0 (1-indexed)
   */
  select0(k) {
    return this.rankSelect.select0(k);
  }

  /**
   * Get memory usage in bytes
   */
  getMemoryUsage() {
    const bitvectorBytes = this.bitvector.length * 4;
    const rankSelectBytes = this.rankSelect.getMemoryUsage();
    return bitvectorBytes + rankSelectBytes;
  }

  /**
   * Get compression ratio
   */
  getCompressionRatio() {
    const uncompressedSize = Math.ceil(this.length / 8);
    return this.getMemoryUsage() / uncompressedSize;
  }
}
