/**************************************************************************
 *
 * locallib.cpp -- Misc functions
 * Copyright (C) 1994  Gary Eddy, Alistair Moffat and Neil Sharman
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 **************************************************************************/

#include "sysfuncs.h"

#include "memlib.h"
#include "bitio_gen.h"
#include "huffman.h"
#include "netorder.h"  /* [RPAP - Jan 97: Endian Ordering] */

#include "locallib.h"
#include "text.h"

int 
vecentropy (int *A, int n)
{
  int total = 0, i;
  double bits = 0.0;

  for (i = 0; i < n; ++i)
    total += A[i];
  for (i = 0; i < n; ++i)
    if (A[i])
      bits += A[i] * log2 (1.0 * total / A[i]);
  return ((int) (bits + 0.9999));

}

static void 
siftdown (int *A, int i, int n)
{
  int ms, t;

  while (2 * i + 1 <= n - 1)
    {
      if (2 * i + 1 == n - 1 || A[2 * i + 1] < A[2 * i + 2])
	ms = 2 * i + 1;
      else
	ms = 2 * i + 2;
      if (A[i] > A[ms])
	{
	  t = A[i];
	  A[i] = A[ms];
	  A[ms] = t;
	}
      i = ms;
    }
}

static void 
buildheap (int *A, int n)
{
  int i;
  for (i = n / 2 - 1; i >= 0; --i)
    siftdown (A, i, n);
}

int 
huffcodebits (mg_u_long *A, int n)
{
  int i, j, tot = 0, bits = 0, v1, v2, *B;

  B = (int *) Xmalloc (n * sizeof (int));
  j = 0;
  for (i = 0; i < n; ++i)
    {
      if (A[i])
	{
	  tot += (B[j++] = A[i]);
	}
    }
  n = j;

  if (n == 0 || tot == 0)
    return (0);

  buildheap (B, n);
  while (n > 1)
    {
      v1 = B[0];
      B[0] = B[n - 1];
      --n;
      siftdown (B, 0, n);
      v2 = B[0];
      B[0] = v1 + v2;
      siftdown (B, 0, n);
      bits += v1 + v2;
    }
  Xfree (B);
  return (bits);
}


int 
modelbits (mg_u_long *A, int n)
{
  int i, bits = 0, last, N = 0;

  last = -1;
  for (i = 0; i < n; ++i)
    {
      if (A[i])
	{
	  bits += BIO_Gamma_Length (i - last) + BIO_Gamma_Length (A[i]);
	  last = i;
	  ++N;
	}
    }
  if (N == 0)
    return (0);
  bits += BIO_Gamma_Length (N);
  return (bits);
}





/* Find the next prime number larger than p */
int 
prime (int p)
{
  int limit;
  p += (p & 1) + 1;
  limit = (int) sqrt ((double) p) + 1;
  do
    {
      int j;
      while (limit * limit < p)
	++limit;
      for (j = 3; j <= limit && p % j; j += 2)
	;			/* NULLBODY */
      if (j > limit)
	break;
      p += 2;
    }
  while (1);
  return (p);
}



int Read_cdh (FILE * f, compression_dict_header * cdh, mg_u_long * /*mem*/,
	      mg_u_long * disk) {
  if (disk)
    (*disk) += sizeof (*cdh);

  /* [RPAP - Jan 97: Endian Ordering] */
  if (fread (cdh, sizeof (*cdh), 1, f) == 1)
    {
      int i;
      NTOHUL(cdh->dict_type);
      NTOHUL(cdh->novel_method);
      for (i = 0; i < TEXT_PARAMS; ++i)
	NTOHUL(cdh->params[i]);
      NTOHUL(cdh->num_words[0]);
      NTOHUL(cdh->num_words[1]);
      NTOHUL(cdh->num_word_chars[0]);
      NTOHUL(cdh->num_word_chars[1]);
      NTOHUL(cdh->lookback);
      return 0;
    }
  else
    return -1;
}


int 
Read_cfh (FILE * f, comp_frags_header * cfh, mg_u_long * mem, mg_u_long * disk)
{
  int i;  /* [RPAP - Jan 97: Endian Ordering] */

  if (Read_Huffman_Data (f, &cfh->hd, mem, disk) == -1)
    return -1;
  if (fread (&cfh->uncompressed_size,
	     sizeof (cfh->uncompressed_size), 1, f) != 1)
    return -1;
  NTOHUL(cfh->uncompressed_size);  /* [RPAP - Jan 97: Endian Ordering] */

  if (disk)
    (*disk) += sizeof (cfh->uncompressed_size);

  memset (cfh->huff_words_size, '\0', sizeof (cfh->huff_words_size));

  if ((int)fread (cfh->huff_words_size + cfh->hd.mincodelen,
	     sizeof (*cfh->huff_words_size),
	     cfh->hd.maxcodelen - cfh->hd.mincodelen + 1, f) !=
      cfh->hd.maxcodelen - cfh->hd.mincodelen + 1)
    return -1;

  /* [RPAP - Jan 97: Endian Ordering] */
  for (i = cfh->hd.mincodelen; i < cfh->hd.maxcodelen + 1; ++i)
    NTOHUL(cfh->huff_words_size[i]);

  if (disk)
    (*disk) += sizeof (*cfh->huff_words_size) *
      (cfh->hd.maxcodelen - cfh->hd.mincodelen + 1);
  return 0;
}

