/*
 *  control.c:          Control unit of WFA structure
 *
 *  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:51 $
 *  $Author: hafner $
 *  $Revision: 5.1 $
 *  $State: Exp $
 */

#include "config.h"

#include <string.h>

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

#include "cwfa.h"
#include "ip.h"
#include "misc.h"
#include "wfalib.h"
#include "control.h"

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

                                prototypes

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

static void
clear_or_alloc (real_t **ptr, size_t size);
static void
compute_images (unsigned from, unsigned to, const wfa_t *wfa, coding_t *c);

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

                                public code

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

void
append_state (bool_t auxiliary_state, real_t final, unsigned level_of_state,
              wfa_t *wfa, coding_t *c)
/*
 *  Append a 'wfa' state. If 'auxiliary_state' == YES then
 *  allocate memory for inner products and state images.  'final' is
 *  the final distribution of the new state. 'level_of_state' is the
 *  level of the subimage which is represented by this state
 *
 *  No return value.
 *
 *  Side effects:
 *      The WFA information are updated in structure 'wfa'
 *      State images are computed and inner products are cleared (in 'c')
 */
{
   wfa->final_distribution [wfa->states] = final;
   wfa->level_of_state [wfa->states]     = level_of_state;

   if (!auxiliary_state)
   {
      unsigned level;

      wfa->domain_type [wfa->states] = USE_DOMAIN_MASK;

      /*
       *  Allocate memory for inner products and for state images
       */
      clear_or_alloc (&c->images_of_state [wfa->states],
                      size_of_tree (c->options.images_level));

      for (level = c->options.images_level + 1;
           level <= c->options.lc_max_level; level++)
         clear_or_alloc (&c->ip_states_state [wfa->states][level],
                         wfa->states + 1);

      clear_or_alloc (&c->ip_images_state [wfa->states],
                      size_of_tree (c->products_level));

      /*
       *  Compute the images of the current state at level 0,..,'imageslevel'
       */

      c->images_of_state [wfa->states][0] = final;
      compute_images (wfa->states, wfa->states, wfa, c);

      /*
       *  Compute the inner products between the current state and the
       *  old states 0,...,'states'-1
       */

      compute_ip_states_state (wfa->states, wfa->states, wfa, c);
   }
   else
   {
      unsigned level;

      wfa->domain_type [wfa->states] = 0;

      /*
       *  Free the allocated memory
       */
      if (c->images_of_state [wfa->states] != NULL)
      {
         Free (c->images_of_state [wfa->states]);
         c->images_of_state [wfa->states] = NULL;
      }
      for (level = 0; level <= c->options.lc_max_level; level++)
         if (c->ip_states_state [wfa->states][level])
         {
            Free (c->ip_states_state [wfa->states][level]);
            c->ip_states_state [wfa->states][level] = NULL;
         }
      if (c->ip_images_state [wfa->states])
      {
         Free (c->ip_images_state [wfa->states]);
         c->ip_images_state [wfa->states] = NULL;
      }
   }

   wfa->states++;
   if (wfa->states >= MAXSTATES)
      error ("Maximum number of states reached!");
}

void
append_basis_states (unsigned basis_states, wfa_t *wfa, coding_t *c)
/*
 *  Append the WFA basis states 0, ... , ('basis_states' - 1).
 *
 *  No return value.
 *
 *  Side effects:
 *      The WFA information are updated in structure 'wfa'
 *      State images and inner products are computed (in 'c')
 */
{
   unsigned level, state;

   /*
    *  Allocate memory
    */

   for (state = 0; state < basis_states; state++)
   {
      clear_or_alloc (&c->images_of_state [state],
                      size_of_tree (c->options.images_level));

      for (level = c->options.images_level + 1;
           level <= c->options.lc_max_level; level++)
         clear_or_alloc (&c->ip_states_state [state][level], state + 1);

      clear_or_alloc (&c->ip_images_state [state],
                      size_of_tree (c->products_level));

      c->images_of_state [state][0] = wfa->final_distribution [state];
      wfa->level_of_state [state]   = -1;
   }

   compute_images (0, basis_states - 1, wfa, c);
   compute_ip_states_state (0, basis_states - 1, wfa, c);
   wfa->states = basis_states;

   if (wfa->states >= MAXSTATES)
      error ("Maximum number of states reached!");
}

void
append_transitions (unsigned state, unsigned label, const real_t *weight,
                    const word_t *into, wfa_t *wfa)
/*
 *  Append the 'wfa' transitions (given by the arrays 'weight' and 'into')
 *  of the range ('state','label').
 *
 *  No return value.
 *
 *  Side effects:
 *      new 'wfa' edges are appended
 */
{
   unsigned edge;

   wfa->y_column [state][label] = 0;
   for (edge = 0; isedge (into [edge]); edge++)
   {
      append_edge (state, into [edge], weight [edge], label, wfa);
      if (into [edge] == wfa->y_state [state][label])
         wfa->y_column [state][label] = 1;
   }
}

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

                                private code

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

static void
compute_images (unsigned from, unsigned to, const wfa_t *wfa, coding_t *c)
/*
 *  Computes the images of the given states 'from', ... , 'to'
 *  at level 0,...,'c->imagelevel'.
 *  Uses the fact that each state image is a linear combination of state
 *  images, i.e. s_i := c_0 s_0 + ... + c_i s_i.
 */
{
   unsigned label, level, state;

   /*
    *  Compute the images Phi(state)
    *  #                level = 0
    *  ##               level = 1
    *  ####             level = 2
    *  ########         level = 3
    *  ...
    *  ########...##    level = imageslevel
    */

   for (level = 1; level <= c->options.images_level; level++)
      for (state = from; state <= to; state++)
         for (label = 0; label < MAXLABELS; label++)
         {
            real_t   *dst, *src;
            unsigned  edge;
            int       domain;           /* current domain */

            if (ischild (domain = wfa->tree[state][label]))
            {
               dst = c->images_of_state [state] + address_of_level (level) +
                     label * size_of_level (level - 1);
               src = c->images_of_state [domain]
                     + address_of_level (level - 1);
               memcpy (dst, src, size_of_level (level - 1) * sizeof (real_t));
            }
            for (edge = 0; isedge (domain = wfa->into[state][label][edge]);
                 edge++)
            {
               unsigned n;
               real_t   weight = wfa->weight [state][label][edge];

               dst = c->images_of_state [state] + address_of_level (level) +
                     label * size_of_level (level - 1);
               src = c->images_of_state [domain]
                     + address_of_level (level - 1);

               for (n = size_of_level (level - 1); n; n--)
                  *dst++ += *src++ * weight;
            }
         }

}

static void
clear_or_alloc (real_t **ptr, size_t size)
/*
 *  if *ptr == NULL     allocate memory with Calloc
 *  otherwise           fill the real_t-array ptr[] with 0
 */
{
   if (*ptr == NULL)
      *ptr = Calloc (size, sizeof (real_t));
   else
      memset (*ptr, 0, size * sizeof (real_t));

}
