/****************************************************************************/
/* This file is part of FreeFEM.                                            */
/*                                                                          */
/* FreeFEM is free software: you can redistribute it and/or modify          */
/* it under the terms of the GNU Lesser General Public License as           */
/* published by the Free Software Foundation, either version 3 of           */
/* the License, or (at your option) any later version.                      */
/*                                                                          */
/* FreeFEM 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 Lesser General Public License for more details.                      */
/*                                                                          */
/* You should have received a copy of the GNU Lesser General Public License */
/* along with FreeFEM. If not, see <http://www.gnu.org/licenses/>.          */
/****************************************************************************/
// SUMMARY : ...
// LICENSE : LGPLv3
// ORG     : LJLL Universite Pierre et Marie Curie, Paris, FRANCE
// AUTHORS : Frederic Hecht
// E-MAIL  : frederic.hecht@sorbonne-univeriste.fr

#include <iostream>
#include <typeinfo>
#include <cstddef>
#include <cstdlib>
#include <cassert>
#include "error.hpp"
using namespace std;

#include "CodeAlloc.hpp"

/* now in global.cpp
size_t CodeAlloc::nb=0, CodeAlloc::lg=0,CodeAlloc::nbpx=0,CodeAlloc::chunk=2048;
size_t CodeAlloc::nbt,CodeAlloc::nbdl=0;
CodeAlloc ** CodeAlloc::mem=0;
bool CodeAlloc::sort=true;
bool  CodeAlloc::cleanning=false;
*/
static long kerr = 0;
static long nbsort = 0;
template<class T>
static void HeapSort(T *c, long n) {
  long l, j, r, i;
  T crit;
  c--; // on decale de 1 pour que le tableau commence a 1
  if (n <= 1) return;
  l = n/2 + 1;
  r = n;
  while (1) { // label 2
    if (l <= 1 ) { // label 20
      crit = c[r];
      c[r--] = c[1];
      if ( r == 1 ) { c[1] = crit; return; }
    } else crit = c[--l];
    j = l;
    while (1) { // label 4
      i = j;
      j = 2*j;
      if (j > r) { c[i] = crit; break; } // L8 -> G2
      if ((j < r) && (c[j] < c[j+1])) j++; // L5
      if (crit < c[j]) c[i] = c[j]; // L6+1 G4
      else { c[i] = crit; break; } //L8 -> G2
    }
  }
}

 void CodeAlloc::resize() {
  Sort_mem();
  if (nbt*1.5 + 10 >= nbpx ) {
    nbpx = chunk;
    mem = (CodeAlloc**)realloc(mem, chunk*sizeof(void*));
    if (mem) nbpx = chunk;
    else ErrorExec("Alloc problem", 0);
    chunk *= 3;
    chunk /= 2;
    assert(chunk > nbpx);
  }
}

void CodeAlloc::Sort_mem() {
  size_t i, j;
  if (nbt == 0) return;
  nbsort++;
  HeapSort(mem, nbt);

  for (i = 0, j = 0; i < nbt; i++)
    if (!isdel(i))
      mem[j++] = mem[i];
  nbt = j;
  if (nbt != nb)
    assert(nbt == nb);
}

void CodeAlloc::clear() {
  return;

  // cleanning=true;
  // Sort_mem();
  // cout << " Clear Alloc Mem: "<< nb << endl; ;
  // for(size_t i=0;i<nbt;i++)
  // {
  // if( ! isdel(i))
  // delete mem[i];
  // }
  // cout << " nb " << nb <<  " ==  " << 0 << " , nbsort: " << nbsort <<endl;
  // nb=0;
  // lg=0;
  // nbpx=0;
  // chunk=2048;
  // sort=true;
  // free(mem);
  // mem=0;
  // nbdl=0;
  // if(kerr)
  // cout << " CodeAlloc: nb err delete  " << kerr << endl;
  // kerr =0;
  // cleanning=false;
}

void CodeAlloc::ErrorDel(void *pp) {
  //    cerr << " Alloc:: Sorry the pointeur " << pp << " is not allocated \n";
  kerr++;
}

void CodeAlloc::operator delete(void * pp) {
  //  return ; // FH
  if (!sort && (nb*2 > nbt)) Sort_mem();
  int ib = 0, ie = nbt-1, im;
  if (pp < mem[ib]) ErrorDel(pp);
  if (pp > mem[ie]) ErrorDel(pp);
  int p = -1;
  while (ib < ie) {
    im = (ib + ie)/2;
    {
      if (pp < mem[im]) ie = im - 1; // in [ib , im-1]
      else if (mem[im] == pp) { p = im; break; }
      else ib = im + 1;// in [im+1 , ie]
    }
  }
  if (p < 0 && mem[ib] == pp) p = ib;

  if (p < 0)
    ErrorDel(pp);
  else {
    setdel(p); //le tableau est detruit
    nbdl++; nb--;
    ::operator delete(pp);
  }
}
