Data update
This commit is contained in:
parent
4bb20c9b71
commit
cbaf4c4b64
12390 changed files with 318560 additions and 27248 deletions
11
Task/LZW-compression/Ada/lzw-compression-1.adb
Normal file
11
Task/LZW-compression/Ada/lzw-compression-1.adb
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
package LZW is
|
||||
|
||||
MAX_CODE : constant := 4095;
|
||||
|
||||
type Codes is new Natural range 0 .. MAX_CODE;
|
||||
type Compressed_Data is array (Positive range <>) of Codes;
|
||||
|
||||
function Compress (Cleartext : in String) return Compressed_Data;
|
||||
function Decompress (Data : in Compressed_Data) return String;
|
||||
|
||||
end LZW;
|
||||
116
Task/LZW-compression/Ada/lzw-compression-2.adb
Normal file
116
Task/LZW-compression/Ada/lzw-compression-2.adb
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
with Ada.Containers.Ordered_Maps;
|
||||
with Ada.Strings.Unbounded;
|
||||
|
||||
package body LZW is
|
||||
package UStrings renames Ada.Strings.Unbounded;
|
||||
use type UStrings.Unbounded_String;
|
||||
|
||||
--------------
|
||||
-- Compress --
|
||||
--------------
|
||||
|
||||
function Compress (Cleartext : in String) return Compressed_Data is
|
||||
-- translate String to Code-ID
|
||||
package String_To_Code is new Ada.Containers.Ordered_Maps (
|
||||
Key_Type => UStrings.Unbounded_String,
|
||||
Element_Type => Codes);
|
||||
|
||||
Dictionary : String_To_Code.Map;
|
||||
-- Next unused Code-ID
|
||||
Next_Entry : Codes := 256;
|
||||
|
||||
-- maximum same length as input, compression ratio always >=1.0
|
||||
Result : Compressed_Data (1 .. Cleartext'Length);
|
||||
-- position for next Code-ID
|
||||
Result_Index : Natural := 1;
|
||||
|
||||
-- current and next input string
|
||||
Current_Word : UStrings.Unbounded_String :=
|
||||
UStrings.Null_Unbounded_String;
|
||||
Next_Word : UStrings.Unbounded_String :=
|
||||
UStrings.Null_Unbounded_String;
|
||||
begin
|
||||
-- initialize Dictionary
|
||||
for C in Character loop
|
||||
String_To_Code.Insert
|
||||
(Dictionary,
|
||||
UStrings.Null_Unbounded_String & C,
|
||||
Character'Pos (C));
|
||||
end loop;
|
||||
|
||||
for Index in Cleartext'Range loop
|
||||
-- add character to current word
|
||||
Next_Word := Current_Word & Cleartext (Index);
|
||||
if String_To_Code.Contains (Dictionary, Next_Word) then
|
||||
-- already in dictionary, continue with next character
|
||||
Current_Word := Next_Word;
|
||||
else
|
||||
-- insert code for current word to result
|
||||
Result (Result_Index) :=
|
||||
String_To_Code.Element (Dictionary, Current_Word);
|
||||
Result_Index := Result_Index + 1;
|
||||
-- add new Code to Dictionary
|
||||
String_To_Code.Insert (Dictionary, Next_Word, Next_Entry);
|
||||
Next_Entry := Next_Entry + 1;
|
||||
-- reset current word to one character
|
||||
Current_Word := UStrings.Null_Unbounded_String &
|
||||
Cleartext (Index);
|
||||
end if;
|
||||
end loop;
|
||||
-- Last word was not entered
|
||||
Result (Result_Index) :=
|
||||
String_To_Code.Element (Dictionary, Current_Word);
|
||||
-- return correct array size
|
||||
return Result (1 .. Result_Index);
|
||||
end Compress;
|
||||
|
||||
----------------
|
||||
-- Decompress --
|
||||
----------------
|
||||
|
||||
function Decompress (Data : in Compressed_Data) return String is
|
||||
-- translate Code-ID to String
|
||||
type Code_To_String is array (Codes) of UStrings.Unbounded_String;
|
||||
|
||||
Dictionary : Code_To_String;
|
||||
-- next unused Code-ID
|
||||
Next_Entry : Codes := 256;
|
||||
|
||||
-- initialize resulting string as empty string
|
||||
Result : UStrings.Unbounded_String := UStrings.Null_Unbounded_String;
|
||||
|
||||
Next_Code : Codes;
|
||||
-- first code has to be in dictionary
|
||||
Last_Code : Codes := Data (1);
|
||||
-- suffix appended to last string for new dictionary entry
|
||||
Suffix : Character;
|
||||
begin
|
||||
-- initialize Dictionary
|
||||
for C in Character loop
|
||||
Dictionary (Codes (Character'Pos (C))) :=
|
||||
UStrings.Null_Unbounded_String & C;
|
||||
end loop;
|
||||
|
||||
-- output first Code-ID
|
||||
UStrings.Append (Result, Dictionary (Last_Code));
|
||||
for Index in 2 .. Data'Last loop
|
||||
Next_Code := Data (Index);
|
||||
if Next_Code <= Next_Entry then
|
||||
-- next Code-ID already in dictionary -> append first char
|
||||
Suffix := UStrings.Element (Dictionary (Next_Code), 1);
|
||||
else
|
||||
-- next Code-ID not in dictionary -> use char from last ID
|
||||
Suffix := UStrings.Element (Dictionary (Last_Code), 1);
|
||||
end if;
|
||||
-- expand the dictionary
|
||||
Dictionary (Next_Entry) := Dictionary (Last_Code) & Suffix;
|
||||
Next_Entry := Next_Entry + 1;
|
||||
-- output the current Code-ID to result
|
||||
UStrings.Append (Result, Dictionary (Next_Code));
|
||||
Last_Code := Next_Code;
|
||||
end loop;
|
||||
-- return String
|
||||
return UStrings.To_String (Result);
|
||||
end Decompress;
|
||||
|
||||
end LZW;
|
||||
21
Task/LZW-compression/Ada/lzw-compression-3.adb
Normal file
21
Task/LZW-compression/Ada/lzw-compression-3.adb
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
with LZW;
|
||||
with Ada.Text_IO;
|
||||
|
||||
procedure Test is
|
||||
package Text_IO renames Ada.Text_IO;
|
||||
package Code_IO is new Ada.Text_IO.Integer_IO (LZW.Codes);
|
||||
|
||||
Test_Data : constant LZW.Compressed_Data :=
|
||||
LZW.Compress ("TOBEORNOTTOBEORTOBEORNOT");
|
||||
begin
|
||||
for Index in Test_Data'Range loop
|
||||
Code_IO.Put (Test_Data (Index), 0);
|
||||
Text_IO.Put (" ");
|
||||
end loop;
|
||||
Text_IO.New_Line;
|
||||
declare
|
||||
Cleartext : constant String := LZW.Decompress (Test_Data);
|
||||
begin
|
||||
Text_IO.Put_Line (Cleartext);
|
||||
end;
|
||||
end Test;
|
||||
|
|
@ -12,7 +12,7 @@ auto compress(in string original) pure nothrow {
|
|||
w = w ~ ch;
|
||||
else {
|
||||
result ~= dict[w];
|
||||
dict[w ~ ch] = dict.length;
|
||||
dict[w ~ ch] = cast(int)dict.length;
|
||||
w = [ch];
|
||||
}
|
||||
return w.empty ? result : (result ~ dict[w]);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ struct LZW {
|
|||
|
||||
enum int initDictSize = 256;
|
||||
static immutable ubyte[initDictSize] bytes;
|
||||
static this() {
|
||||
shared static this() {
|
||||
foreach (immutable T i; 0 .. initDictSize)
|
||||
bytes[i] = i;
|
||||
}
|
||||
|
|
@ -18,7 +18,7 @@ struct LZW {
|
|||
out(result) {
|
||||
if (!original.empty)
|
||||
assert(result[0] < initDictSize);
|
||||
} body {
|
||||
} do {
|
||||
if (original.empty)
|
||||
return [];
|
||||
Tcomp[Ta] dict;
|
||||
|
|
@ -57,7 +57,7 @@ struct LZW {
|
|||
in {
|
||||
if (!compressed.empty)
|
||||
assert(compressed[0] < initDictSize, "Bad compressed");
|
||||
} body {
|
||||
} do {
|
||||
if (compressed.empty)
|
||||
return [];
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ enum Marker: ushort {
|
|||
ubyte[] lzwEncode(scope const(ubyte)[] inp, in uint maxBits) pure nothrow
|
||||
in {
|
||||
assert(maxBits >= 9 && maxBits <= 16);
|
||||
} body {
|
||||
} do {
|
||||
// Encode dictionary array. For encoding, entry at
|
||||
// code index is a list of indices that follow current one,
|
||||
// i.e. if code 97 is 'a', code 387 is 'ab', and code 1022 is 'abc',
|
||||
|
|
@ -148,7 +148,7 @@ ubyte[] lzwDecode(scope const(ubyte)[] inp) pure {
|
|||
}
|
||||
|
||||
clearTable(); // In case encoded bits didn't start with Marker.CLR.
|
||||
for (len = inp.length; len;) {
|
||||
for (len = cast(int)inp.length; len;) {
|
||||
getCode();
|
||||
if (code == Marker.EOD)
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ type LzwCompression
|
|||
fun compress ← List by text uncompressed
|
||||
List output ← int[]
|
||||
text working ← Text.EMPTY
|
||||
Map symbolTable ← text%int[].with(256, <int i|text%int(chr(i) => i))
|
||||
Map symbolTable ← text%int[].with(256, <int i|text%int(chr(i) ⇒ i))
|
||||
for each text c in uncompressed
|
||||
text augmented ← working + c
|
||||
if symbolTable.has(augmented)
|
||||
|
|
@ -21,7 +21,7 @@ fun compress ← List by text uncompressed
|
|||
return output
|
||||
end
|
||||
fun decompress ← text by List compressed
|
||||
Map symbolTable ← int%text[].with(256, <int i|int%text(i => chr(i)))
|
||||
Map symbolTable ← int%text[].with(256, <int i|int%text(i ⇒ chr(i)))
|
||||
text working ← symbolTable[compressed[0]]
|
||||
text output ← *working
|
||||
for each int i in compressed.extract(1)
|
||||
|
|
@ -39,7 +39,7 @@ fun decompress ← text by List compressed
|
|||
end
|
||||
return output
|
||||
end
|
||||
List compressed = compress("TOBEORNOTTOBEORTOBEORNOT")
|
||||
List compressed ← compress("TOBEORNOTTOBEORTOBEORNOT")
|
||||
writeLine(compressed)
|
||||
text decompressed = decompress(compressed)
|
||||
text decompressed ← decompress(compressed)
|
||||
writeLine(decompressed)
|
||||
|
|
|
|||
|
|
@ -1,95 +1,95 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Compress a string to a list of output symbols.
|
||||
func compress(uncompressed string) []int {
|
||||
// Build the dictionary.
|
||||
dictSize := 256
|
||||
// We actually want a map of []byte -> int but
|
||||
// slices are not acceptable map key types.
|
||||
dictionary := make(map[string]int, dictSize)
|
||||
for i := 0; i < dictSize; i++ {
|
||||
// Ugly mess to work around not having a []byte key type.
|
||||
// Using `string(i)` would do utf8 encoding for i>127.
|
||||
dictionary[string([]byte{byte(i)})] = i
|
||||
}
|
||||
// Build the dictionary.
|
||||
dictSize := 256
|
||||
// We actually want a map of []byte -> int but
|
||||
// slices are not acceptable map key types.
|
||||
dictionary := make(map[string]int, dictSize)
|
||||
for i := 0; i < dictSize; i++ {
|
||||
// Ugly mess to work around not having a []byte key type.
|
||||
// Using `string(i)` would do utf8 encoding for i>127.
|
||||
dictionary[string([]byte{byte(i)})] = i
|
||||
}
|
||||
|
||||
var result []int
|
||||
var w []byte
|
||||
for i := 0; i < len(uncompressed); i++ {
|
||||
c := uncompressed[i]
|
||||
wc := append(w, c)
|
||||
if _, ok := dictionary[string(wc)]; ok {
|
||||
w = wc
|
||||
} else {
|
||||
result = append(result, dictionary[string(w)])
|
||||
// Add wc to the dictionary.
|
||||
dictionary[string(wc)] = dictSize
|
||||
dictSize++
|
||||
//w = []byte{c}, but re-using wc
|
||||
wc[0] = c
|
||||
w = wc[:1]
|
||||
}
|
||||
}
|
||||
var result []int
|
||||
var w []byte
|
||||
for i := 0; i < len(uncompressed); i++ {
|
||||
c := uncompressed[i]
|
||||
wc := append(w, c)
|
||||
if _, ok := dictionary[string(wc)]; ok {
|
||||
w = wc
|
||||
} else {
|
||||
result = append(result, dictionary[string(w)])
|
||||
// Add wc to the dictionary.
|
||||
dictionary[string(wc)] = dictSize
|
||||
dictSize++
|
||||
//w = []byte{c}, but re-using wc
|
||||
wc[0] = c
|
||||
w = wc[:1]
|
||||
}
|
||||
}
|
||||
|
||||
if len(w) > 0 {
|
||||
// Output the code for w.
|
||||
result = append(result, dictionary[string(w)])
|
||||
}
|
||||
return result
|
||||
if len(w) > 0 {
|
||||
// Output the code for w.
|
||||
result = append(result, dictionary[string(w)])
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
type BadSymbolError int
|
||||
|
||||
func (e BadSymbolError) Error() string {
|
||||
return fmt.Sprint("Bad compressed symbol ", int(e))
|
||||
return fmt.Sprint("Bad compressed symbol ", int(e))
|
||||
}
|
||||
|
||||
// Decompress a list of output symbols to a string.
|
||||
func decompress(compressed []int) (string, error) {
|
||||
// Build the dictionary.
|
||||
dictSize := 256
|
||||
dictionary := make(map[int][]byte, dictSize)
|
||||
for i := 0; i < dictSize; i++ {
|
||||
dictionary[i] = []byte{byte(i)}
|
||||
}
|
||||
// Build the dictionary.
|
||||
dictSize := 256
|
||||
dictionary := make(map[int][]byte, dictSize)
|
||||
for i := 0; i < dictSize; i++ {
|
||||
dictionary[i] = []byte{byte(i)}
|
||||
}
|
||||
|
||||
var result strings.Builder
|
||||
var w []byte
|
||||
for _, k := range compressed {
|
||||
var entry []byte
|
||||
if x, ok := dictionary[k]; ok {
|
||||
//entry = x, but ensuring any append will make a copy
|
||||
entry = x[:len(x):len(x)]
|
||||
} else if k == dictSize && len(w) > 0 {
|
||||
entry = append(w, w[0])
|
||||
} else {
|
||||
return result.String(), BadSymbolError(k)
|
||||
}
|
||||
result.Write(entry)
|
||||
var result strings.Builder
|
||||
var w []byte
|
||||
for _, k := range compressed {
|
||||
var entry []byte
|
||||
if x, ok := dictionary[k]; ok {
|
||||
//entry = x, but ensuring any append will make a copy
|
||||
entry = x[:len(x):len(x)]
|
||||
} else if k == dictSize && len(w) > 0 {
|
||||
entry = append(w, w[0])
|
||||
} else {
|
||||
return result.String(), BadSymbolError(k)
|
||||
}
|
||||
result.Write(entry)
|
||||
|
||||
if len(w) > 0 {
|
||||
// Add w+entry[0] to the dictionary.
|
||||
w = append(w, entry[0])
|
||||
dictionary[dictSize] = w
|
||||
dictSize++
|
||||
}
|
||||
w = entry
|
||||
}
|
||||
return result.String(), nil
|
||||
if len(w) > 0 {
|
||||
// Add w+entry[0] to the dictionary.
|
||||
w = append(w, entry[0])
|
||||
dictionary[dictSize] = w
|
||||
dictSize++
|
||||
}
|
||||
w = entry
|
||||
}
|
||||
return result.String(), nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
compressed := compress("TOBEORNOTTOBEORTOBEORNOT")
|
||||
fmt.Println(compressed)
|
||||
decompressed, err := decompress(compressed)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println(decompressed)
|
||||
compressed := compress("TOBEORNOTTOBEORTOBEORNOT")
|
||||
fmt.Println(compressed)
|
||||
decompressed, err := decompress(compressed)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println(decompressed)
|
||||
}
|
||||
|
|
|
|||
51
Task/LZW-compression/Julia/lzw-compression-1.jl
Normal file
51
Task/LZW-compression/Julia/lzw-compression-1.jl
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
function compressLZW(decompressed::String)
|
||||
dictsize = 256
|
||||
dict = Dict{String,Int}(string(Char(i)) => i for i in 0:dictsize)
|
||||
result = Vector{Int}(undef, 0)
|
||||
w = ""
|
||||
for c in decompressed
|
||||
wc = string(w, c)
|
||||
if haskey(dict, wc)
|
||||
w = wc
|
||||
else
|
||||
push!(result, dict[w])
|
||||
dict[wc] = dictsize
|
||||
dictsize += 1
|
||||
w = string(c)
|
||||
end
|
||||
end
|
||||
if !isempty(w) push!(result, dict[w]) end
|
||||
return result
|
||||
end
|
||||
|
||||
function decompressLZW(compressed::Vector{Int})
|
||||
dictsize = 256
|
||||
dict = Dict{Int,String}(i => string('\0' + i) for i in 0:dictsize)
|
||||
result = IOBuffer()
|
||||
w = string(Char(compressed[1]))
|
||||
write(result, w)
|
||||
for k in compressed[2:end]
|
||||
if haskey(dict, k)
|
||||
entry = dict[k]
|
||||
elseif k == dictsize
|
||||
entry = string(w, w[1])
|
||||
else
|
||||
error("bad compressed k: $k")
|
||||
end
|
||||
write(result, entry)
|
||||
dict[dictsize] = string(w, entry[1])
|
||||
dictsize += 1
|
||||
w = entry
|
||||
end
|
||||
return String(take!(result))
|
||||
end
|
||||
|
||||
original = ["0123456789", "TOBEORNOTTOBEORTOBEORNOT", "dudidudidudida"]
|
||||
compressed = compressLZW.(original)
|
||||
decompressed = decompressLZW.(compressed)
|
||||
|
||||
for (word, comp, decomp) in zip(original, compressed, decompressed)
|
||||
comprate = (length(word) - length(comp)) / length(word) * 100
|
||||
println("Original: $word")
|
||||
println("-> Compressed: $comp (compr.rate: $(round(comprate, digits=2))%)\n-> Decompressed: $decomp")
|
||||
end
|
||||
72
Task/LZW-compression/Julia/lzw-compression-2.jl
Normal file
72
Task/LZW-compression/Julia/lzw-compression-2.jl
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
""" Trie node struct """
|
||||
mutable struct TrieNode{T}
|
||||
children::Vector{Int} # child node indices (0 = none)
|
||||
code::T # LZW code for this node's phrase
|
||||
end
|
||||
TrieNode(code::Int) = TrieNode{Int}(zeros(Int, 256), code)
|
||||
|
||||
""" LZW compression """
|
||||
function compressLZW(input::Vector{UInt8})
|
||||
if isempty(input)
|
||||
return Int[]
|
||||
end
|
||||
nodes = [TrieNode(i) for i in 0:255]
|
||||
push!(nodes, TrieNode(-1))
|
||||
root = length(nodes) # == 257
|
||||
@inbounds for b in 0:255
|
||||
nodes[root].children[b+1] = b + 1 # initialize root children
|
||||
end
|
||||
|
||||
sizehint!(nodes, length(input) + root)
|
||||
result = Int[]
|
||||
sizehint!(result, length(input))
|
||||
dictsize = 256 # next code to assign
|
||||
current = nodes[root].children[input[1]+1] # guaranteed to exist
|
||||
@inbounds for i in 2:length(input)
|
||||
c = input[i]
|
||||
nxt = nodes[current].children[c+1]
|
||||
if nxt != 0
|
||||
current = nxt
|
||||
else
|
||||
push!(result, nodes[current].code)
|
||||
dictsize += 1
|
||||
push!(nodes, TrieNode(dictsize - 1))
|
||||
new_idx = length(nodes)
|
||||
nodes[current].children[c+1] = new_idx
|
||||
current = nodes[root].children[c+1]
|
||||
end
|
||||
end
|
||||
push!(result, nodes[current].code) # add last remaining
|
||||
return result
|
||||
end
|
||||
|
||||
""" LZW decompression """
|
||||
function decompressLZW(compressed::Vector{Int})
|
||||
isempty(compressed) && return UInt8[]
|
||||
dict = [[UInt8(i)] for i in 0:255]
|
||||
dictsize = 256
|
||||
w = copy(dict[compressed[1]+1])
|
||||
output = copy(w)
|
||||
@inbounds for k in Iterators.drop(compressed, 1)
|
||||
entry = k + 1 <= length(dict) ? dict[k+1] :
|
||||
k == dictsize ? vcat(w, w[1]) : error("Bad compressed code: $k")
|
||||
append!(output, entry)
|
||||
push!(dict, vcat(w, entry[1]))
|
||||
dictsize += 1
|
||||
w = entry
|
||||
end
|
||||
return output
|
||||
end
|
||||
|
||||
const texts = ["0123456789", "TOBEORNOTTOBEORTOBEORNOT", "dudidudidudida"]
|
||||
const inputs = [Vector{UInt8}(t) for t in texts]
|
||||
|
||||
const compressed = compressLZW.(inputs)
|
||||
const decompressed = decompressLZW.(compressed)
|
||||
|
||||
for (orig, comp, decomp) in zip(texts, compressed, decompressed)
|
||||
comprate = (length(orig) - length(comp)) / length(orig) * 100
|
||||
println("Original: $orig")
|
||||
println("→ Compressed: $comp (rate: $(round(comprate, digits=2))%)")
|
||||
println("→ Decompressed: $(String(decomp))\n")
|
||||
end
|
||||
49
Task/LZW-compression/Phix/lzw-compression-1.phix
Normal file
49
Task/LZW-compression/Phix/lzw-compression-1.phix
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
-- demo\rosetta\LZW_compression.exw
|
||||
with javascript_semantics
|
||||
function compress(string uncompressed)
|
||||
integer dict = new_dict(), dictSize = 255, c
|
||||
sequence result = {}
|
||||
string word = ""
|
||||
for i=0 to 255 do setd(""&i,i,dict) end for
|
||||
for c in uncompressed do
|
||||
if getd_index(word&c,dict) then
|
||||
word &= c
|
||||
else
|
||||
result &= getd(word,dict)
|
||||
dictSize += 1
|
||||
setd(word&c,dictSize,dict)
|
||||
word = ""&c
|
||||
end if
|
||||
end for
|
||||
if word!="" then
|
||||
result &= getd(word,dict)
|
||||
end if
|
||||
destroy_dict(dict)
|
||||
return result
|
||||
end function
|
||||
|
||||
function decompress(sequence compressed)
|
||||
integer dict = new_dict(), dictSize = 255, k, ki
|
||||
string dent = "", result = "", word = ""
|
||||
for i=0 to 255 do setd(i,""&i,dict) end for
|
||||
for k in compressed do
|
||||
if getd_index(k,dict) then
|
||||
dent = getd(k,dict)
|
||||
elsif k=dictSize then
|
||||
dent = word&word[1]
|
||||
else
|
||||
crash("error")
|
||||
end if
|
||||
result &= dent
|
||||
setd(dictSize,word&dent[1],dict)
|
||||
dictSize += 1
|
||||
word = dent
|
||||
end for
|
||||
destroy_dict(dict)
|
||||
return result
|
||||
end function
|
||||
|
||||
constant example = "TOBEORNOTTOBEORTOBEORNOT"
|
||||
sequence com = compress(example)
|
||||
pp(com,{pp_IntCh,true,pp_Maxlen,90})
|
||||
?decompress(com)
|
||||
195
Task/LZW-compression/Phix/lzw-compression-2.phix
Normal file
195
Task/LZW-compression/Phix/lzw-compression-2.phix
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
-- demo\rosetta\LZW_compression.exw
|
||||
with javascript_semantics
|
||||
constant CLR = 256, -- Clear table marker
|
||||
EOD = 257, -- End-of-data marker
|
||||
NEW = 258 -- New code index
|
||||
|
||||
integer bits, tmp, oBits
|
||||
|
||||
function finished_bytes(integer x)
|
||||
sequence bytes = {}
|
||||
tmp = (tmp << bits) + x
|
||||
oBits += bits
|
||||
while oBits >= 8 do
|
||||
oBits -= 8
|
||||
assert(floor(tmp/power(2,oBits)) <= 255)
|
||||
bytes &= floor(tmp/power(2,oBits))
|
||||
tmp = and_bits(tmp, power(2,oBits)-1)
|
||||
end while
|
||||
return bytes
|
||||
end function
|
||||
|
||||
function lzwEncode(sequence inp, integer maxBits)
|
||||
assert(maxBits>=9 and maxBits<=16)
|
||||
|
||||
// Encode dictionary array. For encoding, entry at code index
|
||||
// is a list of indices that follow current one, for example
|
||||
// if code 97 is 'a', code 287 is 'ab', and code 922 is 'abc',
|
||||
// then dict[97]['b'+1] = 287, dict[287]['c'+1] = 922, etc.
|
||||
sequence dict = repeat(repeat(0,256),512),
|
||||
result = {}
|
||||
oBits = 0
|
||||
tmp = 0
|
||||
bits = 9
|
||||
|
||||
integer nextCode = NEW,
|
||||
nextShift = 512
|
||||
integer code = inp[1]
|
||||
for c in inp from 2 do
|
||||
integer nc = dict[code+1][c+1]
|
||||
if nc!=0 then
|
||||
code = nc
|
||||
else
|
||||
result &= finished_bytes(code)
|
||||
nc = nextCode
|
||||
dict[code+1][c+1] = nc
|
||||
nextCode += 1
|
||||
code = c
|
||||
end if
|
||||
|
||||
// Next new code would be too long for current table.
|
||||
if nextCode = nextShift then
|
||||
// "Early increment", matches compress and GIF use,
|
||||
// some TIFF variants do a "Late increment" instead.
|
||||
bits += 1
|
||||
// Either reset table back to 9 bits...
|
||||
if bits>maxBits then
|
||||
// Table clear marker must occur before bit reset.
|
||||
result &= finished_bytes(CLR)
|
||||
bits = 9
|
||||
nextShift = 512
|
||||
nextCode = NEW
|
||||
dict = repeat(repeat(0,256),512)
|
||||
else // ... or extend table.
|
||||
nextShift *= 2
|
||||
while length(dict)<nextShift do
|
||||
dict = append(dict,repeat(0,256))
|
||||
end while
|
||||
end if
|
||||
end if
|
||||
end for
|
||||
|
||||
result &= finished_bytes(code)
|
||||
result &= finished_bytes(EOD)
|
||||
if tmp!=0 then
|
||||
assert(tmp<=#FFFF,"tmp > 16 bits")
|
||||
result &= finished_bytes(tmp)
|
||||
end if
|
||||
|
||||
return result
|
||||
end function
|
||||
|
||||
--
|
||||
-- For decoding, dictionary contains index of whatever prefix
|
||||
-- index plus trailing ubyte. i.e. like previous example,
|
||||
-- dict[1022] = { c: 'c', prev: 387 },
|
||||
-- dict[387] = { c: 'b', prev: 97 },
|
||||
-- dict[97] = { c: 'a', prev: 0 }
|
||||
-- the "back" element is used for temporarily chaining indices
|
||||
-- when resolving a code to bytes.
|
||||
--
|
||||
enum PREV, BACK, CH -- decode dict entry subscripts
|
||||
sequence dict
|
||||
integer nextShift, nextCode -- (and re-using bits)
|
||||
|
||||
procedure clearTable()
|
||||
dict = repeat({0,0,0},512)
|
||||
for j=0 to 255 do
|
||||
dict[j+1][CH] = j
|
||||
end for
|
||||
nextCode = NEW
|
||||
nextShift = 512
|
||||
bits = 9
|
||||
end procedure
|
||||
|
||||
function lzwDecode(sequence inp)
|
||||
|
||||
sequence result = {}
|
||||
integer len = length(inp),
|
||||
idx = 1,
|
||||
code = 0,
|
||||
nBits = 0,
|
||||
tmp = 0
|
||||
clearTable() // In case encoded bits didn't start with CLR.
|
||||
while idx<=len do
|
||||
while nBits < bits do
|
||||
if idx<=len then
|
||||
tmp = (tmp << 8) + inp[idx]
|
||||
idx += 1
|
||||
nBits += 8
|
||||
else
|
||||
tmp = tmp << (bits-nBits)
|
||||
nBits = bits
|
||||
end if
|
||||
end while
|
||||
nBits -= bits
|
||||
code = floor(tmp/power(2,nBits))
|
||||
tmp = and_bits(tmp,power(2,nBits)-1)
|
||||
|
||||
if code=EOD then exit end if
|
||||
if code=CLR then
|
||||
clearTable()
|
||||
elsif code>=nextCode then
|
||||
crash("Bad sequence")
|
||||
else
|
||||
integer c = code
|
||||
dict[nextCode+1][PREV] = c
|
||||
while c>255 do
|
||||
integer t = dict[c+1][PREV]
|
||||
dict[t+1][BACK] = c
|
||||
c = t
|
||||
end while
|
||||
dict[nextCode][CH] = c
|
||||
integer cc = c+1
|
||||
while dict[cc][BACK]!=0 do
|
||||
result &= dict[cc][CH]
|
||||
integer t = dict[cc][BACK]
|
||||
dict[cc][BACK] = 0
|
||||
cc = t+1
|
||||
end while
|
||||
result &= dict[cc][CH]
|
||||
|
||||
nextCode += 1
|
||||
if nextCode>=nextShift then
|
||||
// "Early increment", must match encode.
|
||||
bits += 1
|
||||
// If input was correct, we'd have hit a CLR.
|
||||
if bits>16 then crash("Too many bits") end if
|
||||
nextShift *= 2
|
||||
while length(dict)<nextShift do
|
||||
dict = append(dict,repeat(0,3))
|
||||
end while
|
||||
end if
|
||||
end if
|
||||
end while
|
||||
|
||||
if code!=EOD then crash("Bits did not end in EOD") end if
|
||||
return result
|
||||
end function
|
||||
|
||||
procedure main()
|
||||
sequence inputData = get_text(`demo\unixdict.txt`,GT_BINARY)
|
||||
integer li = length(inputData)
|
||||
printf(1,"Input size: %d\n",li)
|
||||
|
||||
sequence encoded = lzwEncode(inputData,12)
|
||||
integer le = length(encoded)
|
||||
printf(1,"Encoded size: %d (%d%%)\n",{le,le/li*100})
|
||||
|
||||
sequence decoded = lzwDecode(encoded)
|
||||
printf(1,"Decoded size: %d\n",length(decoded))
|
||||
|
||||
if length(inputData)!=length(decoded) then
|
||||
printf(1,"Error: decoded size differs\n")
|
||||
return
|
||||
end if
|
||||
for i=1 to length(inputData) do
|
||||
if inputData[i]!=decoded[i] then
|
||||
printf(1,"Bad decode at %d\n",i)
|
||||
return
|
||||
end if
|
||||
end for
|
||||
puts(1,"Decoded OK.\n")
|
||||
end procedure
|
||||
|
||||
main()
|
||||
62
Task/LZW-compression/Pluto/lzw-compression.pluto
Normal file
62
Task/LZW-compression/Pluto/lzw-compression.pluto
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
class lzw
|
||||
-- Compress a string to a list of output symbols.
|
||||
static function compress(uncompressed)
|
||||
-- Build the dictionary.
|
||||
local dict_size = 256
|
||||
local dictionary = {}
|
||||
for i = 0, dict_size - 1 do dictionary[string.char(i)] = i end
|
||||
local w = ""
|
||||
local result = {}
|
||||
local len = #uncompressed
|
||||
for {string.byte(uncompressed, 1, len)} as c do
|
||||
local cs = string.char(c)
|
||||
local wc = w .. cs
|
||||
if dictionary[wc] then
|
||||
w = wc
|
||||
else
|
||||
result:insert(dictionary[w])
|
||||
-- Add wc to the dictionary.
|
||||
dictionary[wc] = dict_size
|
||||
dict_size += 1
|
||||
w = cs
|
||||
end
|
||||
end
|
||||
|
||||
-- Output the code for w
|
||||
if w != "" then result:insert(dictionary[w]) end
|
||||
return result
|
||||
end
|
||||
|
||||
-- Decompress a list of output symbols to a string.
|
||||
static function decompress(compressed)
|
||||
-- Build the dictionary.
|
||||
local dict_size = 256
|
||||
local dictionary = {}
|
||||
for i = 0, dict_size - 1 do dictionary[i] = string.char(i) end
|
||||
local w = string.char(compressed[1])
|
||||
local result = w
|
||||
for i = 2, #compressed do
|
||||
local k = compressed[i]
|
||||
local entry
|
||||
if dictionary[k] then
|
||||
entry = dictionary[k]
|
||||
elseif k == dict_size then
|
||||
entry = w .. string.char(string.byte(w[1]))
|
||||
else
|
||||
error($"Bad compressed k: {k}")
|
||||
end
|
||||
result ..= entry
|
||||
|
||||
-- Add w .. entry[1] to the dictionary.
|
||||
dictionary[dict_size] = w .. string.char(string.byte(entry[1]))
|
||||
dict_size += 1
|
||||
w = entry
|
||||
end
|
||||
return result
|
||||
end
|
||||
end
|
||||
|
||||
local compressed = lzw.compress("TOBEORNOTTOBEORTOBEORNOT")
|
||||
print(compressed:concat(", "))
|
||||
local decompressed = lzw.decompress(compressed)
|
||||
print(decompressed)
|
||||
85
Task/LZW-compression/R/lzw-compression.r
Normal file
85
Task/LZW-compression/R/lzw-compression.r
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
compressLZW <- function(decompressed) {
|
||||
dictsize <- 256
|
||||
# Create dictionary with characters 0-255
|
||||
dict <- list()
|
||||
for (i in 0:255) {
|
||||
dict[[intToUtf8(i)]] <- i
|
||||
}
|
||||
|
||||
result <- c()
|
||||
w <- ""
|
||||
|
||||
# Split string into characters
|
||||
chars <- strsplit(decompressed, "")[[1]]
|
||||
|
||||
for (c in chars) {
|
||||
wc <- paste0(w, c)
|
||||
if (!is.null(dict[[wc]])) {
|
||||
w <- wc
|
||||
} else {
|
||||
result <- c(result, dict[[w]])
|
||||
dict[[wc]] <- dictsize
|
||||
dictsize <- dictsize + 1
|
||||
w <- c
|
||||
}
|
||||
}
|
||||
|
||||
if (w != "") {
|
||||
result <- c(result, dict[[w]])
|
||||
}
|
||||
|
||||
return(result)
|
||||
}
|
||||
|
||||
decompressLZW <- function(compressed) {
|
||||
dictsize <- 256
|
||||
# Create reverse dictionary with integers 0-255 mapping to characters
|
||||
dict <- list()
|
||||
for (i in 0:255) {
|
||||
dict[[as.character(i)]] <- intToUtf8(i)
|
||||
}
|
||||
|
||||
result <- ""
|
||||
w <- intToUtf8(compressed[1])
|
||||
result <- paste0(result, w)
|
||||
|
||||
if (length(compressed) > 1) {
|
||||
for (i in 2:length(compressed)) {
|
||||
k <- compressed[i]
|
||||
|
||||
if (!is.null(dict[[as.character(k)]])) {
|
||||
entry <- dict[[as.character(k)]]
|
||||
} else if (k == dictsize) {
|
||||
entry <- paste0(w, substr(w, 1, 1))
|
||||
} else {
|
||||
stop(paste("bad compressed k:", k))
|
||||
}
|
||||
|
||||
result <- paste0(result, entry)
|
||||
dict[[as.character(dictsize)]] <- paste0(w, substr(entry, 1, 1))
|
||||
dictsize <- dictsize + 1
|
||||
w <- entry
|
||||
}
|
||||
}
|
||||
|
||||
return(result)
|
||||
}
|
||||
|
||||
# Test the functions
|
||||
original <- c("0123456789", "TOBEORNOTTOBEORTOBEORNOT", "dudidudidudida")
|
||||
compressed <- lapply(original, compressLZW)
|
||||
decompressed <- lapply(compressed, decompressLZW)
|
||||
|
||||
# Print results
|
||||
for (i in 1:length(original)) {
|
||||
word <- original[i]
|
||||
comp <- compressed[[i]]
|
||||
decomp <- decompressed[[i]]
|
||||
|
||||
comprate <- (nchar(word) - length(comp)) / nchar(word) * 100
|
||||
|
||||
cat("Original:", word, "\n")
|
||||
cat("-> Compressed:", paste(comp, collapse=" "),
|
||||
sprintf("(compr.rate: %.2f%%)", comprate), "\n")
|
||||
cat("-> Decompressed:", decomp, "\n\n")
|
||||
}
|
||||
4
Task/LZW-compression/Rebol/lzw-compression-1.rebol
Normal file
4
Task/LZW-compression/Rebol/lzw-compression-1.rebol
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
bin: compress "TOBEORNOTTOBEORTOBEORNOT" 'lzw
|
||||
;== #{07544F42454F524E4F54FBF6F3F3A77FDFFEFF}
|
||||
to string! decompress bin 'lzw
|
||||
;== "TOBEORNOTTOBEORTOBEORNOT"
|
||||
74
Task/LZW-compression/Rebol/lzw-compression-2.rebol
Normal file
74
Task/LZW-compression/Rebol/lzw-compression-2.rebol
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
Rebol [
|
||||
title: "Rosetta code: LZW compression"
|
||||
file: %LZW_compression.r3
|
||||
url: https://rosettacode.org/wiki/LZW_compression
|
||||
]
|
||||
lzw-compress: function [str [string! binary!]] [
|
||||
;; Initialize dictionary with all single-byte entries (0-255)
|
||||
;; Keys are binary! series (e.g. #{00}, #{01}, ..., #{FF})
|
||||
;; Values are their corresponding integer codes
|
||||
;; Using binary! keys avoids string encoding issues with non-ASCII bytes
|
||||
dict: copy #[]
|
||||
for i 0 255 1 [ dict/(join #{} i): i ]
|
||||
;; w is the current match buffer as a binary! series
|
||||
;; clear #{} gives us a reusable empty binary! to build patterns into
|
||||
w: clear #{}
|
||||
result: copy []
|
||||
foreach c to binary! str [
|
||||
;; Extend current pattern by appending the new byte c
|
||||
;; join creates a new binary! series each time (w is not mutated here)
|
||||
wc: join w c
|
||||
either dict/:wc [
|
||||
;; Extended pattern wc exists in dictionary — keep growing the match
|
||||
;; w now points to the new longer series wc
|
||||
w: wc
|
||||
][
|
||||
;; Extended pattern wc is new — emit code for longest known match w
|
||||
append result dict/:w
|
||||
;; Register the new pattern wc in the dictionary with the next code
|
||||
dict/:wc: length? dict
|
||||
;; Reuse w's storage by clearing it and appending just the current byte
|
||||
;; This avoids allocating a new binary! series for the reset
|
||||
append clear w c
|
||||
]
|
||||
]
|
||||
;; Flush the last buffered pattern if anything remains in w
|
||||
if (length? w) > 0 [ append result select dict w ]
|
||||
result
|
||||
]
|
||||
lzw-decompress: function [codes [block!]] [
|
||||
;; Initialize the reverse dictionary (code -> binary) for ASCII 0-255
|
||||
;; This is the inverse of the compress dictionary
|
||||
dict: copy []
|
||||
for i 0 255 1 [append dict join #{} i]
|
||||
|
||||
;; Read the first code and convert it to a binary to start the output
|
||||
w: join #{} codes/1
|
||||
result: copy w
|
||||
foreach code next codes [
|
||||
unless entry: pickz dict code [
|
||||
;; Code not yet in dict (pattern references itself)
|
||||
;; This happens when the encoder emits a code it just added
|
||||
assert [code = length? dict]
|
||||
;; The new entry is the previous pattern + its own first byte
|
||||
entry: join w w/1
|
||||
]
|
||||
;; Emit the decoded entry to output
|
||||
append result entry
|
||||
;; Add new dictionary entry: previous pattern + first byte of current entry
|
||||
append dict join w entry/1
|
||||
;; Current entry becomes the new previous pattern
|
||||
w: entry
|
||||
]
|
||||
to string! result
|
||||
]
|
||||
|
||||
foreach text [
|
||||
"TOBEORNOTTOBEORTOBEORNOT"
|
||||
"štěstí v neštěstí"
|
||||
][
|
||||
print ["Original: " text]
|
||||
print ["Compresed: " blk: lzw-compress text]
|
||||
print ["Decompressed:" lzw-decompress blk]
|
||||
prin LF
|
||||
]
|
||||
47
Task/LZW-compression/V-(Vlang)/lzw-compression.v
Normal file
47
Task/LZW-compression/V-(Vlang)/lzw-compression.v
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
fn compress(uncompressed string) []int {
|
||||
mut wsg := ""
|
||||
mut result := []int{}
|
||||
mut dict_size := 256
|
||||
mut dict := map[string]int{}
|
||||
for ial in 0 .. 256 { dict[rune(ial).str()] = ial }
|
||||
for cal in uncompressed.runes() {
|
||||
wc := wsg + rune(cal).str()
|
||||
if wc in dict { wsg = wc }
|
||||
else {
|
||||
result << dict[wsg]
|
||||
dict[wc] = dict_size
|
||||
dict_size++
|
||||
wsg = rune(cal).str()
|
||||
}
|
||||
}
|
||||
if wsg != "" { result << dict[wsg] }
|
||||
return result
|
||||
}
|
||||
|
||||
fn decompress(compressed []int) ?string {
|
||||
mut dict_size := 256
|
||||
mut dict := map[int]string{}
|
||||
for ial in 0 .. 256 { dict[ial] = rune(ial).str() }
|
||||
mut wsg := dict[compressed[0]]
|
||||
mut result := wsg
|
||||
for kal in compressed[1..] {
|
||||
entry := if kal in dict { dict[kal] }
|
||||
else if kal == dict_size { wsg + wsg[0].ascii_str() }
|
||||
else { return none }
|
||||
result += entry
|
||||
dict[dict_size] = wsg + entry[0].ascii_str()
|
||||
dict_size++
|
||||
wsg = entry
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
fn main() {
|
||||
comp := compress("TOBEORNOTTOBEORTOBEORNOT")
|
||||
println(comp)
|
||||
decomp := decompress(comp) or {
|
||||
println("Failed to decompress")
|
||||
return
|
||||
}
|
||||
println(decomp)
|
||||
}
|
||||
74
Task/LZW-compression/VBScript/lzw-compression.vbs
Normal file
74
Task/LZW-compression/VBScript/lzw-compression.vbs
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
Option Explicit
|
||||
Const numchars=127 'plain ASCII
|
||||
|
||||
Function LZWCompress(si)
|
||||
Dim oDict, intMaxCode, i,z,ii,ss,strCurrent,strNext,j
|
||||
Set oDict = CreateObject("Scripting.Dictionary")
|
||||
ReDim a(Len(si))
|
||||
|
||||
intMaxCode = numchars
|
||||
For i = 0 To numchars
|
||||
oDict.Add Chr(i), i
|
||||
Next
|
||||
'strCurrent = ofread.ReadText(1)
|
||||
strCurrent = Left(si,1)
|
||||
j=0
|
||||
For ii=2 To Len(si)
|
||||
strNext = Mid(si,ii,1)
|
||||
ss=strCurrent & strNext
|
||||
If oDict.Exists(ss) Then
|
||||
strCurrent = ss
|
||||
Else
|
||||
a(j)=oDict.Item(strCurrent) :j=j+1
|
||||
intMaxCode = intMaxCode + 1
|
||||
oDict.Add ss, intMaxCode
|
||||
strCurrent = strNext
|
||||
End If
|
||||
Next
|
||||
a(j)=oDict.Item(strCurrent)
|
||||
ReDim preserve a(j)
|
||||
LZWCompress=a
|
||||
Set oDict = Nothing
|
||||
End Function
|
||||
|
||||
Function lzwUncompress(sc)
|
||||
Dim intNext, intCurrent, intMaxCode, i,ss,istr,s,j
|
||||
s=""
|
||||
reDim dict(1000)
|
||||
intMaxCode = numchars
|
||||
For i = 0 To numchars : dict(i)= Chr(i) : Next
|
||||
intCurrent=sc(0)
|
||||
|
||||
For j=1 To UBound(sc)
|
||||
ss=dict(intCurrent)
|
||||
s= s & ss
|
||||
intMaxCode = intMaxCode + 1
|
||||
intnext=sc(j)
|
||||
If intNext<intMaxCode Then
|
||||
dict(intMaxCode)=ss & Left(dict(intNext), 1)
|
||||
Else
|
||||
dict(intMaxCode)=ss & Left(ss, 1)
|
||||
End If
|
||||
intCurrent = intNext
|
||||
Next
|
||||
s= s & dict(intCurrent)
|
||||
lzwUncompress=s
|
||||
End function
|
||||
|
||||
Sub printvec(a)
|
||||
Dim s,i,x
|
||||
s="("
|
||||
For i=0 To UBound (a)
|
||||
s=s & x & a(i)
|
||||
x=", "
|
||||
Next
|
||||
WScript.echo s &")"
|
||||
End sub
|
||||
|
||||
Dim a,b
|
||||
b="TOBEORNOTTOBEORTOBEORNOT"
|
||||
WScript.Echo b
|
||||
a=LZWCompress (b)
|
||||
printvec(a)
|
||||
WScript.echo lzwUncompress (a )
|
||||
wscript.quit 1
|
||||
186
Task/LZW-compression/Zig/lzw-compression.zig
Normal file
186
Task/LZW-compression/Zig/lzw-compression.zig
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
const std = @import("std");
|
||||
const ArrayList = std.ArrayList;
|
||||
const HashMap = std.HashMap;
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
// Custom hash function for byte slices
|
||||
fn hashBytes(bytes: []const u8) u64 {
|
||||
var hash: u64 = 0;
|
||||
for (bytes) |byte| {
|
||||
hash = hash *% 31 +% byte;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
// Context for HashMap with byte slice keys
|
||||
const ByteSliceContext = struct {
|
||||
pub fn hash(self: @This(), key: []const u8) u64 {
|
||||
_ = self;
|
||||
return hashBytes(key);
|
||||
}
|
||||
|
||||
pub fn eql(self: @This(), a: []const u8, b: []const u8) bool {
|
||||
_ = self;
|
||||
return std.mem.eql(u8, a, b);
|
||||
}
|
||||
};
|
||||
|
||||
const DictMap = HashMap([]const u8, u32, ByteSliceContext, std.hash_map.default_max_load_percentage);
|
||||
const ReverseDictMap = HashMap(u32, []const u8, std.hash_map.AutoContext(u32), std.hash_map.default_max_load_percentage);
|
||||
|
||||
fn compress(allocator: Allocator, data: []const u8) !ArrayList(u32) {
|
||||
// Build initial dictionary
|
||||
var dictionary = DictMap.init(allocator);
|
||||
defer {
|
||||
// Clean up allocated dictionary keys
|
||||
var iterator = dictionary.iterator();
|
||||
while (iterator.next()) |entry| {
|
||||
allocator.free(entry.key_ptr.*);
|
||||
}
|
||||
dictionary.deinit();
|
||||
}
|
||||
|
||||
// Initialize dictionary with single bytes (0-255)
|
||||
for (0..256) |i| {
|
||||
const key = try allocator.alloc(u8, 1);
|
||||
key[0] = @intCast(i);
|
||||
try dictionary.put(key, @intCast(i));
|
||||
}
|
||||
|
||||
var w = ArrayList(u8){};
|
||||
defer w.deinit(allocator);
|
||||
|
||||
var compressed = ArrayList(u32){};
|
||||
|
||||
for (data) |b| {
|
||||
// Create wc = w + b
|
||||
var wc = try w.clone(allocator);
|
||||
defer wc.deinit(allocator);
|
||||
try wc.append(allocator, b);
|
||||
|
||||
if (dictionary.contains(wc.items)) {
|
||||
// Update w to wc
|
||||
w.deinit(allocator);
|
||||
w = try wc.clone(allocator);
|
||||
} else {
|
||||
// Write w to output
|
||||
if (w.items.len > 0) {
|
||||
const code = dictionary.get(w.items).?;
|
||||
try compressed.append(allocator, code);
|
||||
}
|
||||
|
||||
// wc is a new sequence; add it to the dictionary
|
||||
const new_key = try allocator.dupe(u8, wc.items);
|
||||
try dictionary.put(new_key, @intCast(dictionary.count()));
|
||||
|
||||
// Reset w to single byte b
|
||||
w.deinit(allocator);
|
||||
w = ArrayList(u8){};
|
||||
try w.append(allocator, b);
|
||||
}
|
||||
}
|
||||
|
||||
// Write remaining output if necessary
|
||||
if (w.items.len > 0) {
|
||||
const code = dictionary.get(w.items).?;
|
||||
try compressed.append(allocator, code);
|
||||
}
|
||||
|
||||
return compressed;
|
||||
}
|
||||
|
||||
fn decompress(allocator: Allocator, data: []const u32) !ArrayList(u8) {
|
||||
if (data.len == 0) {
|
||||
return ArrayList(u8){};
|
||||
}
|
||||
|
||||
// Build the dictionary
|
||||
var dictionary = ReverseDictMap.init(allocator);
|
||||
defer {
|
||||
// Clean up allocated dictionary values
|
||||
var iterator = dictionary.iterator();
|
||||
while (iterator.next()) |entry| {
|
||||
allocator.free(entry.value_ptr.*);
|
||||
}
|
||||
dictionary.deinit();
|
||||
}
|
||||
|
||||
// Initialize dictionary with single bytes (0-255)
|
||||
for (0..256) |i| {
|
||||
const value = try allocator.alloc(u8, 1);
|
||||
value[0] = @intCast(i);
|
||||
try dictionary.put(@intCast(i), value);
|
||||
}
|
||||
|
||||
// Get first sequence
|
||||
const first_entry = dictionary.get(data[0]).?;
|
||||
var w = try allocator.dupe(u8, first_entry);
|
||||
defer allocator.free(w);
|
||||
|
||||
var decompressed = ArrayList(u8){};
|
||||
try decompressed.appendSlice(allocator, w);
|
||||
|
||||
for (data[1..]) |k| {
|
||||
var entry: []const u8 = undefined;
|
||||
var should_free_entry = false;
|
||||
|
||||
if (dictionary.contains(k)) {
|
||||
entry = dictionary.get(k).?;
|
||||
} else if (k == dictionary.count()) {
|
||||
// Special case: k is not in dictionary yet
|
||||
var temp_entry = try allocator.alloc(u8, w.len + 1);
|
||||
@memcpy(temp_entry[0..w.len], w);
|
||||
temp_entry[w.len] = w[0];
|
||||
entry = temp_entry;
|
||||
should_free_entry = true;
|
||||
} else {
|
||||
std.debug.panic("Invalid dictionary key: {}\n", .{k});
|
||||
}
|
||||
|
||||
try decompressed.appendSlice(allocator, entry);
|
||||
|
||||
// New sequence; add it to the dictionary
|
||||
var new_sequence = try allocator.alloc(u8, w.len + 1);
|
||||
@memcpy(new_sequence[0..w.len], w);
|
||||
new_sequence[w.len] = entry[0];
|
||||
try dictionary.put(@intCast(dictionary.count()), new_sequence);
|
||||
|
||||
// Update w
|
||||
allocator.free(w);
|
||||
w = try allocator.dupe(u8, entry);
|
||||
|
||||
if (should_free_entry) {
|
||||
allocator.free(entry);
|
||||
}
|
||||
}
|
||||
|
||||
return decompressed;
|
||||
}
|
||||
|
||||
pub fn main() !void {
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
defer _ = gpa.deinit();
|
||||
const allocator = gpa.allocator();
|
||||
|
||||
const input = "TOBEORNOTTOBEORTOBEORNOT";
|
||||
|
||||
var compressed = try compress(allocator, input);
|
||||
defer compressed.deinit(allocator);
|
||||
|
||||
std.debug.print("Compressed: " , .{} );
|
||||
for (compressed.items) |code| {
|
||||
std.debug.print("{} ", .{code});
|
||||
}
|
||||
std.debug.print("\n" , .{});
|
||||
|
||||
var decompressed = try decompress(allocator, compressed.items);
|
||||
defer decompressed.deinit(allocator);
|
||||
|
||||
std.debug.print("Decompressed bytes: " , .{});
|
||||
for (decompressed.items) |byte| {
|
||||
std.debug.print("{} ", .{byte});
|
||||
}
|
||||
std.debug.print("\n" , .{});
|
||||
|
||||
std.debug.print("Decompressed: {s}\n", .{decompressed.items});
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue