#ifdef DOC_BUILD_INTERNAL
#endif
#include "sharing.h"
#include "misc.h"
SYMBOL sharing_IdxToSymb(unsigned Idx)
{
  return (SYMBOL)-symbol_SetIndexType(Idx,CONSTANT);
}
unsigned sharing_ConstantSIdx(SYMBOL Symb)
{
#ifdef SPASS_CHECK
  if((intptr_t)Symb >= 0){
    misc_StartErrorReport();
    misc_ErrorReport("\n %s: symbol is a variable.",__func__);
    misc_FinishErrorReport();
  }
#endif
  return symbol_GetIndex(Symb);
}
unsigned sharing_ConstantTIdx(TERM Term)
{
#ifdef SPASS_CHECK
  if(term_GetArity(Term) != 0){
    misc_StartErrorReport();
    misc_ErrorReport("\n %s: constant isn't shared.",
       __func__);
    misc_FinishErrorReport();
  }
#endif
  return symbol_GetIndex(term_TopSymbol(Term));
}
unsigned sharing_ComplexIdx(intptr_t Idx)
{
#ifdef SPASS_CHECK
  if(Idx <= 0){
    misc_StartErrorReport();
    misc_ErrorReport("\n %s: index isn't complex.",
       __func__);
    misc_FinishErrorReport();
  }
#endif
  return (unsigned)Idx-1;
}
unsigned sharing_SharedIdx(TERM Term)
{
#ifdef SPASS_CHECK
  if(term_Index(Term) <= 0){
    misc_StartErrorReport();
    misc_ErrorReport("\n %s: term isn't shared.",
       __func__);
    misc_FinishErrorReport();
  }
#endif
  return term_Index(Term)-1;
}
MEMORY sharing_GetMemory(SHARER Sharer)
{
  return Sharer->mem;
}
SYMBSTATE sharing_GetSymbstate(SHARER Sharer)
{
  return Sharer->symbstate;
}
TERMSTATE sharing_GetTermstate(SHARER Sharer)
{
  return Sharer->termstate;
}
SHARER sharing_Init(TERMSTATE Termstate )
{
  MEMORY mem;
  SYMBSTATE symbstate;
  SHARER sharer;
  mem = term_GetMemory(Termstate);
  symbstate = term_GetSymbstate(Termstate);
  sharer = (SHARER)memory_Malloc(mem,sizeof(SHARER_NODE));
  sharer->mem = mem;
  sharer->symbstate = symbstate;
  sharer->termstate = Termstate;
  sharer->cons = array_Create(mem,symbol_GetStateSigLength(symbstate));
  array_SetSize(sharer->cons,symbol_GetStateSigLength(symbstate));
  sharer->shared = array_Create(mem,128);
  sharer->vars = array_Create(mem,16);
  sharer->subterms = array_Create(mem,128);
  sharer->buffer = memory_Malloc(mem, sizeof(char)*100);
  return sharer;
}
void sharing_Free(SHARER Sharer)
{
  MEMORY mem;
  unsigned size, i;
  TERMSTATE termstate;
  TERM term;
  mem = sharing_GetMemory(Sharer);
  termstate = sharing_GetTermstate(Sharer);
  array_Delete(mem,Sharer->subterms);
  for(i = 0, size = array_GetSize(Sharer->shared); i < size; i++){
    term = (TERM)array_GetElement(Sharer->shared,i);
    if(term == NULL){
      continue;
    }
    array_Delete(mem,term_SupertermArray(term));
    if(term_IsValueConstant(termstate,term)){
      term_Delete(termstate,term);
    }else{
      term_Free(termstate,term);
    }
  }
  array_Delete(mem,Sharer->shared);
  array_Delete(mem,Sharer->cons);
  array_Delete(mem,Sharer->vars);
  memory_Free(mem, Sharer->buffer, sizeof(char)*100);
  memory_Free(mem,Sharer,sizeof(SHARER_NODE));
}
TERM sharing_FindAbstrConstantOrCreate(SHARER Sharer, TERM ValConstant);
TERM sharing_GetSharedTermFlat(SHARER Sharer, TERM Term)
{
  MEMORY mem;
  TERMSTATE termstate;
  TERM first, parent, shared, candidate, arg;
  ARRAY candidates, superterms;
  unsigned i, j, size, arity;
  intptr_t pos;
  SYMBOL top;
  termstate = sharing_GetTermstate(Sharer);
  mem = sharing_GetMemory(Sharer);
#ifdef SPASS_CHECK
  for(i = 0, arity = term_GetArity(Term); i < arity; i++){
    shared = term_GetArgument(Term,i);
    if(shared == NULL || term_Index(shared) == 0 || shared !=
       (TERM)array_GetElement(Sharer->shared,
         sharing_SharedIdx(shared))){
      if(shared != NULL){
 term_Print(sharing_GetTermstate(Sharer),shared);
 printf("\n");
      }else{
 printf("(NULL)\n");
      }
      misc_StartErrorReport();
      misc_ErrorReport("\n %s: Argument %u of term is not shared.",__func__,i);
      misc_FinishErrorReport();
    }
  }
#endif
  parent = term_Superterm(Term);
  pos = term_Index(Term);
  arity = term_GetArity(Term);
  top = term_TopSymbol(Term);
  if(arity == 0){
    if(term_IsVariable(termstate,Term)){
      i = (unsigned)top;
      if(i < array_GetSize(Sharer->vars)){
 shared = (TERM)array_GetElement(Sharer->vars,i);
      }else{
 Sharer->vars = array_AddAtIndex(mem,Sharer->vars,i,(intptr_t)NULL);
 shared = NULL;
      }
      if(shared == (TERM)NULL){
 shared = Term;
        term_SetProperty(shared,(intptr_t)NULL);
 term_RplacSupertermArray(shared,array_Create(mem,2));
 Sharer->vars = array_AddAtIndex(mem,Sharer->vars,i,(intptr_t)shared);
 Sharer->shared = array_Add(mem,Sharer->shared,(intptr_t)shared);
 term_SetIndex(shared,array_GetSize(Sharer->shared));
      }else{
 term_Delete(termstate,Term);
      }
    }else if (term_IsValueConstant(termstate, Term)) {
      shared = sharing_FindAbstrConstantOrCreate(Sharer,Term);
    }else{
      i = sharing_ConstantSIdx(top);
      if(i < array_GetSize(Sharer->cons)){
 shared = (TERM)array_GetElement(Sharer->cons,i);
      }else{
 Sharer->cons = array_AddAtIndex(mem,Sharer->cons,i,(intptr_t)NULL);
 shared = NULL;
      }
      if(shared == (TERM)NULL){
 shared = Term;
        term_SetProperty(shared,(intptr_t)NULL);
 term_RplacSupertermArray(shared,array_Create(mem,2));
 Sharer->cons = array_AddAtIndex(mem,Sharer->cons,i,(intptr_t)shared);
 Sharer->shared = array_Add(mem,Sharer->shared,(intptr_t)shared);
 term_SetIndex(shared,array_GetSize(Sharer->shared));
      }else{
 term_Delete(termstate,Term);
      }
    }
    term_SetSize(shared,1);
  }else{
    first = term_FirstArgument(Term);
    candidates = term_SupertermArray(first);
    shared = NULL;
    size = array_GetSize(candidates);
    for(i = 0; i < size; i+=2){
      if(array_GetElement(candidates,i) == 0){
 candidate = (TERM)array_GetElement(candidates,i+1);
 if(candidate == NULL){
   continue;
 }
 if(term_TopSymbol(candidate) != top){
   continue;
 }
 if(term_GetArity(candidate) != arity){
   continue;
 }
 shared = candidate;
 for(j = 1; j < arity; j++){
   if(term_GetArgument(Term,j) != term_GetArgument(candidate,j)){
     shared = NULL;
     break;
   }
 }
 if(shared != NULL){
   term_Free(termstate,Term);
   break;
 }
      }
    }
    if(shared == NULL){
      shared = Term;
      Sharer->shared = array_Add(mem,Sharer->shared,(intptr_t)shared);
      term_SetIndex(shared,array_GetSize(Sharer->shared));
      superterms = array_Create(mem,2);
      term_RplacSupertermArray(shared,superterms);
      term_SetSize(shared,1);
      for(j = 0; j < arity; j++){
 arg = term_GetArgument(shared,j);
 superterms = term_SupertermArray(arg);
 superterms = array_Add(mem,superterms,(intptr_t)j);
 superterms = array_Add(mem,superterms,(intptr_t)shared);
 term_RplacSupertermArray(arg,superterms);
 term_SetSize(shared,term_Size(shared)+term_Size(arg));
      }
    }
  }
  if(parent != NULL){
    term_SetArgument(parent,pos,shared);
  }
  return shared;
}
TERM sharing_GetSharedTermByArgs(SHARER Sharer, SYMBOL Top, ARRAY Args)
{
  MEMORY mem;
  TERMSTATE termstate;
  SYMBSTATE symbstate;
  TERM first, shared, candidate, arg;
  ARRAY candidates, superterms;
  unsigned i, j, size, arity;
  termstate = sharing_GetTermstate(Sharer);
  symbstate = sharing_GetSymbstate(Sharer);
  mem = sharing_GetMemory(Sharer);
#ifdef SPASS_CHECK
  if (Args) {
    if(symbol_GetArity(symbstate,Top) >= 0 &&
       symbol_GetArity(symbstate,Top) != array_GetSize(Args)){
      misc_StartErrorReport();
      misc_ErrorReport("\n %s: The number of arguments %u does not match the arity of the top symbol.",__func__,array_GetSize(Args),symbol_GetArity(symbstate,Top));
      misc_FinishErrorReport();
    }
    for(i = 0, arity = array_GetSize(Args); i < arity; i++){
      shared = (TERM)Args[i];
      if(shared == NULL || term_Index(shared) == 0 || shared !=
  (TERM)array_GetElement(Sharer->shared,
    sharing_SharedIdx(shared))){
 if(shared != NULL){
   term_Print(sharing_GetTermstate(Sharer),shared);
   printf("\n");
 }else{
   printf("(NULL)\n");
 }
 misc_StartErrorReport();
 misc_ErrorReport("\n %s: Argument %u of term is not shared.",__func__,i);
 misc_FinishErrorReport();
      }
    }
  }
#endif
  arity = (Args ? array_GetSize(Args) : 0);
  if(arity == 0) {
    if(symbol_IsVariable(symbstate,Top)){
      i = (unsigned)Top;
      if(i < array_GetSize(Sharer->vars)){
 shared = (TERM)array_GetElement(Sharer->vars,i);
      }else{
 Sharer->vars = array_AddAtIndex(mem,Sharer->vars,i,(intptr_t)NULL);
 shared = NULL;
      }
      if(shared == (TERM)NULL){
 shared = term_CreateArity(termstate,Top,0);
 term_RplacSupertermArray(shared,array_Create(mem,2));
 Sharer->vars = array_AddAtIndex(mem,Sharer->vars,i,(intptr_t)shared);
 Sharer->shared = array_Add(mem,Sharer->shared,(intptr_t)shared);
 term_SetIndex(shared,array_GetSize(Sharer->shared));
      }
    }else{
      i = sharing_ConstantSIdx(Top);
      if(i < array_GetSize(Sharer->cons)){
 shared = (TERM)array_GetElement(Sharer->cons,i);
      }else{
 Sharer->cons = array_AddAtIndex(mem,Sharer->cons,i,(intptr_t)NULL);
 shared = NULL;
      }
      if(shared == (TERM)NULL){
 shared = term_CreateArity(termstate,Top,0);
 term_RplacSupertermArray(shared,array_Create(mem,2));
 Sharer->cons = array_AddAtIndex(mem,Sharer->cons,i,(intptr_t)shared);
 Sharer->shared = array_Add(mem,Sharer->shared,(intptr_t)shared);
 term_SetIndex(shared,array_GetSize(Sharer->shared));
      }
    }
    term_SetSize(shared,1);
  }else{
    first = (TERM)Args[0];
    candidates = term_SupertermArray(first);
    shared = NULL;
    size = array_GetSize(candidates);
    for(i = 0; i < size; i+=2){
      if(array_GetElement(candidates,i) == 0){
 candidate = (TERM)array_GetElement(candidates,i+1);
 if(candidate == NULL){
   continue;
 }
 if(term_TopSymbol(candidate) != Top){
   continue;
 }
 if(term_GetArity(candidate) != arity){
   continue;
 }
 shared = candidate;
 for(j = 1; j < arity; j++){
   if((TERM)Args[j] != term_GetArgument(candidate,j)){
     shared = NULL;
     break;
   }
 }
 if(shared != NULL){
   break;
 }
      }
    }
    if(shared == NULL){
      shared = term_Create(termstate,Top,Args);
      Sharer->shared = array_Add(mem,Sharer->shared,(intptr_t)shared);
      term_SetIndex(shared,array_GetSize(Sharer->shared));
      superterms = array_Create(mem,2);
      term_RplacSupertermArray(shared,superterms);
      term_SetSize(shared,1);
      for(j = 0; j < arity; j++){
 arg = term_GetArgument(shared,j);
 superterms = term_SupertermArray(arg);
 superterms = array_Add(mem,superterms,(intptr_t)j);
 superterms = array_Add(mem,superterms,(intptr_t)shared);
 term_RplacSupertermArray(arg,superterms);
 term_SetSize(shared,term_Size(shared)+term_Size(arg));
      }
    }
  }
  return shared;
}
TERM sharing_GetSharedTerm(SHARER Sharer, TERM Term)
{
  MEMORY mem;
  intptr_t i, j, arity;
  ARRAY subterms;
  TERM subt, arg, shared;
  mem = sharing_GetMemory(Sharer);
  term_RplacSuperterm(Term,NULL);
  term_SetIndex(Term,0);
  subterms = Sharer->subterms;
  subterms = array_Add(mem,subterms,(intptr_t)Term);
  for(i = 0; i < array_GetSize(subterms); i++){
    subt = (TERM)array_GetElement(subterms,i);
    for(j = 0, arity = term_GetArity(subt); j < arity; j++){
      arg = term_GetArgument(subt,j);
      term_RplacSuperterm(arg,subt);
      term_SetIndex(arg,j);
      subterms = array_Add(mem,subterms,(intptr_t)arg);
    }
  }
  for(i = array_GetSize(subterms)-1; i >= 0; i--){
    subt = (TERM)array_GetElement(subterms,i);
    shared = sharing_GetSharedTermFlat(Sharer,subt);
    subterms = array_AddAtIndex(mem,subterms,i,(intptr_t)shared);
  }
  shared = (TERM)array_GetElement(subterms,0);
  array_Clean(subterms);
  Sharer->subterms = subterms;
  return shared;
}
TERM sharing_FindAbstrConstantOrCreate(SHARER Sharer, TERM ValConstant)
{
  misc_StartErrorReport();
  misc_ErrorReport("\n %s: This function is no longer implemented.",__func__);
  misc_FinishErrorReport();
#ifdef SPASS_CHECK
    misc_StartErrorReport();
    misc_ErrorReport("\n %s: Unknown type of value constant or respective state of the type was NULL.",__func__);
    misc_FinishErrorReport();
#endif
  return NULL;
}
BOOL sharing_IsShared(TERM Term)
{
  return term_Index(Term) > 0;
}
BOOL sharing_IsSharedBySharer(SHARER Sharer, TERM Term)
{
  return sharing_IsShared(Term) &&
    sharing_SharedIdx(Term) < array_GetSize(Sharer->shared) &&
    (TERM)(Sharer->shared[sharing_SharedIdx(Term)]) == Term;
}
BOOL sharing_BuildTermNewArgument(ARRAY Argsnew, unsigned Arity)
{
  unsigned i;
  for (i=0; i<Arity; i++) {
    if (Argsnew[i] == (intptr_t)TRUE) {
      return TRUE;
    }
  }
  return FALSE;
}
ARRAY sharing_BuildTerm(SHARER Sharer, TERMSTATE Termstate, MEMORY Mem, SYMBOL Symb, unsigned arity, ARRAY Result, ARRAY Argscounter, ARRAY Buildargs, unsigned Oweight)
{
  unsigned weight, i;
  TERM term;
  weight = 1;
  for (i=0;i<arity && weight <= Oweight;i++) {
    weight += term_Size((TERM)Result[Argscounter[i]]);
    Buildargs[i] = Result[Argscounter[i]];
  }
  if (weight > Oweight)
    return Result;
  array_SetSize(Buildargs, arity);
  term = sharing_GetSharedTermByArgs(Sharer, Symb, Buildargs);
  Result = array_Add(Mem, Result, (intptr_t)term);
  return Result;
}
ARRAY sharing_GenerateSmallerEqualAtoms(SHARER Sharer, ARRAY Predicates, ARRAY Terms, unsigned Oweight)
{
  ARRAY result, argcounter, buildsize, buildargs;
  unsigned i, j, carity,termsize,csize;
  SYMBSTATE symbstate;
  SYMBOL symb;
  MEMORY mem;
  int maxarity;
  symbstate = Sharer->symbstate;
  mem = Sharer->mem;
  result = array_Create(mem, (array_GetSize(Predicates)*array_GetSize(Terms)));
  maxarity = 0;
  termsize = array_GetSize(Terms);
  Oweight--;
  for (i=0;i<array_GetSize(Predicates);i++) {
    if (symbol_GetArity(symbstate, (SYMBOL)Predicates[i]) == 0)
      result = array_Add(mem, result, (intptr_t)sharing_GetSharedTermByArgs(Sharer, (SYMBOL)Predicates[i], NULL));
    if (symbol_GetArity(symbstate, (SYMBOL)Predicates[i]) > maxarity)
      maxarity = symbol_GetArity(symbstate, (SYMBOL)Predicates[i]);
  }
  argcounter = array_Create(mem, maxarity);
  buildargs = array_Create(mem, maxarity);
  buildsize = array_Create(mem, maxarity);
  for (i=0;i<array_GetSize(Predicates);i++) {
    symb = (SYMBOL)Predicates[i];
    carity = symbol_GetArity(symbstate, symb);
    if (carity > 0) {
      for (j=0;j<carity;j++)
 argcounter[j] = 0;
      j = 0;
      csize = 0;
      while (argcounter[0] <= termsize) {
 while (j < carity && argcounter[0] <= termsize) {
   if (argcounter[j] < termsize) {
     if (term_Size((TERM)Terms[argcounter[j]]) + csize <= Oweight) {
       buildargs[j] = Terms[argcounter[j]];
       buildsize[j] = term_Size((TERM)buildargs[j]);
       csize += buildsize[j];
       argcounter[j] = argcounter[j] + 1;
       j++;
     }
     else {
       if (j==0) {
  argcounter[0] = termsize + 1;
  j = carity;
       }
       else {
  argcounter[j] = 0;
  j--;
  csize -= buildsize[j];
       }
     }
   }
   else {
     if (j!=0) {
       argcounter[j] = 0;
       csize -= buildsize[j];
       j--;
     }
     else {
       argcounter[0] = termsize + 1;
       j = carity;
     }
   }
 }
 if (argcounter[0] <= termsize) {
   array_SetSize(buildargs, carity);
   result = array_Add(mem, result, (intptr_t)sharing_GetSharedTermByArgs(Sharer, symb, buildargs));
   j--;
   csize -= buildsize[j];
 }
      }
    }
  }
  array_Delete(mem, buildargs);
  array_Delete(mem, buildsize);
  array_Delete(mem,argcounter);
  return result;
}
ARRAY sharing_GenerateSmallerEqualTerms(SHARER Sharer, ARRAY Constants, ARRAY Functions, unsigned Oweight)
{
  ARRAY functions,result, argscounter, argsnew, buildargs;
  unsigned i, j, maxarity,fsymbs,subtermstart,subtermend, subtermround, minweight,ominweight,arity;
  TERMSTATE termstate;
  SYMBSTATE symbstate;
  SYMBOL symb;
  MEMORY mem;
  BOOL update;
  termstate = Sharer->termstate;
  symbstate = Sharer->symbstate;
  mem = Sharer->mem;
  result = array_Create(mem, Oweight * (array_GetSize(Constants)+array_GetSize(Functions)));
  functions = array_Create(mem, array_GetSize(Functions));
  maxarity = 0;
  for (i=0; i<array_GetSize(Constants);i++) {
    symb = (SYMBOL)Constants[i];
    result = array_Add(mem, result, (intptr_t)sharing_GetSharedTerm(Sharer, term_CreateArity(termstate, symb, 0)));
  }
  for (i=0; i<array_GetSize(Functions);i++) {
    symb = (SYMBOL)Functions[i];
    functions = array_Add(mem, functions, (intptr_t)symb);
    if (symbol_GetArity(symbstate,symb) > (int)maxarity)
      maxarity = symbol_GetArity(symbstate,symb);
  }
  if (array_GetSize(result) == 0) {
    array_Delete(mem,result);
    return NULL;
  }
  fsymbs = array_GetSize(functions);
  i = 0;
  subtermstart = 0;
  subtermround = 0;
  subtermend = array_GetSize(result);
  minweight = term_Size((TERM)result[subtermstart]);
  ominweight = minweight;
  buildargs = array_Create(mem, maxarity);
  argscounter = array_Create(mem, maxarity);
  argsnew = array_Create(mem, maxarity);
  printf("\n Functions: %u MaxArity %u", fsymbs, maxarity);
  while (fsymbs>0 && i < fsymbs) {
    symb = (SYMBOL)functions[i];
    arity = symbol_GetArity(symbstate,symb);
    if ((1 + minweight + (arity-1)*ominweight) > Oweight) {
      fsymbs--;
      if (fsymbs != 0 && i < fsymbs)
 functions[i] = functions[fsymbs];
      if (i == fsymbs)
 i = 0;
    }
    else {
      for (j = 0; j < arity; j++ ) {
 argscounter[j] = subtermstart;
 argsnew[j] = (subtermstart >= subtermround ? (intptr_t)TRUE : (intptr_t)FALSE);
      }
      j = 0;
      arity--;
      while (argscounter[j] < subtermend) {
 if (j == arity) {
   if (sharing_BuildTermNewArgument(argsnew, symbol_GetArity(symbstate,symb)))
     result = sharing_BuildTerm(Sharer, termstate, mem, symb, arity+1, result, argscounter, buildargs, Oweight);
   update = TRUE;
   while (update) {
     if (argscounter[j] + 1 < subtermend) {
       update = FALSE;
       argscounter[j]++;
       argsnew[j] = (argscounter[j] >= subtermround ? TRUE : FALSE);
     }
     else {
       if (j != 0) {
  argscounter[j] = subtermstart;
  argsnew[j] = (subtermstart >= subtermround ? TRUE : FALSE);
  j--;
       }
       if (j == 0 && argscounter[j] + 1 == subtermend) {
  argscounter[j]++;
  update = FALSE;
       }
     }
   }
 }
 else
   j++;
      }
      i++;
    }
    if (fsymbs > 0 && i == fsymbs) {
      i=0;
      srtg_AMergeSortCompare(mem, result, &result[subtermstart], result + (array_GetSize(result) - 1), (BOOL (*)(POINTER, POINTER))term_CompareSizeSortingFunc);
      subtermround = subtermend;
      subtermend = array_GetSize(result);
      if (subtermround == subtermend)
 fsymbs = 0;
      else
 minweight = term_Size((TERM)result[subtermround]);
    }
  }
  array_Delete(mem, argscounter);
  array_Delete(mem, argsnew);
  array_Delete(mem, functions);
  array_Delete(mem, buildargs);
  srtg_AMergeSortCompare(mem, result, result, result + (array_GetSize(result) - 1), (BOOL (*)(POINTER, POINTER))term_CompareSizeSortingFunc);
  return result;
}
