Browse Source

finished and renamed compression utility

git-svn-id: https://svn.inf.ethz.ch/svn/lecturers/a2/trunk@6661 8c9fc860-2736-0410-a75d-ab315db34111
eth.guenter 9 years ago
parent
commit
73f16c8542
2 changed files with 569 additions and 388 deletions
  1. 0 388
      source/Huffman.mod
  2. 569 0
      source/OZip.mod

+ 0 - 388
source/Huffman.mod

@@ -1,388 +0,0 @@
-module Huffman; (** AUTHOR GF; PURPOSE "files and streams compression"; *)
-
-import Streams, Commands, Files, Strings, Kernel;
-
-const 
-	BlockSize = 8*1024;
-	HTag = 00FF00F1H ;
-	
-type 
-	HuffmanNode = object 
-		var 
-			frequency: longint;
-			left, right: HuffmanNode;		(* both nil in case of leaf *)
-			pattern: char;						
-		
-		procedure & Init( patt: char; freq: longint );
-		begin
-			pattern := patt;  frequency := freq;  left := nil;  right := nil
-		end Init;
-		
-		procedure AddChildren( l, r: HuffmanNode );
-		begin
-			left := l;  right := r;  frequency := l.frequency + r.frequency
-		end AddChildren;
-			
-	end HuffmanNode;
-	
-	
-	Codebits = record
-		bitsize: longint;
-		val: longint
-	end;
-	
-	CodeTable = array 256 of Codebits;
-	
-		
-	HuffmanCode = object
-		var 
-			wsize, bitsize: longint;
-			buffer: pointer to array BlockSize div 2 of longint;
-			lastval, lastbits: longint;
-		
-		procedure &Init;
-		begin  
-			new( buffer );  Clear
-		end Init;
-		
-		procedure Clear;
-		begin
-			wsize := 0;  lastval := 0;  lastbits := 0
-		end Clear;
-		
-		
-		procedure AppendBits( const bits: Codebits );
-		var 
-			bitsize, val, addval, addbits, shift: longint;
-		begin
-			bitsize := bits.bitsize;  val := bits.val;
-			if lastbits + bitsize > 32 then
-				addbits := 32 - lastbits;  shift := bitsize - addbits;
-				addval := lsh( val, -shift );
-				lastval := lsh( lastval, addbits ) + addval;
-				dec( bitsize, addbits );  dec( val, lsh( addval, shift ) );
-				buffer[wsize] := lastval;  inc( wsize );  lastval := 0;  lastbits := 0
-			end;
-			lastval := lsh( lastval, bitsize ) + val;  inc( lastbits, bitsize );
-			if lastbits = 32 then
-				buffer[wsize] := lastval;  inc( wsize );  lastval := 0;  lastbits := 0
-			end
-		end AppendBits;
-		
-		
-		procedure WriteCode( w: Streams.Writer );
-		var i: longint;
-		begin
-			bitsize := 32*wsize + lastbits;
-			if lastbits > 0 then  
-				buffer[wsize] := ash( lastval, 32 - lastbits );  inc( wsize ); 
-			end;
-			
-			w.RawLInt( bitsize );
-			for i := 0 to wsize - 1 do  w.RawLInt( buffer[i] )  end;
-			w.Update
-		end WriteCode;
-		
-		
-		procedure ReadCode( r: Streams.Reader );
-		var i, n: longint;
-		begin
-			Clear;
-			r.RawLInt( bitsize );  n := (bitsize + 31) div 32;
-			for i := 0 to n - 1 do  r.RawLInt( buffer[i] )  end
-		end ReadCode;
-		
-		
-		procedure Decode( tree: HuffmanNode;  w: Streams.Writer );
-		var i, x: longint; n: HuffmanNode;
-		begin
-			i := 0;
-			repeat
-				n := tree; 
-				repeat
-					if i mod 32 = 0 then  x := buffer[i div 32]  end;
-					if ash( x, i mod 32 ) < 0 then  n := n.left  else  n := n.right  end;
-					inc( i )
-				until n.left = nil;	(* leaf *)
-				w.Char( n.pattern )
-			until i >= bitsize;
-			w.Update
-		end Decode;
-	
-	end HuffmanCode;
-		
-
-	Pattern = record
-		frequency: longint;
-		pattern: char
-	end;
-	
-	PatternFrequencies = pointer to array of Pattern;		(* ordered by frequency *)
-	
-	
-	procedure Encode*( r: Streams.Reader;  w: Streams.Writer );
-	var 
-		buffer: HuffmanCode;  i, n, needed, ofs, got, chunksize, timeout: longint;
-		codeTable: array 256 of Codebits;
-		pf: PatternFrequencies;
-		plaintext: array BlockSize of char;
-		timer: Kernel.Timer;
-	begin 
-		new( buffer );  new( timer );
-		w.RawLInt( HTag );
-		loop
-			if r is Files.Reader then
-				r.Bytes( plaintext, 0, BlockSize, chunksize );
-			else
-				(* give reader some time (~3 sec) to accumulate data *)
-				timeout := 100;  ofs := 0;  needed := BlockSize;
-				repeat  n := r.Available( );
-					if n > 0 then
-						if n > needed then  n := needed  end;
-						r.Bytes( plaintext, ofs, n, got );  inc( ofs, got );  dec( needed, got )
-					end;
-					if needed > 0 then 
-						if timeout <= 1600 then  timer.Sleep( timeout );  timeout := 2*timeout
-						else  needed := 0
-						end;  
-					end;
-				until needed = 0;
-				chunksize := ofs
-			end;
-			if chunksize < 1 then  exit  end;
-			pf := CountPatterns( plaintext, chunksize );
-			InitCodeTable( codeTable, NewHuffmanTree( pf ) );
-			buffer.Clear; 
-			for i := 0 to chunksize - 1 do  
-				buffer.AppendBits( codeTable[ord( plaintext[i] )] );
-			end;
-			WriteFrequencies( pf, w );
-			buffer.WriteCode( w );
-			w.Update
-		end
-	end Encode;
-	
-		
-	procedure Decode*( r: Streams.Reader;  w: Streams.Writer; var msg: array of char ): boolean;
-	var 
-		tree: HuffmanNode;
-		buffer: HuffmanCode;
-		tag: longint;
-	begin 
-		r.RawLInt( tag );
-		if tag # HTag  then
-			msg := "Huffman.Decode: bad input (compressed stream expected)"; 
-			return false
-		end;
-		new( buffer );
-		while r.Available( ) >= 11 do
-			tree := NewHuffmanTree( ReadFrequencies( r ) );
-			buffer.ReadCode( r );
-			buffer.Decode( tree,  w )
-		end;
-		return true
-	end Decode;
-		
-	
-	procedure CountPatterns( const block: array of char; blksize: longint ): PatternFrequencies;
-	var 
-		i, n, start: longint;
-		a: array 256 of Pattern;
-		pf: PatternFrequencies;
-		
-			procedure Quicksort( low, high: longint );  
-			var 
-				i, j, m: longint;  tmp: Pattern;
-			begin
-				if low < high then
-					i := low;  j := high;  m := (i + j) div 2;
-					repeat
-						while a[i].frequency < a[m].frequency do  inc( i )  end;
-						while a[j].frequency > a[m].frequency do  dec( j )  end;
-						if i <= j then
-							if i = m then  m := j
-							elsif j = m then  m := i
-							end;
-							tmp := a[i];  a[i] := a[j];  a[j] := tmp;
-							inc( i );  dec( j )
-						end;
-					until i > j;
-					Quicksort( low, j );  Quicksort( i, high )
-				end
-			end Quicksort;
-	
-	begin
-		for i := 0 to 255 do  a[i].pattern := chr( i );  a[i].frequency := 0  end;
-		for i := 0 to blksize - 1 do  inc( a[ord( block[i] )].frequency )  end;
-		for i := 0 to 255 do  
-			if a[i].frequency > 0 then (* scale => [1..101H] *)
-				a[i].frequency := 100H * a[i].frequency div blksize + 1;
-			end
-		end;
-		Quicksort( 0, 255 );	(* sort patterns by frequency *)
-		i := 0;
-		while a[i].frequency = 0 do  inc( i )  end; 	(* skip unused patterns *)
-		n := 256 - i;  start := i;
-		new( pf, n );
-		for i := 0 to n - 1 do  pf[i] := a[start + i]  end;
-		return pf
-	end CountPatterns;
-		
-	
-	procedure NewHuffmanTree( pf: PatternFrequencies ): HuffmanNode;
-	var 
-		i, start, top: longint;  n, n2: HuffmanNode;
-		a: pointer to array of HuffmanNode;
-		patt: char;
-	begin
-		new( a, len( pf^ ) );  top := len( pf^ ) - 1;
-		for i := 0 to top do  new( a[i], pf[i].pattern, pf[i].frequency )  end;
-		if top = 0 then  
-			(* the whole, probably last small block contains only a single pattern *)
-			patt := chr( (ord( a[0].pattern ) + 1) mod 256 );	(* some different pattern *)
-			new( n, 0X, 0 );  new( n2, patt, 0 );  n.AddChildren( n2, a[0] );
-		else
-			start := 0;  
-			while start < top do  
-				new( n, 0X, 0 );  n.AddChildren( a[start], a[start+1] ); 
-				i := start + 1;  
-				while (i < top) & (a[i+1].frequency < n.frequency) do  a[i] := a[i+1];  inc( i )  end;
-				a[i] := n;  
-				inc( start );
-			end
-		end;
-		return n
-	end NewHuffmanTree;
-	
-	
-	procedure InitCodeTable( var table: CodeTable; huffmanTree: HuffmanNode );
-	var 
-		start: Codebits;
-	
-		procedure Traverse( node: HuffmanNode;  bits: Codebits );
-		begin
-			if node.left = nil then  (* leaf *)
-				table[ord( node.pattern )] := bits;
-			else
-				inc( bits.bitsize );  
-				bits.val := 2*bits.val;  Traverse( node.right, bits );	(* ..xx0 *)
-				bits.val := bits.val + 1;  Traverse( node.left, bits );	(* ..xx1 *)
-			end;
-		end Traverse;
-	
-	begin
-		start.bitsize := 0;  start.val := 0;
-		Traverse( huffmanTree, start );
-	end InitCodeTable;
-	
-	
-	procedure ReadFrequencies( r: Streams.Reader ): PatternFrequencies;
-	var
-		i, n: longint; 
-		pf: PatternFrequencies;
-	begin
-		r.RawNum( n );  
-		new( pf, n );
-		for i := 0 to n - 1 do
-			r.RawNum( pf[i].frequency );  r.Char( pf[i].pattern ); 
-		end;
-		return pf
-	end ReadFrequencies;
-	
-	procedure WriteFrequencies( pf: PatternFrequencies; w: Streams.Writer );
-	var i, n: longint;
-	begin
-		n := len( pf^ );
-		w.RawNum( n );
-		for i := 0 to n - 1 do 
-			w.RawNum( pf[i].frequency );  w.Char( pf[i].pattern );
-		end;
-	end WriteFrequencies;
-	
-	
-	procedure OpenNewFile( const name: array of char ): Files.File;
-	var
-		name2: array 128 of char;  res: longint;
-	begin
-		if Files.Old( name ) # nil then
-			copy( name, name2);  Strings.Append( name2, ".Bak" );
-			Files.Rename( name, name2, res )
-		end;
-		return Files.New( name )
-	end OpenNewFile;
-	
-	
-	procedure EncodeFile*( c: Commands.Context );
-	var
-		f1, f2: Files.File;
-		r: Files.Reader;  w: Files.Writer;
-		name1, name2: array 128 of char;
-	begin
-		if c.arg.GetString( name1 ) then
-			if ~c.arg.GetString( name2 ) then
-				name2 := name1;
-				Strings.Append( name2, ".hc" )
-			end;
-			f1 := Files.Old( name1 );
-			if f1 # nil then
-				Files.OpenReader( r, f1, 0 ); 
-				f2 := OpenNewFile( name2 );  Files.OpenWriter( w, f2, 0 );
-				Encode( r, w );
-				w.Update;
-				Files.Register( f2 )
-			else
-				c.error.String( "could not open file  " ); c.error.String( name1 ); c.error.Ln
-			end
-		else
-			c.error.String( "usage: Huffman.EncodeFile filename [filename] ~ " ); c.error.Ln;
-		end;
-		c.error.Update
-	end EncodeFile;
-	
-	
-	procedure DecodeFile*( c: Commands.Context );
-	var
-		f1, f2: Files.File;
-		r: Files.Reader;  w: Files.Writer;
-		name1, name2, msg: array 128 of char;
-	begin
-		if c.arg.GetString( name1 ) then
-			if ~c.arg.GetString( name2 ) then
-				name2 := name1;
-				if Strings.EndsWith( ".hc", name2 ) then  name2[Strings.Length( name2 ) - 3] := 0X
-				else Strings.Append( name2, ".uncomp" )
-				end;
-			end;
-			f1 := Files.Old( name1 );
-			if f1 # nil then
-				Files.OpenReader( r, f1, 0 );	 
-				f2 := OpenNewFile( name2 );  Files.OpenWriter( w, f2, 0 );
-				if Decode( r, w, msg ) then
-					w.Update;
-					Files.Register( f2 )	
-				else
-					c.error.String( msg ); c.error.Ln
-				end
-			else
-				c.error.String( "could not open file  " ); c.error.String( name1 ); c.error.Ln
-			end
-		else
-			c.error.String( "usage: Huffman.DecodeFile filename [filename] ~ " ); c.error.Ln;
-		end;
-		c.error.Update
-	end DecodeFile;
-	
-
-
-end Huffman.
-
-
-	Huffman.EncodeFile   Huffman.mod ~
-	Huffman.EncodeFile   Huffman.Obj ~
-	Huffman.EncodeFile   uebung01.pdf ~
-		
-	Huffman.DecodeFile   Huffman.mod.hc  TTT.mod ~
-	Huffman.DecodeFile   Huffman.Obj.hc  TTT.Obj ~
-	Huffman.DecodeFile   uebung01.pdf.hc  TTT.pdf ~
-	
-	SystemTools.Free Huffman ~

+ 569 - 0
source/OZip.mod

@@ -0,0 +1,569 @@
+module OZip; (** AUTHOR GF; PURPOSE "files and streams compression tool"; *)
+
+import Streams, Commands, Files, Strings, Kernel, Log := KernelLog;
+
+const 
+	BlockSize = 8*1024;
+	ComprTag = longint(0FEFD1F2FH);
+	Suffix = ".oz";
+	
+type 
+
+	HuffmanNode = object 
+		var 
+			frequency: longint;
+			left, right: HuffmanNode;		(* both nil in case of leaf *)
+			pattern: char;						
+		
+		procedure & Init( patt: char; freq: longint );
+		begin
+			pattern := patt;  frequency := freq;  left := nil;  right := nil
+		end Init;
+		
+		procedure AddChildren( l, r: HuffmanNode );
+		begin
+			left := l;  right := r;  frequency := l.frequency + r.frequency
+		end AddChildren;
+			
+	end HuffmanNode;
+	
+	
+	Codebits = record
+		bitsize: longint;
+		val: longint
+	end;
+	
+	CodeTable = array 256 of Codebits;
+	
+		
+	HuffmanCode = object
+		var 
+			wsize, bitsize: longint;
+			buffer: pointer to array BlockSize div 3 of longint;
+			lastval, lastbits: longint;
+		
+		procedure &Init;
+		begin  
+			new( buffer );  Clear
+		end Init;
+		
+		procedure Clear;
+		begin
+			wsize := 0;  lastval := 0;  lastbits := 0
+		end Clear;
+		
+		
+		
+		procedure WriteCode( w: Streams.Writer );
+		var i: longint;
+		begin
+			bitsize := 32*wsize + lastbits;
+			if lastbits > 0 then  
+				buffer[wsize] := ash( lastval, 32 - lastbits );  inc( wsize ); 
+			end;
+			
+			w.RawLInt( bitsize );
+			for i := 0 to wsize - 1 do  w.RawLInt( buffer[i] )  end;
+			w.Update
+		end WriteCode;
+		
+		
+		procedure ReadCode( r: Streams.Reader );
+		var i, n: longint;
+		begin
+			Clear;
+			r.RawLInt( bitsize );  n := (bitsize + 31) div 32;
+			for i := 0 to n - 1 do  r.RawLInt( buffer[i] )  end
+		end ReadCode;
+		
+		
+		procedure AppendBits( const bits: Codebits );
+		var 
+			bitsize, val, addval, addbits, shift: longint;
+		begin
+			bitsize := bits.bitsize;  val := bits.val;
+			if lastbits + bitsize > 32 then
+				addbits := 32 - lastbits;  shift := bitsize - addbits;
+				addval := lsh( val, -shift );
+				lastval := lsh( lastval, addbits ) + addval;
+				dec( bitsize, addbits );  dec( val, lsh( addval, shift ) );
+				buffer[wsize] := lastval;  inc( wsize );  lastval := 0;  lastbits := 0
+			end;
+			lastval := lsh( lastval, bitsize ) + val;  inc( lastbits, bitsize );
+			if lastbits = 32 then
+				buffer[wsize] := lastval;  inc( wsize );  lastval := 0;  lastbits := 0
+			end
+		end AppendBits;
+				
+		procedure EncodeBlock( const tab: CodeTable;  const buf: array of char;  length: longint );
+		var i: longint;
+		begin
+			Clear;
+			for i := 0 to length - 1 do AppendBits( tab[ord( buf[i] )] )  end
+		end EncodeBlock;
+		
+		procedure DecodeBlock( tree: HuffmanNode;  var buf: array of char;  var length: longint );
+		var i, x, pos: longint; n: HuffmanNode; 
+		begin
+			i := 0;  pos := 0;
+			repeat
+				n := tree; 
+				repeat
+					if i mod 32 = 0 then  x := buffer[i div 32]  end;
+					if ash( x, i mod 32 ) < 0 then  n := n.left  else  n := n.right  end;
+					inc( i )
+				until n.left = nil;	(* leaf *)
+				buf[pos] := n.pattern;  inc( pos )
+			until i >= bitsize;
+			length := pos
+		end DecodeBlock;
+	
+	end HuffmanCode;
+		
+
+	Pattern = record
+		frequency: longint;
+		pattern: char
+	end;
+	
+	PatternArray = array 256 of Pattern;
+	PatternFrequencies = pointer to array of Pattern;		(* ordered by frequency *)
+	
+	MTFList = pointer to record
+		next: MTFList;
+		byte: char;
+	end;
+	
+	
+	procedure Compress*( r: Streams.Reader;  w: Streams.Writer );
+	var 
+		huff: HuffmanCode;  n, needed, ofs, got, chunksize, timeout: longint;
+		codeTable: CodeTable;
+		pf: PatternFrequencies;
+		bwIndex: longint;
+		buffer: pointer to array of char;
+		timer: Kernel.Timer;
+	begin 
+		new( huff );  new( timer );  new( buffer, BlockSize );
+		w.RawLInt( ComprTag );
+		loop
+			if r is Files.Reader then
+				r.Bytes( buffer^, 0, BlockSize, chunksize );
+			else
+				(* give reader some time (~3 sec) to accumulate data *)
+				timeout := 100;  ofs := 0;  needed := BlockSize;
+				repeat  n := r.Available( );
+					if n > 0 then
+						if n > needed then  n := needed  end;
+						r.Bytes( buffer^, ofs, n, got );  inc( ofs, got );  dec( needed, got )
+					end;
+					if needed > 0 then 
+						if timeout <= 1600 then  timer.Sleep( timeout );  timeout := 2*timeout
+						else  needed := 0
+						end;  
+					end;
+				until needed = 0;
+				chunksize := ofs
+			end;
+			if chunksize < 1 then  exit  end;
+			BWEncode( buffer^, chunksize, bwIndex );
+			pf := CountPatterns( buffer^, chunksize );
+			WriteFrequencies( pf, w );
+			InitCodeTable( codeTable, BuildHuffmanTree( pf ) );
+			huff.EncodeBlock( codeTable, buffer^, chunksize );
+			huff.WriteCode( w );
+			w.RawLInt( bwIndex );
+			w.Update;
+			if r is Files.Reader then  Log.Char( '.' )  end
+		end
+	end Compress;
+	
+	
+		
+	procedure Uncompress*( r: Streams.Reader;  w: Streams.Writer;  var msg: array of char ): boolean;
+	var 
+		tree: HuffmanNode;
+		huff: HuffmanCode;
+		buffer: pointer to array of char;
+		tag, chunksize, i, bwIndex: longint;
+	begin 
+		r.RawLInt( tag );
+		if tag # ComprTag  then
+			msg := "OZip.Uncompress: bad input (compressed stream expected)"; 
+			return false
+		end;
+		new( huff ); new( buffer, BlockSize );
+		while r.Available( ) >= 15 (* min size of a compressed block *) do
+			tree := BuildHuffmanTree( ReadFrequencies( r ) );
+			huff.ReadCode( r );
+			huff.DecodeBlock( tree,  buffer^, chunksize );
+			r.RawLInt( bwIndex );
+			BWDecode( buffer^, chunksize, bwIndex );
+			for i := 0 to chunksize - 1 do  w.Char( buffer[i] )  end
+		end;
+		w.Update;
+		return true
+	end Uncompress;
+		
+	
+	procedure BuildPatternFrequencies( var a: PatternArray ): PatternFrequencies;
+	var 
+		i, n, start: longint;
+		pf: PatternFrequencies;
+		
+		procedure SortPF( low, high: longint );  
+		var 
+			i, j, m: longint;  tmp: Pattern;
+		begin
+			if low < high then
+				i := low;  j := high;  m := (i + j) div 2;
+				repeat
+					while a[i].frequency < a[m].frequency do  inc( i )  end;
+					while a[j].frequency > a[m].frequency do  dec( j )  end;
+					if i <= j then
+						if i = m then  m := j  elsif j = m then  m := i  end;
+						tmp := a[i];  a[i] := a[j];  a[j] := tmp;
+						inc( i );  dec( j )
+					end;
+				until i > j;
+				SortPF( low, j );  SortPF( i, high )
+			end
+		end SortPF;
+		
+	begin
+		SortPF( 0, 255 );	(* sort patterns by frequency *)
+		i := 0;
+		while a[i].frequency = 0 do  inc( i )  end; 	(* skip unused patterns *)
+		n := 256 - i;  start := i;
+		new( pf, n );
+		for i := 0 to n - 1 do  pf[i] := a[start + i]  end;
+		return pf
+	end BuildPatternFrequencies;
+	
+	procedure CountPatterns( const block: array of char; blksize: longint ): PatternFrequencies;
+	var 
+		i: longint;  a: PatternArray;
+	begin
+		for i := 0 to 255 do  a[i].pattern := chr( i );  a[i].frequency := 0  end;
+		for i := 0 to blksize - 1 do  inc( a[ord( block[i] )].frequency )  end;
+		for i := 0 to 255 do  
+			if a[i].frequency > 0 then (* scale => [1..101H] *)
+				a[i].frequency := 100H * a[i].frequency div blksize + 1;
+			end
+		end;
+		return BuildPatternFrequencies( a )
+	end CountPatterns;
+		
+	
+	procedure BuildHuffmanTree( pf: PatternFrequencies ): HuffmanNode;
+	var 
+		i, start, top: longint;  n, n2: HuffmanNode;
+		a: pointer to array of HuffmanNode;
+		patt: char;
+	begin
+		new( a, len( pf^ ) );  top := len( pf^ ) - 1;
+		for i := 0 to top do  new( a[i], pf[i].pattern, pf[i].frequency )  end;
+		if top = 0 then  
+			(* the whole, probably last small block contains only a single pattern *)
+			patt := chr( (ord( a[0].pattern ) + 1) mod 256 );	(* some different pattern *)
+			new( n, 0X, 0 );  new( n2, patt, 0 );  n.AddChildren( n2, a[0] );
+		else
+			start := 0;  
+			while start < top do  
+				new( n, 0X, 0 );  n.AddChildren( a[start], a[start+1] ); 
+				i := start + 1;  
+				while (i < top) & (a[i+1].frequency < n.frequency) do  a[i] := a[i+1];  inc( i )  end;
+				a[i] := n;  
+				inc( start );
+			end
+		end;
+		return n
+	end BuildHuffmanTree;
+	
+	
+	procedure InitCodeTable( var table: CodeTable; huffmanTree: HuffmanNode );
+	var 
+		start: Codebits; i: longint;
+	
+		procedure Traverse( node: HuffmanNode;  bits: Codebits );
+		begin
+			if node.left = nil then  (* leaf *)
+				table[ord( node.pattern )] := bits;
+			else
+				inc( bits.bitsize );  
+				bits.val := 2*bits.val;  Traverse( node.right, bits );	(* ..xx0 *)
+				bits.val := bits.val + 1;  Traverse( node.left, bits );	(* ..xx1 *)
+			end;
+		end Traverse;
+	
+	begin
+		for i := 0 to 255 do  table[i].bitsize := 0;  table[i].val := 0  end;
+		start.bitsize := 0;  start.val := 0;
+		Traverse( huffmanTree, start );
+	end InitCodeTable;
+	
+	
+	procedure ReadFrequencies( r: Streams.Reader ): PatternFrequencies;
+	var i, n: longint; 
+		pf: PatternFrequencies;
+		a: PatternArray;
+	begin
+		r.RawNum( n );  
+		if n > 0 then
+			new( pf, n );
+			for i := 0 to n - 1 do  r.RawNum( pf[i].frequency );  r.Char( pf[i].pattern )  end
+		else
+			for i := 0 to 255 do  a[i].pattern := chr( i );  r.RawNum( a[i].frequency )  end;
+			pf := BuildPatternFrequencies( a )
+		end;
+		return pf
+	end ReadFrequencies;
+	
+	procedure WriteFrequencies( pf: PatternFrequencies; w: Streams.Writer );
+	var i, n: longint;
+		a: array 256 of longint;
+	begin
+		n := len( pf^ );
+		if n < 128 then
+			w.RawNum( n );
+			for i := 0 to n - 1 do  w.RawNum( pf[i].frequency );  w.Char( pf[i].pattern )  end
+		else
+			w.RawNum( 0 );
+			for i := 0 to 255 do  a[i] := 0  end;
+			for i := 0 to n -1 do  a[ord( pf[i].pattern )] := pf[i].frequency  end;
+			for i := 0 to 255 do  w.RawNum( a[i] )  end
+		end
+	end WriteFrequencies;
+	
+	
+	(* Borrows Wheeler Transformation, Encode*)
+	procedure BWEncode( var buf: array of char; length: longint; var index: longint );
+	type
+		Rotation = record 
+			shift: longint; 
+			lastbyte: char  
+		end;
+	var 
+		r: pointer to array of Rotation;
+		i, j: longint;
+	
+		procedure Less( a, b: longint ): boolean;
+		var i, x1, x2, i1, i2: longint;  c1, c2: char;
+		begin
+			i := 0; x1 := r[a].shift;  x2 := r[b].shift;
+			repeat
+				i1 := x1 + i;  if i1 >= length then  dec( i1, length )  end;
+				i2 := x2 + i;  if i2 >= length then  dec( i2, length )  end;
+				c1 := buf[i1];  c2 := buf[i2];
+				if c1 < c2 then  return true  elsif c1 > c2 then  return false  end;
+				inc( i )
+			until i = length;
+			return false
+		end Less;
+				
+		procedure SortR( lo, hi: longint );
+		var i, j, m: longint;  tmp: Rotation;
+		begin
+			if lo < hi then
+				i := lo;  j := hi;  m := (lo + hi) div 2;
+				repeat
+					while Less( i, m ) do  inc( i )  end;  
+					while Less( m, j ) do  dec( j )  end;
+					if i <= j then
+						if m = i then  m := j  elsif  m = j then  m := i  end;
+						tmp := r[i];  r[i] := r[j];  r[j] := tmp;
+						inc( i );  dec( j )
+					end
+				until i > j;
+				SortR( lo, j );  SortR( i, hi )
+			end
+		end SortR;
+		
+	begin
+		new( r, length );
+		for i := 0 to length - 1 do
+			r[i].shift := i; 
+			if i = 0 then  j := length - 1  else  j := i - 1  end;
+			r[i].lastbyte := buf[j]
+		end;
+		SortR( 0, length -1 );
+		(* replace buffer by column L *)
+		for i := 0 to length -1 do  buf[i] := r[i].lastbyte  end;
+		(* find index of the original row *)
+		index := 0;  while r[index].shift # 0 do  inc( index )  end;
+		MTFEncode( buf, length )
+	end BWEncode;
+	
+			
+	(* Borrows Wheeler Transformation, Decode*)	
+	procedure BWDecode( var buf: array of char; length, index: longint );
+	var 
+		l, f: pointer to array of char;; 
+		lc, fc: pointer to array of longint;
+		xn: array 256 of longint;  i, j, n: longint;
+		ch: char;
+		
+		procedure SortF( lo, hi: longint );
+		var i, j, m: longint;  tmp: char;
+		begin
+			if lo < hi then
+				i := lo;  j := hi;  m := (lo + hi) div 2;
+				repeat
+					while f[i] < f[m] do  inc( i )  end;  
+					while f[m] < f[j] do  dec( j )  end;
+					if i <= j then
+						if m = i then  m := j  elsif m = j then  m := i  end;
+						tmp := f[i];  f[i] := f[j];  f[j] := tmp;
+						inc( i );  dec( j )
+					end
+				until i > j;
+				SortF( lo, j );  SortF( i, hi )
+			end
+		end SortF;
+		
+	begin
+		MTFDecode( buf, length );
+		new( l, length );  new( f, length ); new( lc, length );  new( fc, length );
+		for i := 0 to 255 do  xn[i] := 0  end;
+		for i := 0 to length - 1 do 
+			l[i] := buf[i];  f[i] := l[i];
+			j := ord( l[i] );  lc[i] := xn[j];  inc( xn[j] )
+		end;
+		SortF( 0, length - 1 );
+		for i := 0 to 255 do  xn[i] := 0  end;
+		for i := 0 to length - 1 do 
+			j := ord( f[i] );  fc[i] := xn[j];  inc( xn[j] )
+		end;
+		for i := 0 to length - 1 do
+			ch := f[index];  n := fc[index];  buf[i] := ch;  index := 0;
+			while (l[index] # ch) or (lc[index] # n) do  inc( index )  end
+		end;
+	end BWDecode;
+	
+	
+	(* Borrows Wheeler move to front *)	
+	procedure MTFEncode( var buf: array of char;  length: longint );
+	var alpha, l, m: MTFList;  i, k: longint;  ch: char;
+	begin
+		alpha := nil;
+		for i := 0 to 255 do
+			new( l );  l.next := alpha;  l.byte := chr( 255 - i );  alpha := l;
+		end;
+		for i := 0 to length - 1 do
+			ch := buf[i];
+			if alpha.byte = ch then  k := 0
+			else
+				l := alpha;  m := alpha.next;  k := 1;
+				while m.byte # ch do  inc( k );  l := m;  m := m.next  end;
+				l.next := m.next;  m.next := alpha;  alpha := m
+			end;
+			buf[i] := chr( k )
+		end
+	end MTFEncode;
+			
+	(* Borrows Wheeler move to front *)	
+	procedure MTFDecode( var buf: array of char;  length: longint );
+	var alpha, l, m: MTFList;  i, c: longint;  ch: char; 
+	begin
+		alpha := nil;
+		for i := 0 to 255 do
+			new( l );  l.next := alpha;  l.byte := chr( 255 - i );  alpha := l;
+		end;
+		for i := 0 to length - 1 do
+			ch := buf[i];
+			if ch # 0X then 
+				c := ord( ch );  l := alpha;
+				while c > 1 do  l := l.next;  dec( c )  end;
+				m := l.next;  l.next := m.next;  m.next := alpha;  
+				alpha := m
+			end;
+			buf[i] := alpha.byte;
+		end
+	end MTFDecode;
+	
+	
+	procedure NewFile( const name: array of char ): Files.File;
+	var
+		name2: array 128 of char;  res: longint;
+	begin
+		if Files.Old( name ) # nil then
+			copy( name, name2);  Strings.Append( name2, ".Bak" );
+			Files.Rename( name, name2, res );
+			Log.String( "Backup created in " ); Log.String( name2 );  Log.Ln
+		end;
+		return Files.New( name )
+	end NewFile;
+	
+	
+	procedure CompressFile*( c: Commands.Context );
+	var
+		f1, f2: Files.File;
+		r: Files.Reader;  w: Files.Writer;
+		name1, name2: array 128 of char;
+	begin
+		if c.arg.GetString( name1 ) then
+			if ~c.arg.GetString( name2 ) then
+				name2 := name1;  Strings.Append( name2, Suffix )
+			end;
+			f1 := Files.Old( name1 );
+			if f1 # nil then
+				Files.OpenReader( r, f1, 0 ); 
+				f2 := NewFile( name2 );  Files.OpenWriter( w, f2, 0 );
+				Compress( r, w );  w.Update;  Files.Register( f2 );
+				Log.Ln;
+				Log.String( "Compression finished, outfile = " );  Log.String( name2 );  Log.Ln; 
+			else
+				c.error.String( "could not open file  " );  c.error.String( name1 );  c.error.Ln
+			end
+		else
+			c.error.String( "usage: OZip.CompressFile infile [outfile] ~ " );  c.error.Ln;
+		end;
+		c.error.Update
+	end CompressFile;
+	
+	
+	procedure UncompressFile*( c: Commands.Context );
+	var
+		f1, f2: Files.File;
+		r: Files.Reader;  w: Files.Writer;
+		name1, name2, msg: array 128 of char;
+	begin
+		if c.arg.GetString( name1 ) then
+			if ~c.arg.GetString( name2 ) then
+				name2 := name1;
+				if Strings.EndsWith( Suffix, name2 ) then  name2[Strings.Length( name2 ) - 3] := 0X
+				else  Strings.Append( name2, ".uncomp" )
+				end
+			end;
+			f1 := Files.Old( name1 );
+			if f1 # nil then
+				Files.OpenReader( r, f1, 0 );	 
+				f2 := NewFile( name2 );  Files.OpenWriter( w, f2, 0 );
+				if Uncompress( r, w, msg ) then
+					w.Update;  Files.Register( f2 )	
+				else
+					c.error.String( msg );  c.error.Ln
+				end
+			else
+				c.error.String( "could not open file  " );  c.error.String( name1 );  c.error.Ln
+			end
+		else
+			c.error.String( "usage: OZip.UncompressFile infile [outfile] ~ " );  c.error.Ln;
+		end;
+		c.error.Update
+	end UncompressFile;
+	
+end OZip.
+
+
+	OZip.CompressFile   OZip.mod ~
+	OZip.CompressFile   OZip.Obj ~
+	OZip.CompressFile   summary.pdf ~
+		
+	OZip.UncompressFile   OZip.mod.oz  TTT.mod ~
+	OZip.UncompressFile   OZip.Obj.oz  TTT.Obj ~
+	OZip.UncompressFile   summary.pdf.oz  TTT.pdf ~
+	
+	SystemTools.Free OZip  ~
+	
+