/*
 *  nd.c:               Output of prediction tree
 *
 *  Written by:         Ullrich Hafner
 *
 *  This file is part of FIASCO (Fractal Image And Sequence COdec)
 *  Copyright (C) 1994-2000 Ullrich Hafner
 */

/*
 *  $Date: 2000/06/14 20:50:31 $
 *  $Author: hafner $
 *  $Revision: 5.1 $
 *  $State: Exp $
 */

#include "config.h"

#include "types.h"
#include "macros.h"
#include "error.h"

#include "wfa.h"
#include "arith.h"
#include "misc.h"
#include "bit-io.h"
#include "rpf.h"
#include "list.h"

#include "nd.h"

/*****************************************************************************

                                prototypes

*****************************************************************************/

static unsigned
encode_nd_tree (const wfa_t *wfa, bitfile_t *output);
static void
encode_nd_coefficients (unsigned total, const wfa_t *wfa, bitfile_t *output);

/*****************************************************************************

                                public code

*****************************************************************************/

void
write_nd (const wfa_t *wfa, bitfile_t *output)
/*
 *  Write prediction information of 'wfa' to given stream 'output'.
 *  Coefficients are quantized with model 'p_rpf'.
 *
 *  No return value.
 */
{
   unsigned total = encode_nd_tree (wfa, output);

   if (total > 0)
      encode_nd_coefficients (total, wfa, output);
}

/*****************************************************************************

                                private code

*****************************************************************************/

static unsigned
encode_nd_tree (const wfa_t *wfa, bitfile_t *output)
/*
 *  Write prediction tree of 'wfa' to given stream 'output'.
 *
 *  No return value.
 */
{
   lqueue_t *queue;                     /* queue of states */
   int       state, next;               /* state and its current child */
   unsigned  used, not_used;            /* counter ND used/not used */
   u_word_t  low;                       /* Start of the current code range */
   u_word_t  high;                      /* End of the current code range */
   u_word_t  underflow;                 /* Number of underflow bits pending */
   u_word_t  sum0, sum1;                /* Probability model */
   unsigned  bits = bits_processed (output);

   used = not_used = 0;

   /*
    *  Initialize arithmetic coder
    */
   low       = 0;
   high      = 0xffff;
   underflow = 0;
   sum0      = 1;
   sum1      = 11;

   queue = alloc_queue (sizeof (int));
   state = wfa->root_state;
   queue_append (queue, &state);

   /*
    *  Traverse the WFA tree in breadth first order (using a queue).
    */
   while (queue_remove (queue, &next))
   {
      unsigned label;

      if (wfa->level_of_state [next] > wfa->wfainfo->p_max_level + 1)
      {
         /*
          *  Nondetermismn is not allowed at levels larger than
          *  'wfa->wfainfo->p_max_level'.
          */
         for (label = 0; label < MAXLABELS; label++)
            if (ischild (state = wfa->tree [next][label]))
               queue_append (queue, &state); /* continue with children */
      }
      else if (wfa->level_of_state [next] > wfa->wfainfo->p_min_level)
      {
         for (label = 0; label < MAXLABELS; label++)
            if (ischild (state = wfa->tree [next][label]))
            {
               unsigned range;          /* Current interval range */

               if (isedge (wfa->into [next][label][0])) /* prediction used */
               {
                  used++;

                  /*
                   *  Encode a '1' symbol
                   */
                  range =  (high - low) + 1;
                  low   = low + (u_word_t) ((range * sum0) / sum1);
                  RESCALE_OUTPUT_INTERVAL;
               }
               else                     /* no predict., continue with children */
               {
                  not_used++;
                  if (wfa->level_of_state [state] > wfa->wfainfo->p_min_level)
                     queue_append (queue, &state);

                  /*
                   *  Encode a '0' symbol
                   */
                  range =  (high - low) + 1;
                  high  = low + (u_word_t) ((range * sum0) / sum1 - 1);
                  RESCALE_OUTPUT_INTERVAL;
                  sum0++;
               }
               /*
                *  Update the frequency counts
                */
               sum1++;
               if (sum1 > 50)           /* Scale the symbol frequencies */
               {
                  sum0 >>= 1;
                  sum1 >>= 1;
                  if (!sum0)
                     sum0 = 1;
                  if (sum0 >= sum1)
                     sum1 = sum0 + 1;
               }
            }

      }
   }
   free_queue (queue);

   /*
    *  Flush the quasi-arithmetic encoder
    */
   low = high;
   RESCALE_OUTPUT_INTERVAL;
   OUTPUT_BYTE_ALIGN (output);

   debug_message ("%d nd fields: %d used nd, %d used not nd", used + not_used,
                  used, not_used);
   {
      unsigned total = used + not_used;

      debug_message ("nd-tree:      %5d bits. (%5d symbols => %5.2f bps)",
                     bits_processed (output) - bits, total,
                     total > 0 ? ((bits_processed (output) - bits) /
                                  (double) total) : 0);
   }

   return used;
}

static void
encode_nd_coefficients (unsigned total, const wfa_t *wfa, bitfile_t *output)
/*
 *  Write #'total' weights of nondeterministic part of 'wfa' to given 'output'
 *  stream. Coefficients are stored with arithmetic coding (the model is
 *  given by 'p_rpf').
 *
 *  No return value.
 */
{
   unsigned bits = bits_processed (output);

   {
      unsigned *coefficients;           /* array of factors to encode */
      unsigned *ptr;                    /* pointer to current factor */
      unsigned  state, label, edge;
      word_t    domain;

      ptr = coefficients  = Calloc (total, sizeof (unsigned));

      for (state = wfa->basis_states; state < wfa->states; state++)
         for (label = 0; label < MAXLABELS; label++)
            if (ischild (wfa->tree [state][label])
                && isedge (wfa->into [state][label][0]))
               for (edge = 0; isedge (domain = wfa->into [state][label][edge]);
                    edge++)
               {
                  if (ptr - coefficients >= (int) total)
                     error ("Can't write more than %d coefficients.", total);

                  *ptr++ = rtob (wfa->weight [state][label][edge],
                                 wfa->wfainfo->dc_rpf);
               }

      /*
       *  Encode array of coefficients with arithmetic coding
       */
      {
         const int scaling = 50;        /* scaling factor of prob. model */
         unsigned  c_symbols = 1 << (wfa->wfainfo->dc_rpf->mantissa_bits + 1);

         encode_array (output, coefficients, NULL, &c_symbols, 1,
                       total, scaling);
      }

      debug_message ("nd-factors:   %5d bits. (%5d symbols => %5.2f bps)",
                     bits_processed (output) - bits, total,
                     total ? ((bits_processed (output) - bits)
                              / (double) total) : 0);
      Free (coefficients);
   }
}


