1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
| <pre>
Data: [Header (16)][Lookup table (max 7168)][Compressed data]
Header: [Unused bits at last compressed byte (3)][Length of lookup table (13)]
Lookup table: [Character (13 to 28)][Character (13 to 28)][...]
Character: [Length of binary code (4)][Original character (8)][Binary code (1 to 16)]
Compressed data: [10010101011011010101...]
<i>Numbers between brackets are length values in BITS</i>
<hr>
<script language="javascript">
// Use a long text to get a good look at the needed time
var text = "Five score years ago, a great American, in whose symbolic shadow we stand today, signed the Emancipation Proclamation. This momentous decree came as a great beacon light of hope to millions of Negro slaves who had been seared in the flames of withering injustice. It came as a joyous daybreak to end the long night of their captivity. But one hundred years later, the Negro still is not free. One hundred years later, the life of the Negro is still sadly crippled by the manacles of segregation and the chains of discrimination. One hundred years later, the Negro lives on a lonely island of poverty in the midst of a vast ocean of material prosperity. One hundred years later, the Negro is still languished in the corners of American society and finds himself an exile in his own land. And so we've come here today to dramatize a shameful condition. In a sense we've come to our nation's capital to cash a check. When the architects of our republic wrote the magnificent words of the Constitution and the Declaration of Independence, they were signing a promissory note to which every American was to fall heir. This note was a promise that all men, yes, black men as well as white men, would be guaranteed the unalienable Rights of Life, Liberty and the pursuit of Happiness. It is obvious today that America has defaulted on this promissory note, insofar as her citizens of color are concerned. Instead of honoring this sacred obligation, America has given the Negro people a bad check, a check which has come back marked insufficient funds.But we refuse to believe that the bank of justice is bankrupt. We refuse to believe that there are insufficient funds in the great vaults of opportunity of this nation. And so, we've come to cash this check, a check that will give us upon demand the riches of freedom and the security of justice.We have also come to this hallowed spot to remind America of the fierce urgency of Now. This is no time to engage in the luxury of cooling off or to take the tranquilizing drug of gradualism. Now is the time to make real the promises of democracy. Now is the time to rise from the dark and desolate valley of segregation to the sunlit path of racial justice. Now is the time to lift our nation from the quicksands of racial injustice to the solid rock of brotherhood. Now is the time to make justice a reality for all of God's children.It would be fatal for the nation to overlook the urgency of the moment. This sweltering summer of the Negro's legitimate discontent will not pass until there is an invigorating autumn of freedom and equality. Nineteen sixty-three is not an end, but a beginning. And those who hope that the Negro needed to blow off steam and will now be content will have a rude awakening if the nation returns to business as usual. And there will be neither rest nor tranquility in America until the Negro is granted his citizenship rights. The whirlwinds of revolt will continue to shake the foundations of our nation until the bright day of justice emerges.But there is something that I must say to my people, who stand on the warm threshold which leads into the palace of justice: In the process of gaining our rightful place, we must not be guilty of wrongful deeds. Let us not seek to satisfy our thirst for freedom by drinking from the cup of bitterness and hatred. We must forever conduct our struggle on the high plane of dignity and discipline. We must not allow our creative protest to degenerate into physical violence. Again and again, we must rise to the majestic heights of meeting physical force with soul force.The marvelous new militancy which has engulfed the Negro community must not lead us to a distrust of all white people, for many of our white brothers, as evidenced by their presence here today, have come to realize that their destiny is tied up with our destiny. And they have come to realize that their freedom is inextricably bound to our freedom.";
text += text;
text += text;
text += text;
begin = new Date();
// Compress string
var compressed = compress(text);
end = new Date();
alert("Ingepakt in: " + (end - begin) + " miliseconde");
document.write("Compressed length: " + compressed.length + "\n");
document.write("Original length: " + text.length + "\n");
document.write("Saved: " + parseInt(100 - compressed.length / text.length * 100) + "%\n")
document.write("Original text: " + text);
document.write("\n");
begin = new Date();
// Decompress here
var decompressed = decompress(compressed);
end = new Date();
alert("Uitgepakt in: " + (end - begin) + " miliseconde");
document.write("Unpacked text: " + decompressed);
function decompress(strCompressed)
{
var strBits = "";
// Create binary code of strCompressed
var arrBits = new Array();
for (var i=0; i < strCompressed.length; i++)
{
arrBits.push(toBinary(strCompressed.charCodeAt(i), 8));
}
strBits = arrBits.join("");
arrBits = null;
// Extract header bits
var intHeaderUnusedBits = parseInt(strBits.substr(0, 3), 2);
var intHeaderLengthLookupTable = parseInt(strBits.substr(3, 13), 2);
// Extract lookup table and create lookup table array
var i = 16;
var intBinaryCodeLength;
var strOriginalCharacter;
var intBinaryCode;
var arrLookup = new Array();
var intLoopLength = intHeaderLengthLookupTable + 16;
while (i < intLoopLength)
{
intBinaryCodeLength = parseInt(strBits.substr(i, 4), 2);
strOriginalCharacter = String.fromCharCode(parseInt(strBits.substr(i + 4, 8), 2));
intBinaryCode = strBits.substr(4 + 8 + i, intBinaryCodeLength);
arrLookup[intBinaryCode] = strOriginalCharacter;
i += 4 + 8 + intBinaryCodeLength;
}
// Extract compressed data and replace compressed data with data from lookup table array
i = 16 + intHeaderLengthLookupTable;
var strOutput = "";
var arrOutput = new Array();
intLoopLength = strBits.length - intHeaderUnusedBits;
var strTempBits = "";
while (i < intLoopLength)
{
strTempBits += strBits.charAt(i);
i++;
if (arrLookup[strTempBits])
{
arrOutput.push(arrLookup[strTempBits]);
strTempBits = strBits.charAt(i);
i++;
}
}
strOutput = arrOutput.join("");
arrOutput = null;
// Output
return strOutput;
}
function compress(strText)
{
// Variables used for creating a frequenty table
var arrChars = new Array();
var strChar = "";
var strRegExpChar = "";
var strTempText = strText;
var intTempLength;
var objTempTreeNode;
// Create the frequenty table
while (strTempText.length > 0)
{
strChar = strTempText.charAt(0);
intTempLength = strTempText.length;
// Not all characters can be used with the RegExp object, escape them
if ("[\\^$.|?*+()".indexOf(strChar) > -1)
{
strRegExpChar = "\\" + strChar;
}
else
{
strRegExpChar = strChar;
}
strTempText = strTempText.replace(new RegExp(strRegExpChar, "g"), "");
objTempTreeNode = new treeNode("LEAF");
objTempTreeNode.value = strChar;
objTempTreeNode.regex = strRegExpChar;
// Calculate weight in % of total
objTempTreeNode.weight = (intTempLength - strTempText.length) / strText.length * 100;
// Add the tree node object to the arrChars array
arrChars.push(objTempTreeNode);
}
// Sort, based on weight
arrChars.sort(function(x,y){return y.weight-x.weight});
// Put characters in a heap (this will remove all empty items which are created while
// creating the frequenty table, since javascript doesn't like array index numbers like
// "143" to be used)
var arrHeap = new Array();
for (var i in arrChars)
{
arrHeap.push(arrChars[i]);
}
// Create a Huffman table
var objLeft;
var objRight;
var objParent;
while (arrHeap.length > 1)
{
// Remove the two lowest nodes from the heap
objLeft = arrHeap.pop();
objRight = arrHeap.pop();
// Create a new parent node which will contain both child nodes
objParent = new treeNode("BRANCH");
objParent.weight = objLeft.weight + objRight.weight;
// Assign the child nodes to the parent node and put the parent node on the heap
objParent.childLeft = objLeft;
objParent.childRight = objRight;
arrHeap.push(objParent);
// Sort, based on weight
arrHeap.sort(function(x,y){return y.weight-x.weight});
}
// Create the tree which will be outputted and added to the compressed code
objParent.setBinary("");
// Create the header and lookup table and put the bits of it in front of strBits
var strBits = strText;
// Replace 0 and 1 before the actual loop, to prevent them from messing
// up all other bits. This is only an internal matter and does not change
// anything to the outputted bit sequence
strBits = strBits.replace(new RegExp("0", "g"), "\0\0ZERO\0\0");
strBits = strBits.replace(new RegExp("1", "g"), "\0\0ONE\0\0");
for (var i in arrChars)
{
if (arrChars[i].value == "0")
{
strBits = strBits.replace(new RegExp("\0\0ZERO\0\0", "g"), arrChars[i].binary);
arrChars[i].regex = "\0\0ZERO\0\0";
}
else if (arrChars[i].value == "1")
{
strBits = strBits.replace(new RegExp("\0\0ONE\0\0", "g"), arrChars[i].binary);
arrChars[i].regex = "\0\0ONE\0\0";
}
}
// Create the header and lookup table and put the bits of it in front of strBits
var strHeaderLookupTable = "";
for (var i in arrChars)
{
// Replace this character with its bit sequence
strBits = strBits.replace(new RegExp(arrChars[i].regex, "g"), arrChars[i].binary);
// Length of binary code
strHeaderLookupTable += toBinary(arrChars[i].binary.length, 4);
// Original character
strHeaderLookupTable += toBinary(arrChars[i].value.charCodeAt(0), 8);
// Binary code
strHeaderLookupTable += arrChars[i].binary;
}
var strHeaderLengthLookupTable = toBinary(strHeaderLookupTable.length, 13);
var strHeader = strHeaderLengthLookupTable + strHeaderLookupTable;
strBits = strHeader + strBits;
var strHeaderUnusedBits = toBinary(8 - ((strBits.length + 3) % 8), 3);
if (strHeaderUnusedBits.length > 3)
{
strHeaderUnusedBits = "000";
}
strBits = strHeaderUnusedBits + strBits;
// Append empty zeros to strBits to make sure it ends with an octet
while (strBits.length % 8 > 0)
{
strBits += "0";
}
// Create normal ASCII characters of the binary string in strBits
var strCompressed = "";
var arrCompressed = new Array();
for (var i = 0; i < strBits.length; i = i + 8)
{
arrCompressed.push(String.fromCharCode(parseInt(strBits.substr(i, 8), 2)));
}
strCompressed = arrCompressed.join("");
arrCompressed = null;
// Return the compressed string
return strCompressed;
}
// Object which will be created for every node in the binary Huffman tree
function treeNode(strType)
{
this.type = strType;
this.weight = null;
this.binary = null;
// Only add properties which are used by the specified type
if (strType == "LEAF")
{
// LEAF type
this.value = null;
this.regex = null;
}
else
{
// BRANCH type
this.childLeft = null;
this.childRight = null;
}
// Function which updates its binary value ánd which executes itself at its child nodes (if
// the current node is of the BRANCH type)
this.setBinary = function(strBinary)
{
this.binary = strBinary;
if (this.type == "BRANCH")
{
return this.childLeft.setBinary(strBinary + "0") + this.childRight.setBinary(strBinary + "1");
}
else
{
return this.value + strBinary + "\0";
}
}
}
// Function to convert a number to base 2. The second parameter can adjust the minimal length
// of the outputted value, which is default to 8.
function toBinary(intNumber, intLength)
{
intNumber = "0000000000000000" + intNumber.toString(2);
return intNumber.substr(intNumber.length - (intLength || 8));
}
</script> |