/** @file ikmeans_lloyd.tc
 ** @brief Integer K-Means - LLoyd Algorithm - Definition
 ** @author Brian Fulkerson
 ** @author Andrea Vedaldi
 **/

/*
Copyright (C) 2007-12 Andrea Vedaldi and Brian Fulkerson.
All rights reserved.

This file is part of the VLFeat library and is made available under
the terms of the BSD license (see the COPYING file).
*/

/** @internal
 ** @brief Helper function to initialize a filter for Lloyd algorithm
 **
 ** @param f filter.
 **/

static void
vl_ikm_init_lloyd (VlIKMFilt * f VL_UNUSED)
{ }

/** @internal
 ** @brief LLoyd algorithm
 ** @param f IKM quantizer.
 ** @param data Training data.
 ** @param N Number of traning data.
 ** @return error code.
 **/

static int
vl_ikm_train_lloyd (VlIKMFilt* f, vl_uint8 const* data, vl_size N)
{
  int err =  0 ;
  vl_uindex iter, i, j, k  ;
  vl_uint32 *asgn = vl_malloc (sizeof(vl_uint32) * N) ;
  vl_uint32 *counts = vl_malloc (sizeof(vl_uint32) * N) ;

  for (iter = 0 ; 1 ; ++ iter) {
    vl_bool done = 1 ;

    /* ---------------------------------------------------------------
     *                                               Calc. assignments
     * ------------------------------------------------------------ */

    for (j = 0 ; j < N ; ++j) {
      vl_ikmacc_t best_dist = 0 ;
      vl_index best = -1 ;

      for (k = 0; k < f->K ; ++k) {
        vl_ikmacc_t dist = 0 ;

        /* compute distance with this center */
        for (i = 0; i < f->M ; ++i) {
          vl_ikmacc_t delta =
            data [j * f->M + i] - f->centers [k * f->M + i] ;
          dist += delta * delta ;
        }

        /* compare with current best */
        if (best == -1 || dist < best_dist) {
          best = k ;
          best_dist = dist ;
        }
      }
      if (asgn [j] != best) {
        asgn [j] = (vl_uint32) best ;
        done = 0 ;
      }
    }

    /* stopping condition */
    if (done || iter == f->max_niters) break ;

    /* ---------------------------------------------------------------
     *                                                   Calc. centers
     * ------------------------------------------------------------ */

    /* re-compute centers */
    memset (f->centers, 0, sizeof(*f->centers) * f->M * f->K);
    memset (counts,  0, sizeof(*counts) * f->K);
    for (j = 0; j < N; ++j) {
      vl_uindex this_center = asgn [j] ;
      ++ counts [this_center] ;
      for (i = 0; i < f->M ; ++i) {
        f->centers [this_center * f->M + i] += data[j * f->M + i] ;
      }
    }

    for (k = 0; k < f->K; ++k) {
      vl_index n = counts [k];
      if (n > 0xffffff) {
        err = 1 ;
      }
      if (n > 0) {
        for (i = 0; i < f->M; ++i) {
          f->centers [k * f->M + i] /= n;
        }
      } else {
        /*
           If no data are assigned to the center, it is not changed
           with respect to the previous iteration, so we do not do anything.
        */
      }
    }
  }

  vl_free (counts) ;
  vl_free (asgn) ;
  return err ;
}


/** @internal
 ** @brief LLoyd algorithm
 ** @param f     IKM quantizer.
 ** @param asgn  Assignments (out).
 ** @param data  Data to quantize.
 ** @param N     Number of data.
 **/

static void
vl_ikm_push_lloyd (VlIKMFilt *f, vl_uint32 *asgn, vl_uint8 const *data, vl_size N)
{
  vl_uindex j ;
  for(j = 0 ; j < N ; ++j) {
    asgn[j] = vl_ikm_push_one (f->centers, data + j * f->M, f->M, f->K);
  }
}

/*
 * Local Variables: *
 * mode: C *
 * End: *
 */
