home *** CD-ROM | disk | FTP | other *** search
/ ftp.barnyard.co.uk / 2015.02.ftp.barnyard.co.uk.tar / ftp.barnyard.co.uk / cpm / walnut-creek-CDROM / CPM / TURBOPAS / SQZTURBO.LBR / SQZMAIN.INC < prev    next >
Text File  |  2000-06-30  |  15KB  |  390 lines

  1. { Initialize all nodes to single element
  2.   binary trees with zero weight and depth. }
  3. Procedure ZeroTree;
  4.   var i: integer;
  5.   begin
  6.     For i := 0 to NumNodes do with Node[i] do
  7.       begin
  8.         Weight:=0.0; TDepth:=0; LChild:=NoChild; RChild:=NoChild;
  9.       end;
  10.   end; { Procedure ZeroTree }
  11.  
  12. Procedure PutWe(w: integer);
  13.   var b1, b2: char;
  14.   begin
  15.     b1 := chr(w and $FF);      b2 := chr(w shr 8);
  16.     WriteOutFile(b1);              WriteOutFile(b2);
  17.   end; { Procedure PutWe }
  18.  
  19. Function GetCNr: char;
  20.   Var return: char;
  21.  
  22.     Function Alike: boolean;
  23.       begin
  24.         If EOFile then EOFlag:=true else NewChar:=GetC;
  25.         If EOFile then Alike:=false
  26.         else           Alike:=( (NewChar=LastChar) and (LikeCt<255) );
  27.       end; { Function Alike }
  28.  
  29.     Procedure NoHistory; {set up the state machine}
  30.       begin
  31.         State:=SentChar;
  32.         If not EOFile then LastChar:=GetC;
  33.         EoFlag:=EoFile; If EOFlag then State:=EndFile;
  34.         Return:=LastChar;
  35.       end; { Procedure NoHistory }
  36.  
  37.     Procedure SentAChar;   {LastChar is sent, need lookahead}
  38.  
  39.         Procedure SentDLE;
  40.           begin
  41.             State:=NoHist; Return:=chr(0);
  42.           end; { Procedure SentDLE }
  43.  
  44.         Procedure CheckAlike;
  45.           begin
  46.             LikeCt:=1;   While alike do LikeCt:=succ(LikeCt);
  47.             Case LikeCt of
  48.                 1: begin
  49.                      LastChar:=NewChar; Return:=LastChar;
  50.                      EoFlag:=EoFile;
  51.                    end;
  52.                 2: begin { just pass through }
  53.                      State:=SendNewC; Return:=LastChar;
  54.                    end;
  55.               else begin
  56.                      State:=SendCnt; Return:=DLE;
  57.                    end;
  58.             end;
  59.           end; { Procedure CheckAlike }
  60.  
  61.       begin
  62.         If EOFlag then State := EndFile
  63.                              {no return value, set to SPEOF in calling routine}
  64.         else
  65.           If LastChar=DLE then SentDLE
  66.           else CheckAlike;
  67.       end; { Procedure SentAChar }
  68.  
  69.     Procedure SendNewChar;   {Previous sequence complete, newchar set}
  70.       begin
  71.         EOFlag:=EOFile;
  72.         State:=SentChar; LastChar:=NewChar; Return:=LastChar;
  73.       end; { Procedure SendNewChar }
  74.  
  75.     Procedure SendCount;  {Sent DLE for repeat sequence, send count}
  76.       begin
  77.         State:=SendNewC; Return:=chr(LikeCt);
  78.       end; { Procedure SendCount }
  79.  
  80.   begin
  81.     Case State of
  82.         NoHist: NoHistory;
  83.       SentChar: SentAChar;
  84.       SendNewC: SendNewChar;
  85.        SendCnt: SendCount;
  86.            else WriteLn('Program Bug - Bad State!');
  87.     end;
  88.     GetCnr:=Return;
  89.   end; { Function GetCNr }
  90.  
  91. Procedure WriteHeader;
  92.   Var i, k, l, r, NumNodes: integer;       { NumNodes: nbr of nodes in
  93.                                                          simplified tree }
  94.   begin
  95.     PutWe(Recognize);                      { identifies as compressed }
  96.     PutWe(Crc);                            { unsigned sum of original data }
  97.  
  98.                                   { Record the original file name w/o drive }
  99.     If (InFileName[2]=':') then
  100.       InFileName:=copy(InFileName,3,length(InFileName)-2);
  101.     InFileName:=InFileName+chr(0);                    {mark end of file name}
  102.     For i:=1 to Length(InFileName) do WriteOutFile(InFileName[i]);
  103.  
  104.     { Write out a simplified decoding tree. Only the interior nodes are
  105.       written. When a child is a leaf index (representing a data value)
  106.       it is recoded as -(index + 1) to distinguish it from interior
  107.       indexes which are recoded as positive indexes in the new tree.
  108.       Note that this tree will be empty for an empty file. }
  109.  
  110.     If DcTreeHd<NumVals then NumNodes:=0
  111.     else                     NumNodes:=DcTreeHd-(pred(NumVals));
  112.     PutWe(NumNodes);
  113.  
  114.     i:=DcTreeHd;
  115.     For k:=0 to pred(NumNodes) do
  116.       begin
  117.         l:=Node[i].LChild;           r:=Node[i].RChild;
  118.         if l<NumVals then l:=-(succ(l)) else l:=DcTreeHd-l;
  119.         if r<NumVals then r:=-(succ(r)) else r:=DcTreeHd-r;
  120.         PutWe(l); { left child }     PutWe(r); { right child }
  121.         i:=pred(i);
  122.       end;
  123.   end; { Procedure WriteHeader }
  124.  
  125. {$A-}
  126.  
  127. Procedure Adjust(Top, Bottom: integer; Var List: ValType);
  128.   Var k, Temp: integer;
  129.  
  130.     { Compare two trees, if a > b return true, else return false. }
  131.     Function CmpTrees(a, b: integer): boolean;    {entry with root nodes}
  132.       begin
  133.         CmpTrees:=false;
  134.         If Node[a].Weight>Node[b].Weight then CmpTrees:=true
  135.         else
  136.           If Node[a].Weight=Node[b].Weight then
  137.             If Node[a].TDepth>Node[b].TDepth then CmpTrees:=true;
  138.       end; { Function CmpTrees }
  139.  
  140.   begin
  141.     k:=succ(2*Top);                                       { left child of top }
  142.     Temp:=List[Top];                         { remember root node of top tree }
  143.     If (k<=Bottom) then
  144.       begin
  145.         If ( k<Bottom) and (CmpTrees(List[k],List[succ(k)]) ) then k:=succ(k);
  146.       { k indexes "smaller" child (in heap of trees) of top
  147.         now make top index "smaller" of old top and smallest child }
  148.         If CmpTrees(Temp,List[k]) then
  149.           begin
  150.             List[Top]:=List[k]; List[k]:=Temp; Adjust(k,Bottom,List);
  151.           end;
  152.       end;
  153.   end; { Procedure Adjust }
  154.  
  155. {$A+}
  156.  
  157. { The count of number of occurrances of each input value have already been
  158.   prevented from exceeding MAXCOUNT. Now we must scale them so that their
  159.   sum doesn't exceed ceiling and yet no non-zero count can become zero.
  160.   This scaling prevents errors in the weights of the interior nodes of the
  161.   Huffman tree and also ensures that the codes will fit in an unsigned integer.
  162.   Rescaling is used if necessary to limit the code length. }
  163. Procedure Scale(Ceil: integer);                 { upper limit on total weight }
  164.   var i, c, ovflw, divisor: integer;
  165.       w, sum:               real;
  166.       Increased:            boolean;
  167.   begin
  168.     Repeat { Until not Increased }
  169.       Sum:=0; OvFlw:=0;
  170.       For i:=0 to pred(NumVals) do
  171.         begin
  172.           If Node[i].Weight>(Ceil-Sum) then OvFlw:=succ(OvFlw);
  173.           Sum:=Sum+Node[i].Weight;
  174.         end;
  175.       Divisor:=succ(Ovflw);
  176.     { Ensure no non-zero values are lost }
  177.       Increased:=false;
  178.       For i:=0 to pred(NumVals) do
  179.         begin
  180.           w:=Node[i].Weight;
  181.           If (w<Divisor) and (w<>0) then
  182.             begin          { Don't fail to provide a code if it's used at all }
  183.               Node[i].Weight:=Divisor; Increased:=true;
  184.             end;
  185.         end;
  186.     Until not Increased;
  187.   { Scaling factor choosen, now scale }
  188.     If Divisor>1 then
  189.       For i:=0 to pred(NumVals) do with Node[i] do
  190.         Weight:=int((Weight/Divisor)+0.5);
  191.   end; { Procedure Scale }
  192.  
  193. {$A-}
  194.   { Recursive routine to walk the indicated subtree and level
  195.     and maintain the current path code in bstree. When a leaf
  196.     is found the entire code string and length are put into
  197.     the encoding table entry for the leaf's data value.
  198.     Returns ERROR if codes are too long. }
  199.  
  200. Function BuildEnc(Level, Root: integer): integer;       {returns error or null}
  201.   Var l, r, Return: integer;
  202.   begin
  203.     Return:=Null;
  204.     l:=Node[Root].LChild; r:=Node[Root].RChild;
  205.     If (l=NoChild) and (r=NoChild) then
  206.       begin                                                {have a leaf}
  207.         CodeLen[Root]:=Level;
  208.         Code[Root]:=TCode and ($FFFF shr (16-Level));
  209.         If Level>16 then Return:=Error else Return:=Null;
  210.       end
  211.     else
  212.       begin
  213.         If l<>NoChild then
  214.           begin                                  {Clear path bit and go deeper}
  215.             TCode:=TCode and not(1 shl Level);
  216.             If BuildEnc(succ(Level),l)=Error then Return:=Error;
  217.           end;
  218.         If r<>NoChild then
  219.           begin                                    {Set path bit and go deeper}
  220.             TCode:=TCode or (1 shl Level);
  221.             If BuildEnc(succ(Level),r)=Error then Return:=Error;
  222.           end;
  223.       end;
  224.     BuildEnc:=Return;
  225.   end; { Function BuildEnc }
  226.  
  227. {$A+}
  228.  
  229. Procedure BuildTree(Var List: ValType; Len: integer);       {Huffman algorithm}
  230.   Var FreeNode:    integer;                            {next free node in tree}
  231.       LCh, RCh:    integer;              {temporaries for left, right children}
  232.       i:           integer;
  233.  
  234.     Function Maximum(a, b: integer): integer;
  235.       begin
  236.         If a>b then Maximum:=a else Maximum:=b;
  237.       end; { Function Maximum }
  238.  
  239.   begin
  240.                    { Initialize index to next available (non-leaf) node.
  241.                      Lower numbered nodes correspond to leaves (data values). }
  242.     FreeNode:=NumVals;
  243.                     { Take from list two btrees with least weight and build an
  244.                       interior node pointing to them.  This forms a new tree. }
  245.     While (Len>1) do
  246.       begin
  247.         LCh:=List[0];                           { This one will be left child }
  248.                              { delete top (least) tree from the list of trees }
  249.         Len:=pred(Len);
  250.         List[0]:=List[Len];
  251.         Adjust(0,pred(Len),List);
  252.                            { Take new top (least) tree. Reuse list slot later }
  253.         RCh:=List[0];                          { This one will be right child }
  254.             { Form new tree from the two least trees using a free node as root.
  255.               Put the new tree in the list. }
  256.         With Node[FreeNode] do
  257.           begin
  258.             LChild:=LCh; RChild:=RCh;
  259.             Weight:=Node[LCh].Weight+Node[RCh].Weight;
  260.             TDepth:=succ(Maximum(Node[LCh].TDepth,Node[RCh].TDepth));
  261.           end;
  262.         List[0]:=FreeNode;                                 {put at top for now}
  263.         FreeNode:=succ(FreeNode);                              {next free node}
  264.                                        { reheap list to get least tree at top }
  265.         Adjust(0,pred(Len),List);
  266.       end;
  267.     DcTreeHd:=List[0];                                   { head of final tree }
  268.   end; { Procedure BuildTree }
  269.  
  270. { Initialize the Huffman translation. This requires reading the input file
  271.   through any preceding translation functions to get the frequency
  272.   distribution of the various values. }
  273.  
  274. Procedure InitializeHuffman;
  275.   var c, i:     integer;
  276.       BtList:   ValType;                  { list of intermediate binary trees }
  277.       ListLen:  integer;                  { length of btlist }
  278.       Ceiling:  integer;                  { limit for scaling }
  279.  
  280.   { Heap and Adjust maintain a list of binary trees as a heap with the top
  281.     indexing the binary tree on the list which has the least weight or,
  282.     in case of equal weights, least depth in its longest path. The depth part
  283.     is not strictly necessary, but tends to avoid long codes which might
  284.     provoke rescaling. }
  285.  
  286.     Procedure Heap(Var List: ValType; l: integer);
  287.       Var i, len: integer;
  288.       begin
  289.         Len:=(l-2) div 2;
  290.         For i:=Len DownTo 0 do Adjust(i,pred(l),List);
  291.       end; { Procedure Heap }
  292.  
  293.   begin
  294.     Write('Pass 1: Analysis,');
  295.     Crc:=0; ZeroTree; State:=NoHist; EOFile:=false; EOFlag:=false;
  296.     Repeat { Until EOFlag }                    { Build frequency info in tree }
  297.       C:=ord(GetCnr);
  298.       If EOFlag then C:=SpEOF;
  299.       With Node[C] do
  300.         If Weight<MaxCount then Weight:=Weight+1.0;
  301.       If EOFlag then Write(' End of file found,');
  302.     until (EOFlag);
  303.   { PrintFrequency; }
  304.     Ceiling:=MaxCount;
  305.            { Try to build encoding table. Fail if any code is > 16 bits long. }
  306.     Repeat { Until BuildEnc(0,DcTreeHd) <> Error }
  307.       If (Ceiling<>MaxCount) then Write(' *** rescaling ***,');
  308.       Scale(Ceiling);
  309.       Ceiling:=Ceiling div 2;                        {in case we rescale again}
  310.       ListLen:=0;                  {find length of list and build single nodes}
  311.         For i:=0 to pred(NumVals) do
  312.           If Node[i].Weight>0.0 then
  313.             begin
  314.               Node[i].TDepth:=0; BtList[listlen]:=i; ListLen:=succ(ListLen);
  315.             end;
  316.       Heap(BtList,pred(ListLen));
  317.       Write(' Building tree');
  318.       BuildTree(BtList,ListLen);
  319.       For i:=0 to pred(NumVals) do CodeLen[i]:=0;
  320.     until (BuildEnc(0,DcTreeHd)<>Error);
  321.   { PrintList;}
  322.                                               { Initialize encoding variables }
  323.     CBitsRem:=0; CurIn:=0;
  324.   end; { Procedure InitializeHuffman }
  325.  
  326.   { Get an encoded byte or EOF. Reads from specified stream AS NEEDED.
  327.     There are two unsynchronized bit-byte relationships here:
  328.       The input stream bytes are converted to bit strings of various lengths
  329.       via the static variables named Cxxxxx.  These bit strings are
  330.       concatenated without padding to become the stream of encoded result
  331.       bytes, which this function returns one at a time. The EOF (end of file)
  332.       is converted to SPEOF for convenience and encoded like any other input
  333.       value. True EOF is returned after that. }
  334.  
  335. Function GetHuff: char;                    {returns byte values except for EOF}
  336.   Var RByte:      integer;                                  {Result byte value}
  337.       Need, Take: integer;                                    {numbers of bits}
  338.       Return:     integer;
  339.   begin
  340.     RByte:=0;
  341.     Need:=8;                                          {build one byte per call}
  342.     Return:=Error;                                    {start off with an error}
  343.                                    { Loop to build a byte of encoded data.
  344.                                      Initialization forces read the first time}
  345.     While Return=Error do
  346.       begin
  347.         If CBitsRem>=Need then
  348.           begin                              {Current code fullfills our needs}
  349.             If need = 0 then Return:=RByte and $00FF
  350.             else
  351.               begin
  352.                 RByte:=RByte or (CCode shl (8-Need));
  353.                                                            {take what we need}
  354.                 CCode:=CCode shr Need;
  355.                                                            {and leave the rest}
  356.                 CBitsRem:=CBitsRem-Need;
  357.                 Return:=RByte and $00FF;
  358.               end;
  359.           end
  360.         else
  361.           begin
  362.             If CBitsRem>0 then
  363.               begin                            {We need more than current code}
  364.                 RByte:=RByte or (CCode shl (8-Need));
  365.                                                            {take what there is}
  366.                 Need:=Need-CBitsRem;
  367.               end;
  368.             If curin=SpEOF then
  369.               begin
  370.                 CBitsRem:=0;
  371.                 If Need=8 then
  372.                   begin                                           {end of file}
  373.                     Done:=true;
  374.                     Return:=0;                           {any valid char value}
  375.                   end
  376.                 else Return:=RByte and $00FF;                      {data first}
  377.               end
  378.             else
  379.               begin
  380.                 CurIn:=ord(GetCnr);
  381.                 If EOFlag then CurIn:=SpEOF;
  382.                 CCode:=Code[CurIn];
  383.                 CBitsRem:=CodeLen[CurIn];
  384.               end;
  385.           end;
  386.       end;
  387.     GetHuff:=chr(Return);
  388.   end; { Function GetHuff }
  389.  
  390.