Skip to content

Commit 8c51aec

Browse files
committed
new file: Encoding/huffman_file_compression.sf
1 parent 64ce635 commit 8c51aec

16 files changed

+229
-72
lines changed

Encoding/huffman_file_compression.sf

+166
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
#!/usr/bin/ruby
2+
3+
# Compress/decompress small files using Huffman coding.
4+
5+
define(
6+
CHUNK_SIZE = 1024**2, # 1 MB
7+
SIGNATURE = ('HFM' + 3.chr),
8+
)
9+
10+
func walk(Hash n, String s, Hash h) {
11+
if (n.has(:a)) {
12+
h{n{:a}} = s
13+
return nil
14+
}
15+
walk(n{:0}, s+'0', h)
16+
walk(n{:1}, s+'1', h)
17+
}
18+
19+
func make_tree(Array bytes) {
20+
21+
var nodes = bytes.freq.sort.map_2d { |b,f|
22+
Hash(a => b, freq => f)
23+
}
24+
25+
var n = Hash()
26+
while (nodes.sort_by!{|h| h{:freq} }.len > 1) {
27+
n = Hash(:0 => nodes.shift, :1 => nodes.shift)
28+
n{:freq} = (n{:0}{:freq} + n{:1}{:freq})
29+
nodes << n
30+
}
31+
32+
walk(n, '', n{:tree} = Hash())
33+
return n
34+
}
35+
36+
func huffman_encode(Array bytes, Hash t) {
37+
bytes.map { t{_} }.join
38+
}
39+
40+
func huffman_decode (String bits, Hash tree) {
41+
bits.gsub(Regex('(' + tree.keys.sort_by { .len }.join('|') + ')'), {|s| tree{s} })
42+
}
43+
44+
func create_huffman_entry (Array bytes, FileHandle out_fh) {
45+
46+
var h = make_tree(bytes){:tree}
47+
var enc = huffman_encode(bytes, h)
48+
49+
var dict = ''
50+
var codes = ''
51+
52+
for i in (0..255) {
53+
var c = (h{i} \\ '')
54+
codes += c
55+
dict += c.len.chr
56+
}
57+
58+
out_fh.print(dict)
59+
out_fh.print(pack("B*", codes))
60+
out_fh.print(pack("N", enc.len))
61+
out_fh.print(pack("B*", enc))
62+
}
63+
64+
# Compress file
65+
func huffman_compress_file(File input, File output) {
66+
67+
var in_fh = input.open('<:raw') || die "Can't open file <<#{input}>> for reading"
68+
var out_fh = output.open('>:raw') || die "Can't open file <<#{output}>> for writing"
69+
70+
var header = SIGNATURE
71+
72+
# Print the header
73+
out_fh.print(header)
74+
75+
while (in_fh.read(\var chunk, CHUNK_SIZE)) {
76+
create_huffman_entry([unpack('C*', chunk)], out_fh)
77+
}
78+
79+
# Close the files
80+
in_fh.close
81+
out_fh.close
82+
}
83+
84+
func read_bits (FileHandle fh, Number bits_len) {
85+
86+
var str = ''
87+
fh.read(\str, bits_len>>3)
88+
str = unpack('B*', str)
89+
90+
while (str.len < bits_len) {
91+
str += unpack('B*', fh.getc \\ break)
92+
}
93+
94+
if (str.len > bits_len) {
95+
str.substr!(0, bits_len)
96+
}
97+
98+
return str
99+
}
100+
101+
func decode_huffman_entry (FileHandle fh, FileHandle out_fh) {
102+
103+
var codes = []
104+
var codes_len = 0
105+
106+
fh.read(\var buffer, 256)
107+
108+
[unpack('C*', buffer)].map{ Num(_) }.each_kv {|c,l|
109+
if (l > 0) {
110+
codes_len += l
111+
codes << [c, l]
112+
}
113+
}
114+
115+
var codes_bin = read_bits(fh, codes_len)
116+
117+
var rev_dict = Hash()
118+
for c,l in (codes) {
119+
var code = substr(codes_bin, 0, l)
120+
codes_bin.substr!(l)
121+
rev_dict{code} = c.chr
122+
}
123+
124+
var enc_len = Num(unpack('N', 4.of{ fh.getc }.join))
125+
126+
if (enc_len > 0) {
127+
var enc_data = read_bits(fh, enc_len)
128+
out_fh.print(huffman_decode(enc_data, rev_dict))
129+
return true
130+
}
131+
132+
return false
133+
}
134+
135+
# Decompress file
136+
func huffman_decompress_file(File input, File output) {
137+
138+
var in_fh = input.open('<:raw') || die "Can't open file <<#{input}>> for reading"
139+
140+
if (SIGNATURE.len.of { in_fh.getc }.join != SIGNATURE) {
141+
die "Not a HFM archive!\n"
142+
}
143+
144+
var out_fh = output.open('>:raw') || die "Can't open file <<#{output}>> for writing"
145+
146+
while (!in_fh.eof) {
147+
decode_huffman_entry(in_fh, out_fh) || break
148+
}
149+
150+
in_fh.close
151+
out_fh.close
152+
}
153+
154+
ARGV.getopt!('d', \var decode)
155+
156+
var file = File(ARGV.shift) || do {
157+
say "usage: #{File(__MAIN__).basename} [-d] [input file]"
158+
Sys.exit(2)
159+
}
160+
161+
if (decode || file.match(/\.hfm\.enc\z/)) {
162+
huffman_decompress_file(file, File("output.hfm.dec"))
163+
}
164+
else {
165+
huffman_compress_file(file, File("output.hfm.enc"))
166+
}

Encoding/integers_binary_encoding.sf

+5-7
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@
66

77
# Encode and decode a random list of integers into a binary string.
88

9-
func encode_integers (Array compressed) {
9+
func encode_integers (Array integers) {
1010

1111
var counts = []
1212
var count = 0
1313
var bits_per_symbol = 2
1414
var processed_len = 0
1515

16-
for k in (compressed) {
16+
for k in (integers) {
1717
while (k >= bits_per_symbol) {
1818

1919
if (count > 0) {
@@ -27,7 +27,7 @@ func encode_integers (Array compressed) {
2727
++count
2828
}
2929

30-
counts << [bits_per_symbol.len(2)-1, compressed.len - processed_len]
30+
counts << [[bits_per_symbol.len(2)-1, integers.len - processed_len]].grep { .tail > 0 }...
3131

3232
var clen = counts.len
3333
var header = clen.chr
@@ -40,8 +40,7 @@ func encode_integers (Array compressed) {
4040
var bits = []
4141

4242
for b,len in (counts) {
43-
len > 0 || next
44-
bits << compressed.splice(0, len).map{ sprintf('%0*s', b, .as_bin) }...
43+
bits << integers.splice(0, len).map{ sprintf('%0*s', b, .as_bin) }...
4544
}
4645

4746
header + pack('B*', bits.join)
@@ -59,14 +58,13 @@ func decode_integers (String str) {
5958
count_len.times {
6059
var b = str_shift(\str).ord
6160
var l = Num(unpack('N', 4.of { str_shift(\str) }.join))
62-
counts << [b, l]
61+
counts << [b, l] if (l > 0)
6362
}
6463

6564
var chunks = []
6665
var bits = str.ascii2bin
6766

6867
for b,len in (counts) {
69-
len > 0 || next
7068
chunks << bits.substr(0, b*len).slices(b)...
7169
bits.substr!(b*len)
7270
}

Game solvers/asciiplanes-player.sf

+14-18
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ var miss_char = AIR
4747
var head_char = HEAD
4848
var seed = 0
4949

50-
func usage {
50+
func print_usage {
5151
print <<"EOT"
5252
usage: #{__MAIN__} [options]
5353
@@ -74,28 +74,24 @@ EOT
7474
Sys.exit
7575
}
7676

77-
func ver {
77+
func print_version {
7878
print "#{pkgname} #{version}\n"
7979
Sys.exit
8080
}
8181

8282
if (ARGV) {
83-
var getopt =
84-
try { frequire('Getopt::Long') }
85-
catch { STDERR.print("Can't load the 'Getopt::Long' Perl module...\n"); Sys.exit(2) }
86-
87-
getopt.GetOptions(
88-
'board-size|size=i' => func(_,v) { BOARD_SIZE = Num(v) },
89-
'planes-num=i' => func(_,v) { PLANES_NUM = Num(v) },
90-
'seed=i' => func(_,v) { seed = Num(v) },
91-
'head-char=s' => func(_,v) { head_char = Str(v) },
92-
'hit-char=s' => func(_,v) { hit_char = Str(v) },
93-
'miss-char=s' => func(_,v) { miss_char = Str(v) },
94-
'wrap!' => func(_,v) { wrap_plane = Bool(v) },
95-
'colors!' => func(_,v) { use_colors = Bool(v) },
96-
'simulate!' => func(_,v) { simulate = Bool(v) },
97-
'help|h|?' => func(*_) { usage() },
98-
'version|v|V' => func(*_) { ver() },
83+
ARGV.getopt!(
84+
'board-size|size=i' => \BOARD_SIZE,
85+
'planes-num=i' => \PLANES_NUM,
86+
'seed=i' => \seed,
87+
'head-char=s' => \head_char,
88+
'hit-char=s' => \hit_char,
89+
'miss-char=s' => \miss_char,
90+
'wrap!' => \wrap_plane,
91+
'colors!' => \use_colors
92+
'simulate!' => \simulate,
93+
'help|h|?' => print_usage,
94+
'version|v|V' => print_version,
9995
)
10096
}
10197

Games/asciiplanes.sf

+12-16
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ var wrap_plane = false
3838
var hit_char = %q{O}
3939
var miss_char = %q{`}
4040

41-
func usage {
41+
func print_usage {
4242
print <<"EOT"
4343
usage: #{__MAIN__} [options]
4444
@@ -62,25 +62,21 @@ EOT
6262
Sys.exit
6363
}
6464

65-
func ver {
65+
func print_version {
6666
print "#{pkgname} #{version}\n"
6767
Sys.exit
6868
}
6969

7070
if (ARGV) {
71-
var getopt =
72-
try { frequire('Getopt::Long') }
73-
catch { STDERR.print("Can't load the 'Getopt::Long' Perl module...\n"); Sys.exit(2) }
74-
75-
getopt.GetOptions(
76-
'board-size|size=i' => func(_,v) { BOARD_SIZE = Num(v) },
77-
'planes-num=i' => func(_,v) { PLANES_NUM = Num(v) },
78-
'hit-char=s' => func(_,v) { hit_char = Str(v) },
79-
'miss-char=s' => func(_,v) { miss_char = Str(v) },
80-
'wrap!' => func(_,v) { wrap_plane = Bool(v) },
81-
'colors!' => func(_,v) { use_colors = Bool(v) },
82-
'help|h|?' => func(*_) { usage() },
83-
'version|v|V' => func(*_) { ver() },
71+
ARGV.getopt!(
72+
'board-size|size=i' => \BOARD_SIZE,
73+
'planes-num=i' => \PLANES_NUM,
74+
'hit-char=s' => \hit_char,
75+
'miss-char=s' => \miss_char,
76+
'wrap!' => \wrap_plane,
77+
'colors!' => \use_colors,
78+
'help|h|?' => print_usage,
79+
'version|v|V' => print_version,
8480
)
8581
}
8682

@@ -247,7 +243,7 @@ while (count > 0) {
247243
if (point == nil) {
248244
info_board[x][y] = miss_char
249245
}
250-
elsif (var m = point.match(/^head_(\d)$/i)) {
246+
elsif (point.match(/^head_(\d)$/i)) { |m|
251247
var dir = Num(m[0])
252248
var item = plane_chars[(PLANES_NUM - count) % plane_chars.len]
253249
var code = directions[dir]

Math/AGM_calculate_pi.sf

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
func AGM_PI(digits=100) {
1010

1111
var acc = 8+digits
12-
local Number!PREC = *"#{4*digits}"
12+
local Number!PREC = numify(4*digits)
1313

1414
var (an, bn, tn, pn) = (1, sqrt(1/2), 1/4, 1)
1515

Math/bernoulli_numbers_from_primes.sf

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ func bern_from_primes(n) {
1919

2020
var log2B = (log(4*Num.tau*n)/2 + n*log(n) - n*log(Num.tau) - n)/log(2)
2121

22-
local Number!PREC = *(int(n + log2B) + (n <= 90 ? 18 : 0))
22+
local Number!PREC = numify(int(n + log2B) + (n <= 90 ? 18 : 0))
2323

2424
var K = (n! * 2 / Num.tau**n)
2525
var d = 1

Math/continued_fraction_factorization_method.sf

+2-2
Original file line numberDiff line numberDiff line change
@@ -151,10 +151,10 @@ func cffmm (n, multiplier=1) {
151151
while (A.len < L) {
152152

153153
y = (r*z - y)
154-
z = ((N - y*y) / z) # exact division
154+
z = idiv(N - y*y, z) # exact division
155155
r = (ROUND_DIVISION ? idiv_round(x+y, z) : idiv(x+y, z))
156156

157-
(f1, f2) = (f2, (r*f2 + f1) % n)
157+
(f1, f2) = (f2, addmod(mulmod(r, f2, n), f1, n))
158158

159159
if (z.is_square) {
160160
resolve_factor(f1, z)

Math/continued_fraction_factorization_method_simple.sf

+6-6
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,11 @@ func cffm (n) {
6363

6464
do {
6565
y = (r*z - y)
66-
z = ((n - y*y) // z) #/
67-
r = round((x + y) / z)
66+
z = idiv(n - y*y, z)
67+
r = idiv(x + y, z)
6868

69-
var a = ((x*f2 + e2) % n)
70-
var b = (a*a % n)
69+
var a = addmod(x*f2, e2, n)
70+
var b = mulmod(a, a, n)
7171

7272
if (b.is_square) {
7373
resolve_factor(a, b)
@@ -80,8 +80,8 @@ func cffm (n) {
8080
S{b} = a
8181
}
8282

83-
(f1, f2) = (f2, (r*f2 + f1) % n)
84-
(e1, e2) = (e2, (r*e2 + e1) % n)
83+
(f1, f2) = (f2, addmod(r*f2, f1, n))
84+
(e1, e2) = (e2, addmod(r*e2, e1, n))
8585

8686
} while (z != 1)
8787

Math/function_inverse_binary_search.sf

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
func binary_inverse (n, f, min=0, max=n, prec=192) {
1313

14-
local Number!PREC = *"#{prec}"
14+
local Number!PREC = prec.numify
1515

1616
(min, max) = (max, min) if (min > max)
1717

Math/function_inverse_mediant_inequality.sf

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
func mediant_inverse (n, f, min=0, max=n, prec=192) {
1818

19-
local Number!PREC = *"#{prec}"
19+
local Number!PREC = prec.numify
2020

2121
(min, max) = (max, min) if (min > max)
2222

0 commit comments

Comments
 (0)