Class: Omnizip::Algorithms::BZip2::Bwt
- Inherits:
-
Object
- Object
- Omnizip::Algorithms::BZip2::Bwt
- Defined in:
- lib/omnizip/algorithms/bzip2/bwt.rb
Overview
Burrows-Wheeler Transform (BWT)
The BWT is a block-sorting compression algorithm that rearranges a character string into runs of similar characters. This transformation is reversible and forms the foundation of the BZip2 compression algorithm.
The transformation works by:
- Creating all rotations of the input string
- Sorting these rotations lexicographically
- Taking the last column of the sorted rotations
- Recording the row index of the original string
This groups similar characters together, making the data more compressible for subsequent stages (MTF, RLE, Huffman).
Instance Method Summary collapse
-
#build_lf_mapping(last_column) ⇒ Array<Integer>
Build LF (Last-to-First) mapping for BWT decode.
-
#decode(data, primary_index) ⇒ String
Decode data using reverse Burrows-Wheeler Transform.
-
#encode(data) ⇒ Array<String, Integer>
Encode data using Burrows-Wheeler Transform (optimized).
Instance Method Details
#build_lf_mapping(last_column) ⇒ Array<Integer>
Build LF (Last-to-First) mapping for BWT decode
For each position i in the sorted order (first column), LF tells us which position in the sorted order corresponds to the same character in the last column
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
# File 'lib/omnizip/algorithms/bzip2/bwt.rb', line 108 def build_lf_mapping(last_column) n = last_column.length # Count occurrences of each byte value counts = Array.new(256, 0) last_column.each_byte { |b| counts[b] += 1 } # Build cumulative counts (start position of each byte in sorted array) cumulative = Array.new(256, 0) sum = 0 256.times do |i| cumulative[i] = sum sum += counts[i] end # Build the LF mapping # For each position in last column, find its position in first column lf = Array.new(n) occurrence = Array.new(256, 0) # Track which occurrence of each byte last_column.each_byte.with_index do |byte, i| # This byte's position in first column is: # cumulative[byte] + occurrence[byte] pos_in_first = cumulative[byte] + occurrence[byte] occurrence[byte] += 1 # Now find which last column position corresponds to this first column position # We need the inverse: which last column index has this sorted position lf[pos_in_first] = i end lf end |
#decode(data, primary_index) ⇒ String
Decode data using reverse Burrows-Wheeler Transform
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/omnizip/algorithms/bzip2/bwt.rb', line 77 def decode(data, primary_index) return "".b if data.empty? # Build LF (Last-to-First) mapping # This maps each position in L to corresponding position in F lf = build_lf_mapping(data) # Reconstruct by following the LF chain result = [] idx = primary_index data.length.times do # The first column is the sorted last column # Get the character at this position byte_val = data.bytes.sort[idx] result << byte_val # Follow LF mapping to next position idx = lf[idx] end result.pack("C*").b end |
#encode(data) ⇒ Array<String, Integer>
Encode data using Burrows-Wheeler Transform (optimized)
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/omnizip/algorithms/bzip2/bwt.rb', line 46 def encode(data) return ["".b, 0] if data.empty? n = data.length bytes = data.bytes # Build suffix array without creating rotation strings # Use direct byte comparison for efficiency suffix_array = (0...n).to_a # Sort using optimized comparison that avoids string allocation suffix_array.sort! do |a, b| compare_rotations(bytes, a, b, n) end # Find primary index (position where suffix starts at 0) primary_index = suffix_array.index(0) # Extract last column (character before each suffix) transformed = suffix_array.map do |idx| bytes[(idx - 1) % n] end.pack("C*").b [transformed, primary_index] end |