/*
    DFT++ is a density functional package developed by the research group
    of Professor Tomas Arias

    Copyright 1996-2003 Sohrab Ismail-Beigi

    This file is part of DFT++.

    DFT++ 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.

    DFT++ 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 DFT++; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

    Please see the file CREDITS for a list of authors.

    For academic users, we request that publications using results obtained with
    this software reference

    "New algebraic formulation of density functional calculation," by Sohrab Ismail-Beigi
    and T.A. Arias, Computer Physics Communications 128:1-2, 1-45 (June 2000).

    and, if using the wavelet basis, further reference

    "Multiresolution analysis of electronic structure: semicardinal and wavelet bases,"
    T.A. Arias, Reviews of Modern Physics 71:1, 267-311 (January 1999).

    and 

    "Robust ab initio calculation of condensed matter: transparent convergence through
    semicardinal multiresolution analysis,'' I.P. Daykov, T.A. Arias, and
    Torkel D. Engeness, Physical Review Letters, 90:21, 216402 (May 2003).

    For your convenience, preprints of the above articles may be obtained from
    http://arXiv.org/abs/cond-mat/9909130, 9805262, and 0204411, respectively.
*/

/*
 *           Sohrab Ismail-Beigi               Jan. 6, 1997
 *
 *  mem.c:   Routines that dynamically allocate/free memory.
 *
 *  void *mymalloc() is just a fancy calloc() that exits with
 *           provided error messages if it can't allocate memory.
 *
 */

/* $Id: mem.cpp,v 1.12.2.3 2003/05/29 18:54:29 ivan Exp $ */

#include "header.h"

#ifndef DFT_TRACE_MEM

/* Allocate size bytes of memory; if it fails, die with some
 * information.  If it suceeds, it zeroes out the memory before
 * returning the pointer. */
void *
mymalloc(int size,const char *what,const char *where)
{

#ifdef DFT_PROFILING
  timerOn(8);  // Turn on memory allocation timer.
#endif // DFT_PROFILING

  void *p;

  if (size <= 0) 
    {
#ifdef DFT_PROFILING
  timerOff(8);  // Turn off memory allocation timer.
#endif // DFT_PROFILING
      return ((void *) 0);
    }

#ifdef DFT_MEMALIGN    // not all platforms have memalign in standard lib
  p = (void *)memalign(32,size);
#else
  p = (void *)malloc(size);
#endif

  if (p == (void *)0)
    die("Can't malloc %d for %s in %s.\n", size, what, where);
  else
    {
      int i;
      char *c = (char *)p;
      for (i=0; i < size; i++)
	c[i] = 0;

#ifdef DFT_PROFILING
  timerOff(8);  // Turn off memory allocation timer.
#endif // DFT_PROFILING

    }
  
  return p;

}


/* Reallocate size bytes of memory; if it fails, die with some
 * information.  If it suceeds, returns the new block pointer */
void *
myrealloc(void *pold,int size,const char *what,const char *where)
{

#ifdef DFT_PROFILING
  timerOn(8);  // Turn on memory allocation timer.
#endif // DFT_PROFILING

  void *p;

  if (size <= 0) 
    {
#ifdef DFT_PROFILING
  timerOff(8);  // Turn off memory allocation timer.
#endif // DFT_PROFILING
      return ((void *) 0);
    }

  p = (void *)realloc(pold,size);

  if (p == 0)
    die("Can't realloc %d for %s in %s.\n", size, what, where);

#ifdef DFT_PROFILING
  timerOff(8);  // Turn off memory allocation timer.
#endif // DFT_PROFILING

  return p;
}

void
myfree(void* ptr)
{
  if (ptr != NULL)
    free(ptr);
}


#else // DFT_TRACE_MEM : trace memory usage


/*************************************************
 *                                               *            
 *  For debugging purpose, use the following     *
 *  memory access routines to trace usage.       *
 *                                               *
 *************************************************/

int num_blocks = 0;
int max_block  = 0;
long int accum_alloc = 0L;
long int current_alloc = 0L;
long int max_alloc = 0L;

#define PREFIX     16
#define POSTFIX    16
#define MAGIC1   1234
#define MAGIC2  -9876
#define MAGIC3  "little-dogs"
#define POSTCHECK  10

void
mem_trace_report()
{
  int i;
  dft_log("\n>Memory Trace Report:\n");
  dft_log(">Memory\ttotal allocations\t%d\n",num_blocks);
  dft_log(">Memory\tmaximum block allocated\t%d\n",max_block);
  dft_log(">Memory\taccumulative memory\t%ld\n", accum_alloc);
  dft_log(">Memory\tcurrent memory\t%ld\n", current_alloc);
  dft_log(">Memory\tmaximum memory\t%ld\n", max_alloc);
  dft_log("\n\n");
  dft_log_flush();
}

void *
mymalloc(int size,const char *what,const char *where)
{

#ifdef DFT_PROFILING
  timerOn(8);  // Turn on memory allocation timer.
#endif // DFT_PROFILING

  void *p;
  int * pint;
  int dieflag = 0;

  if (size <= 0)
    {
#ifdef DFT_PROFILING
      timerOff(8);  // Turn off memory allocation timer.
#endif // DFT_PROFILING

      return((void*) 0);
    }

  // Allocating the memory
#ifdef DFT_MEMALIGN    // not all platforms have memalign in standard lib
  p = (void *) memalign(32, size + PREFIX*sizeof(int) + POSTFIX*sizeof(char) );
#else
  p = (void *) malloc(size + PREFIX*sizeof(int) + POSTFIX*sizeof(char) );
#endif

  if (p == (void*) 0)
    {
      mem_trace_report();
      die("Can't malloc %d for %s in %s.\n", size, what, where);
    }

  // Post-allocation work.

  // The prefix memory is 
  //     [MAGIC1, SIZE, NUM_BLOCKS, SIZE, MAGIC2, ... content ... ]
  //
  pint = (int*) p;
  pint[0] = MAGIC1;
  pint[1] = size;
  pint[2] = num_blocks;
  pint[3] = size;
  pint[4] = MAGIC2;
  pint += PREFIX;  // PREFIX >= 5

  p = (void*) pint;

  num_blocks++;  
  accum_alloc += size;
  current_alloc += size;
  if (current_alloc > max_alloc)
    max_alloc = current_alloc;
  if (size > max_block)
    max_block = size;

  {
    int i;
    char *c = (char *)p;
    for (i=0; i < size; i++)
      c[i] = 0;
  }

  // The postfix memory is
  //    [ ... content ... ,  MAGIC3 ]
  //
  strncpy((char*)p+size, MAGIC3, POSTCHECK);

#ifdef DFT_PROFILING
  timerOff(8);  // Turn off memory allocation timer.
#endif // DFT_PROFILING

  return p;

}

void
myfree(void *ptr)
{
  int size, * pint;
  char errstr[100];
  int error_flag = 0;

  // check for prefix MAGICs.
  pint = (int*) ptr;
  pint -= PREFIX;

  if (pint[0] != MAGIC1)
    {
      // MAGIC string 1 corrupted.
      dft_log(DFT_SILENCE,
	      "MAGIC 1 corrupted in memory trace, block %d,  %d != %d\n",
	      pint[2], pint[0], MAGIC1);
      dft_log_flush();
      error_flag = 1;
    }

  if (pint[4] != MAGIC2)
    {
      // MAGIC string 2 corrupted.
      dft_log(DFT_SILENCE,
	      "MAGIC 2 corrupted in memory trace, block %d,  %d != %d\n",
	      pint[2], pint[4], MAGIC2);
      dft_log_flush();
      error_flag = 1;
    }

  size = pint[1];
  if (pint[1] != pint[3])
    {
      // size of blocks mismatch.
      dft_log(DFT_SILENCE,
	      "Size of blocks mismatch, block %d,  %d != %d\n",
	      pint[2], pint[1], pint[3]); dft_log_flush();
      error_flag = 1;
    }

  // check for postfix MAGICs.
  if (error_flag == 0) {
    char * pchar = (char*) ptr + size;

    if (strncmp(pchar,MAGIC3,POSTCHECK)!=0)
      {
	// MAGIC string 3 corrupted.
	dft_log(DFT_SILENCE,
		"MAGIC 3 corrupted in memory trace\n");
	dft_log_flush();
	error_flag = 1;
      }
  }

  if (error_flag == 0) {

    size = pint[1];
    current_alloc += -size;
    free(pint);

  }
  else
    {
      mem_trace_report();
      dft_log_flush();
      die("Die in mem trace.");
    }
  
}


#endif // DFT_TRACE_MEM
