/*********************************************************
** Copyright (c) 2005
** University of Washington
** Licensed under the terms set forth by University of
** Washington. If you did not sign such a license, you
** are using this software/code illegally and you do not
** have permission to use, modify, or redistribute
** this or any files in this software package.
**
** File: Cluster.cpp
**
**********************************************************/
#include "Cluster.h"
#include "ClusterException.h"
//#define __STDC_LIMIT_MACROS
#include <stdint.h>
#include "SafeInt.h"

/* 
 * Constructs a CCluster object.
 * Arguments:
 *   pFunc : Pointer to object that computes distance between two data points.
 *   pCombine : Pointer to object that combines two data points.
 */
CCluster::CCluster(CDistanceFunction* pFunc, CCombineDataFunction *pCombine)
  : m_pDistance(pFunc), m_pCombineData(pCombine), foo(0)
{
}

/*
 * Accessor for the distance function object
 */
CDistanceFunction* CCluster::GetDistanceFunction()
{
  return m_pDistance;
}

/*
 * Accessor for the combine-data object
 */
CCombineDataFunction* CCluster::GetCombineDataFunction()
{
  return m_pCombineData;
}

/*
 * Computes the maximum amount of memory that the algorithm will use.
 * If the number of bytes is larger than the maximum unsigned int,
 * throws an exception.
 */
void CCluster::CheckMemory(uintmax_t iDataSize, uintmax_t iDataCount) 
{
  uintmax_t memReq, sizemax, div, reps, i;
  size_t mod;

  try {
    memReq = GetMemoryRequirement(iDataSize, iDataCount);
    sizemax = uintmax_t(SIZE_MAX / 2);
    mod = CSafeUIntMax::Mod(memReq, sizemax);
    div = CSafeUIntMax::Divide(memReq, sizemax);
    reps = CSafeUIntMax::Add(div, (mod == 0 ? 0 : 1));
  }
  catch(out_of_range *pOr) {
    delete pOr;
    __throw_cluster_ex(CLUSTEX_OUTOFMEMORY, "Not enough memory available to cluster.");
  }
    
  if (div > sizemax)
    throw new CClusterException(CLUSTEX_OUTOFMEMORY, "Not enough memory available to cluster.");
  
  char** mems = new char*[reps];
  for (i = 0; i < div; i++)
    {
      mems[i] = NULL;
    }
  
  try {
    char* mem = NULL;
    for (i = 0; i < div; i++)
      {
	// try to allocate enough memory
	try {
	  mem = (char*)malloc(sizemax);
	}
	catch(bad_alloc&) {
	  __throw_cluster_ex(CLUSTEX_OUTOFMEMORY, "Not enough memory available to cluster.");
	}
	
	if (NULL == mem)
	  __throw_cluster_ex(CLUSTEX_OUTOFMEMORY, "Not enough memory available to cluster.");
	mems[i] = mem;
      }
    
    // remainder
    try {
      mem = (char*)malloc(size_t(mod));
    }
    catch(bad_alloc&) {
      __throw_cluster_ex(CLUSTEX_OUTOFMEMORY, "Not enough memory available to cluster.");
    }
    if (NULL == mem)
      __throw_cluster_ex(CLUSTEX_OUTOFMEMORY, "Not enough memory available to cluster.");
    mems[i] = mem;
  }
  catch(...) {
    // Failed... cleanup & re-throw
    if (NULL != mems) {
      for(i = 0; i < div; i++)
	{
	  if (NULL != mems[i])
	    free(mems[i]);
	}
      
      delete mems;
    }
    throw;
  }

  // Successful... cleanup  
  if (NULL != mems) {
    for(i = 0; i < div; i++)
      {
	if (NULL != mems[i])
	  free(mems[i]);
      }
    
    delete mems;
  }
}
