PROGRAM Huffman_Decoding
!-----------------------------------------------------------------------------
! Program to decode compressed text using a Huffman decoding tree.  Allowed
! characters and their codes (assumed to have been assigned using Huffman's
! algorithm) are read from a file.  Variable used:
!   Root : pointer to root of decoding tree
!
! Input(keyboard):  Name of file containing Huffman codes and  the name
!                   of a compressed file
! Input(file):      Characters and their Huffman codes from CodeFile
!                   Compressed text from CompressedFile
! Output:           A graphical representation of the Huffman tree
!                   and the decoded text
!-----------------------------------------------------------------------------

  IMPLICIT NONE
  TYPE BinTreeNode
    CHARACTER*1 :: Data
    TYPE(BinTreeNode), POINTER :: LChild, RChild
  END TYPE BinTreeNode

  TYPE(BinTreeNode), POINTER :: Root

  CALL BuildDecodingTree(Root)
  CALL PrintTree(Root, 8)
  PRINT *
  PRINT *
  CALL Decode(Root)

CONTAINS

  !-BuildDecodingTree---------------------------------------------------------
  ! Subroutine that reads characters and their codes (assumed to be assigned
  ! using Huffman's algorithm) from CodeFile and constructs the Huffman
  ! decoding tree.  Local variables used are:
  !   CodeFile       : name of file containing characters and their codes
  !   OpenStatus     : status variable for OPEN
  !   InputStatus    : status variable for READ
  !   AllocateStatus : status variable for ALLOCATE
  !   Ch             : character from code file
  !   Code           : Huffman code for Ch
  !
  ! Input (file): Characters and their Huffman codes
  ! Return:       Root
  !---------------------------------------------------------------------------

  SUBROUTINE BuildDecodingTree(Root)

    TYPE(BinTreeNode), POINTER :: Root
    CHARACTER(20) :: CodeFile, Ch*1, Code*10
    INTEGER :: OpenStatus, InputStatus, AllocateStatus

    ! Create an empty binary tree
    ALLOCATE(Root, STAT = AllocateStatus)
    IF (AllocateStatus /= 0) STOP "*** Not enough memory ***"

    Root%Data = "*"
    NULLIFY(Root%LChild)
    NULLIFY(Root%RChild)

    ! Get name of file containing Huffman codes and open it for input
    WRITE (*, '(1X, A)', ADVANCE = "NO") &
          "Enter name of file containing Huffman codes: "
    READ *, CodeFile
    OPEN (UNIT = 10, FILE = CodeFile, STATUS = "OLD", ACTION = "READ", &
          POSITION = "REWIND", IOSTAT = OpenStatus)
    IF (OpenStatus > 0) STOP "*** Cannot open code file ***"

    PRINT *
    ! Read characters and their codes from CodeFile
    DO
       READ(10, '(A, 1X, A)', IOSTAT = InputStatus) Ch, Code
       IF (InputStatus > 0) STOP "*** File input error ***"
       IF (InputStatus < 0) EXIT  ! End of file, terminate repetition

       ! Otherwise proceed
       CALL AddToTree (Ch, Code, Root)
    END DO

  END SUBROUTINE BuildDecodingTree


 !-AddToTree-----------------------------------------------------------------
  ! Subroutine that creates a node for character Ch and adds it to the
  ! Huffman decoding tree with root node pointed to by Root. Local variables
  ! used are:
  !   AllocateStatus : status variable for ALLOCATE
  !   I       : subscript
  !   TempPtr : pointer to new node(s)
  !   P       : pointer to nodes in path labeled by Code
  !
  ! Accepts:  Ch, Code, Root
  ! Returns:  Modified Huffman tree with specified Root
  !---------------------------------------------------------------------------

  SUBROUTINE AddToTree(Ch, Code, Root)

    TYPE(BinTreeNode), POINTER :: Root, TempPtr, P
    CHARACTER(1) :: Ch, Code*10
    INTEGER :: AllocateStatus, I

    ! Descend the Huffman tree using characters in Code
    I = 1
    P => Root

    DO
       IF (I > LEN_TRIM(Code)) EXIT
       ! If end of Code reached, terminate descent in tree

       ! Otherwise continue descent
       IF (Code(I:I) == "0") THEN                ! Descend left
          IF (.NOT. ASSOCIATED(p%LChild)) THEN   ! Create node along path
             ALLOCATE(TempPtr, STAT = AllocateStatus)
             IF (AllocateStatus /= 0) STOP "*** Not enough memory ***"

             TempPtr%Data = "*"
             NULLIFY(TempPtr%LChild)
             NULLIFY(TempPtr%RChild)
             P%LChild => TempPtr
          END IF
          I = I + 1
          P => P%LChild
       ELSE IF (Code(I:I) == "1") THEN           ! Descend right
          IF (.NOT. ASSOCIATED(p%RChild)) THEN   ! Create node along path
             ALLOCATE(TempPtr, STAT = AllocateStatus)
             IF (AllocateStatus /= 0) STOP "*** Not enough memory ***"

             TempPtr%Data = "*"
             NULLIFY(TempPtr%LChild)
             NULLIFY(TempPtr%RChild)
             P%RChild => TempPtr
          END IF
          I = I + 1
          P => P%RChild
       END IF
       P%Data = Ch
    END DO

  END SUBROUTINE AddToTree


  !-PrintTree-----------------------------------------------------------------
  ! Subroutine that uses recursion to display a binary tree.   The tree is
  ! displayed "on its side" with each level indented by a specified value
  ! Indent, but with no arcs sketched in.  Local variable used is:
  !   I : DO-loop counter
  !
  ! Accepts:  Root and Indent
  ! Output :  Graphical representation of the binary tree
  !---------------------------------------------------------------------------

  RECURSIVE SUBROUTINE PrintTree(Root, Indent)

    TYPE(BinTreeNode), POINTER :: Root
    INTEGER :: I, Indent

    IF (ASSOCIATED(Root)) THEN
       CALL PrintTree(Root%RChild, Indent + 7)
       DO I = 1, Indent
          WRITE (*, '(A)', ADVANCE = "NO") " "
       END DO
       PRINT *, Root%Data
       CALL PrintTree(Root%LChild, Indent + 7)
    END IF

   END SUBROUTINE PrintTree


  !-Decode--------------------------------------------------------------------
  ! Subroutine that reads compressed text (a string of bits) from a file
  ! and decodes it using the Huffman decoding tree with specified Root.
  ! Local variables used are:
  !   CompressedFile : name of  file containing text to be decoded
  !   Bit            : next bit
  !   P              : pointer to trace path in decoding tree
  !   EndOfFile      : indicates end of compressed file
  !
  ! Accepts:      Root
  ! Input (file): Individual bits from CompressedFile
  ! Output:       Decoded text
  !---------------------------------------------------------------------------

  SUBROUTINE Decode (Root)

    TYPE(BinTreeNode), POINTER :: Root, P
    CHARACTER(1) :: Bit, CompressedFile*20
    INTEGER :: EndOfFile

    ! Get name of compressed file and open it for input
    WRITE (*, '(1X, A)', ADVANCE = "NO") &
          "Enter name of file containing compressed text: "
    READ *, CompressedFile
    OPEN (UNIT = 20, FILE = CompressedFile, STATUS = "OLD", &
          ACTION = "READ", POSITION = "REWIND")

    READ(20, '(A)', IOSTAT = EndOfFile, ADVANCE = "NO") Bit

    PRINT *
    DO
       IF (EndOfFile < 0) EXIT
       ! If end of file, terminate decoding

       ! Otherwise continue descent in Huffman tree to find character
       P => Root
       DO
          IF (.NOT. ASSOCIATED(P%LChild) .AND. &
              .NOT. ASSOCIATED(P%RChild)) EXIT
          ! If leaf node reached, terminate descent

          ! Otherwise, continue descent
          WRITE(*, '(A)', ADVANCE = "NO") Bit
          IF (Bit == "0") THEN
             P => P%LChild
          ELSE IF (Bit == "1") THEN
             P => P%RChild
          END IF
          READ(20, '(A)', IOSTAT = EndOfFile, ADVANCE = "NO") Bit
       END DO

       ! Print the decoded character
       PRINT *, "-- ", P%Data
    END DO

   END SUBROUTINE Decode

END PROGRAM Huffman_Decoding
