/**************************************************************************
 *
 * Weights.cpp -- Dealing with document weights
 * Copyright (C) 1999  Rodger McNab
 *
 * 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 "UCArray.h"
#include "Weights.h"
#include "sysfuncs.h"

#define MAXBITS (sizeof(mg_u_long) * 8)


ApproxWeightsData::ApproxWeightsData () {
  weightBuf = NULL;
  table = NULL;
  Free();
}

ApproxWeightsData::~ApproxWeightsData () {
  Free ();
}

void ApproxWeightsData::Free () {
  bits = 0;
  mask = 0;
  L = 0.0;
  B = 0.0;
  numLevelDocs = 0;
  if (weightBuf != NULL) {
    delete [] weightBuf;
    weightBuf = NULL;
  }
  if (table != NULL) {
    delete [] table;
    table = NULL;
  }
}

bool ApproxWeightsData::Read (FILE *approxWeightsFile, mg_u_long diskPtr,
			      mg_u_long _numLevelDocs) {
  Free ();
  
  // go to the appropriate place in the weights file
  fseek (approxWeightsFile, diskPtr, SEEK_SET);

  // read in approx weights parameters
  ReadUC (approxWeightsFile, bits);
  ReadD (approxWeightsFile, L);
  ReadD (approxWeightsFile, B);

  mask = (bits == 32) ? 0xffffffff : (1 << bits) - 1;
  numLevelDocs = _numLevelDocs;

  // allocate buffer for the weights
  mg_u_long arrSize = (bits*numLevelDocs+sizeof(mg_u_long)-1) / 32;
  weightBuf = new mg_u_long[arrSize];

  // read in the weights
  mg_u_long i;
  for (i=0; i<arrSize; ++i) {
    ReadUL (approxWeightsFile, weightBuf[i]);
  }

  // create precomputed table of values if it will be small enough
  // (if not small enough table == NULL
  if (bits <= 12) {
    mg_u_long i, tableSize = (1 << bits);
    table = new float[tableSize];
    table[0] = L;
    for (i = 1; i < tableSize; ++i)
      table[i] = table[i - 1] * B;
  }

  return true;
}

float ApproxWeightsData::GetLowerApproxDocWeight (mg_u_long levelDocNum) {
  // sanity check
  if (bits == 0 || weightBuf == NULL) return 1.0;

  register mg_u_long c, pos;
  register mg_u_long *dw;

  // get the compressed version of the weight
  pos = levelDocNum * bits;
  dw = &weightBuf[pos / MAXBITS];
  pos &= (MAXBITS - 1);
  c = *dw >> pos;
  if (pos + bits > MAXBITS)
    c |= *(dw + 1) << (MAXBITS - pos);
  c &= mask;

  // decompress the weight
  if (table != NULL) return table[c];
  return (L * pow (B, (double) c));
}



float GetExactDocWeight (FILE *exactWeightsFile, mg_u_long diskPtr,
			 mg_u_long levelDocNum) {
  // go to the appropriate place in the weights file
  fseek (exactWeightsFile, diskPtr+sizeof(float)*levelDocNum, SEEK_SET);

  // read in the exact weight
  float weight;
  ReadF (exactWeightsFile, weight);
  
  return weight;
}
