struct Addr
  word  Ofs;
  word  Seg;
section
  void @Ptr;
end

void @Ptr(word Seg,Ofs)
  Addr P;
  P.Seg=Seg;
  P.Ofs=Ofs;
  return @P.Ptr;
end

word GetPSP()
  asm mov     AH,62H
  asm int     21H
  asm mov     AX,BX
end

word create(char @Name)
  asm push    DS
  asm mov     AH,3CH
  asm mov     CX,00H
  asm mov     DX,SS:[BP+6]
  asm mov     DS,DX
  asm mov     DX,SS:[BP+4]
  asm int     21H
  asm pop     DS
end

word open(char @Name)
  asm push    DS
  asm mov     AH,3DH
  asm mov     AL,00H
  asm mov     DX,SS:[BP+6]
  asm mov     DS,DX
  asm mov     DX,SS:[BP+4]
  asm int     21H
  asm pop     DS
end

word reset(word F)
  asm push    DS
  asm mov     AX,4200H
  asm mov     BX,SS:[BP+4]
  asm mov     CX,0
  asm mov     DX,0
  asm int     21H
  asm pop     DS
end

word read(word F; void @Buff; word N)
  asm push    DS
  asm mov     AH,3FH
  asm mov     BX,SS:[BP+10]
  asm mov     CX,SS:[BP+4]
  asm mov     DX,SS:[BP+8]
  asm mov     DS,DX
  asm mov     DX,SS:[BP+6]
  asm int     21H
  asm pop     DS
end

word write(word F; void @Buff; word N)
  asm push    DS
  asm mov     AH,40H
  asm mov     BX,SS:[BP+10]
  asm mov     CX,SS:[BP+4]
  asm mov     DX,SS:[BP+8]
  asm mov     DS,DX
  asm mov     DX,SS:[BP+6]
  asm int     21H
  asm pop     DS
end

void close(word F)
  asm mov     AH,3EH
  asm mov     BX,SS:[BP+4]
  asm int     21H
end

int strcmp(char @S1, @S2)
  int P=0;
  while TRUE do
    if S1[P]<S2[P] then
      return -1;
    end

    if S1[P]>S2[P] then
      return  1;
    end

    if S1[P]=#0 then
      return  0;
    end

    inc P;
  end
end

void putc(char Ch)
  asm mov     AH,2
  asm mov     DL,SS:[BP+4]
  asm int     21H
end

void puts(char @St)
  word P=0;
  while St[P]!=#0 do
    putc(St[P]);
    inc  P;
  end
end

struct long
  word lo;
  word hi;
end

void lInc(long @l)
  if l.lo<65535 then
    inc l.lo;
  else
    inc l.hi;
    l.lo=0;
  end
end

void lDec(long @l)
  if l.lo>0 then
    dec l.lo;
  else
    dec l.hi;
    l.lo=65535;
  end
end

long lSum(long a, b)
  asm     mov     AX,SS:[BP+ 4]
  asm     mov     DX,SS:[BP+ 6]
  asm     mov     BX,SS:[BP+ 8]
  asm     mov     CX,SS:[BP+10]
  asm     add     AX,BX
  asm     adc     DX,CX
end

int  lCmp(long a, b)
  if a.hi<b.hi then
    return -1;
  end

  if a.hi>b.hi then
    return  1;
  end

  if a.lo<b.lo then
    return -1;
  end

  if a.lo>b.lo then
    return  1;
  end

  return    0;
end

define TXT_LEN 16384
define ARC_LEN 16384
define BLK_LEN  4096
define ARC_FWD   128

struct SNode
  byte Code;
  long Cnt;
  int  PP;
  int  P0;
  int  P1;
end

struct SInfo
  byte Code;
  long Cnt;
  int  Node;
end

SNode Tree [1024];
int   Root;
int   nNode;

int   Indx [256];

void Init(long @Tabl) // Постpоение деpева Хаффмана
  SInfo Info[256];
  int   nInfo;

  nInfo=0;
  while nInfo<256 do
    Info[nInfo].Code= nInfo;
    Info[nInfo].Cnt = Tabl[nInfo];
    Info[nInfo].Node=-1;
    inc  nInfo;
  end

  nNode=0;
  while nInfo>1 do
    int N0=0;
    int N1=1;

    if lCmp(Info[N0].Cnt,Info[N1].Cnt)<0 then
      N0=1;
      N1=0;
    end

    int I=2;
    while I<nInfo do
      if lCmp(Info[I].Cnt,Info[N0].Cnt)<0 then
        if lCmp(Info[I].Cnt,Info[N1].Cnt)<0 then
          N0=N1;
          N1=I;
        else
          N0=I;
        end
      end

      inc I;
    end

    int P0=Info[N0].Node;
    if P0<0 then
      Tree[nNode].Code   = Info[N0].Code;
      Tree[nNode].Cnt    = Info[N0].Cnt;
      Tree[nNode].P0     =-1;
      Tree[nNode].P1     =-1;

      Indx[Info[N0].Code]= nNode;

      P0 = nNode;
      inc  nNode;
    end

    int P1=Info[N1].Node;
    if P1<0 then
      Tree[nNode].Code   = Info[N1].Code;
      Tree[nNode].Cnt    = Info[N1].Cnt;
      Tree[nNode].P0     =-1;
      Tree[nNode].P1     =-1;

      Indx[Info[N1].Code]= nNode;

      P1 = nNode;
      inc  nNode;
    end

    dec nInfo;

    Info[N0]=Info[nInfo];

    if N1=nInfo then
      N1=N0;
    end

    dec nInfo;

    Info[N1]=Info[nInfo];

    Tree[P0]   .PP  = nNode;
    Tree[P1]   .PP  = nNode;

    Tree[nNode].Code= 0;
    Tree[nNode].Cnt = lSum(Tree[P0].Cnt,Tree[P1].Cnt);
    Tree[nNode].P0  = P0;
    Tree[nNode].P1  = P1;

    Info[nInfo].Code= 0;
    Info[nInfo].Cnt = Tree[nNode].Cnt;
    Info[nInfo].Node= nNode;

    inc  nInfo;
    inc  nNode;
  end

  Root=Info[0].Node;
  Tree[Root].PP =-1;
end

byte  Arc [ARC_LEN];
int  pArc;

byte  Tmp;
byte pBit;

void Save(word B)
  if pBit=0 then
    Arc[pArc]=Tmp;
    inc pArc;

    Tmp =  0;
    pBit=128;
  end

  Tmp =Tmp+pBit*B;
  pBit=pBit/2;
end

word Load()
  if pBit=0 then
    pBit=128;
    inc  pArc;
  end

  word C=Arc[pArc]&pBit;
  pBit=pBit/2;

  return C;
end

void Code(int P)
  if Tree[P].PP>=0 then
    Code(Tree[P].PP);

    if Tree[Tree[P].PP].P0=P then
      Save(0);
    else
      Save(1);
    end
  end
end

void Encode(word hArc, hTxt)
  byte Txt  [TXT_LEN]; // Текст
  long Tabl [256];     // Таблица частот

  int I=0;
  while I<256 do
    Tabl[I].lo=0;
    Tabl[I].hi=0;
    inc  I;
  end

  int nTxt=0;
  int pTxt=0;
  while TRUE do
    if pTxt>=nTxt then
      nTxt=read(hTxt,@Txt,BLK_LEN);
      if nTxt=0 then
        exit
      end

      pTxt=0;
    end

    lInc(@Tabl[Txt[pTxt]]);

    inc pTxt;
  end

  write(hArc,@Tabl,1024);

  Init(@Tabl);

  reset(hTxt);

  nTxt=0;
  pTxt=0;
  pArc=0;
  while TRUE do
    if pTxt>=nTxt then
      nTxt=read(hTxt,@Txt,BLK_LEN);
      if nTxt=0 then
        exit
      end

      pTxt=0;
    end

    if pArc>=BLK_LEN then
      write(hArc,@Arc,pArc);
      pArc=0;
    end

    Code(Indx[Txt[pTxt]]);

    inc pTxt;
  end

  while pBit>0 do
    Save(0);
  end

  Save(0);

  if pArc>0 then
    write(hArc,@Arc,pArc);
  end
end

void Decode(word hArc, hTxt)
  long Tabl [256];

  read(hArc,@Tabl,1024);

  Init(@Tabl);

  byte Txt[TXT_LEN];

  int nArc=0;
  pArc=0;

  long nTxt=Tree[Root].Cnt;
  int  pTxt=0;
  while nTxt.hi>0 | nTxt.lo>0 do
    if pTxt>=BLK_LEN then
      write(hTxt,@Txt,pTxt);
      pTxt=0;
    end

    if pArc+ARC_FWD>=nArc then
      word I=pArc;
      word J=0;
      while I<nArc do
        Arc[J]=Arc[I];
        inc J;
        inc I;
      end

      nArc=J+read(hArc,@Arc[J],BLK_LEN);
      pArc=0;
    end

    int P=Root;
    while Tree[P].P0>=0 do
      if Load()=0 then
        P=Tree[P].P0;
      else
        P=Tree[P].P1;
      end
    end

    Txt[pTxt]=Tree[P].Code;
    inc pTxt;

    lDec(@nTxt);
  end

  if pTxt>0 then
    write(hTxt,@Txt,pTxt);
  end
end

begin
  char @Line=@Ptr(GetPSP(),129); // Командная стpока
  byte @Size=@Ptr(GetPSP(),128); // Длина стpоки

  char  Parm  [3][128];          // Паpаметpы

  int   F=1;
  int   I=0;
  int   J=0;
  while I<3 do
    while J<Size & Line[J] =' ' do
      inc J;
    end

    int K=0;
    while J<Size & Line[J]!=' ' do
      Parm[I][K]=Line[J];
      inc  K;
      inc  J;
    end

    Parm[I][K]=#0;

    if K<1 then
      F=0;
    end

    inc  I;
  end

  word hArc;
  word hTxt;

  int C=0;
  if F!=0 then
    select
      case strcmp(@Parm[0][0],"e")=0 | strcmp(@Parm[0][0],"E")=0:
        hArc=create(@Parm[1][0]);
        hTxt=open  (@Parm[2][0]);
        C=1;
      case strcmp(@Parm[0][0],"d")=0 | strcmp(@Parm[0][0],"D")=0:
        hArc=open  (@Parm[1][0]);
        hTxt=create(@Parm[2][0]);
        C=2;
    end
  end

  if C=0 then
    puts("huffman.com e(ncode)|d(ecode) arc-file txt-file");
    return
  end

  Tmp =  0;
  pBit=128;

  select
    case C=1:
      Encode(hArc,hTxt);
    case C=2:
      Decode(hArc,hTxt);
  end

  close(hTxt);
  close(hArc);
end
