#ifndef __MAVLTREE

#define __MAVLTREE

#include <assert.h>
#include "MBStaticList.h"


#undef  MAX
#undef  MIN
#define MAX(x,y) ((x)>(y)?x:y)
#define MIN(x,y) ((x)<(y)?x:y)

template <class MKey, class MValue>
class MAVLTree
{
  public:
    MAVLTree();
    ~MAVLTree();

    int Init                  (const bool bAscend=true);
    int Shutdown              ();

    int First                 () const;
    int Last                  () const;
    int Next                  (const int iIterator) const ;
    int Previous              (const int iIterator) const;

    int Insert                (const MKey& kKey, const MValue& vValue);
    int DeleteFromKey         (const MKey& kKey);
    int DeleteFromID          (const int   iID);
 
    int GetFromIDP            (const int   iID , MValue*& pValue);
    int GetFromID             (const int   iID , MValue&  Value);
    int GetFromKeyP           (const MKey& kKey, MValue*& pValue);
    int GetFromKey            (const MKey& kKey, MValue&  Value);    
    int GetIDFromKey          (const MKey& kKey)  const;

    int Parent                (const int   iID) const;
    int Left                  (const int   iID) const;
    int Right                 (const int   iID) const;
    int Root                  () const;

    int GetNumberElements     () const;

    
  protected:
    typedef int eDirection;
    #define ELEFT  0
    #define ERIGHT 1
    
    struct MAVLTreeNode
    {
      MKey  m_kKey;
      int   m_iDataID;
      int   m_iParent;
      int   m_iChilds[2];
      int   m_iBalance;
    };

    bool          IsLowerOrder        (const MKey& KeyA, const MKey& KeyB) const;
    int           Son                 (const int iID, const eDirection edDirection) const;
    int           First               (const int iSubtreeRoot) const;
    int           Last                (const int iSubtreeRoot) const;    
    MAVLTreeNode* GetNode             (const int iNodeID) const;    
    MValue*       GetDataFromNode     (const int iNodeID) const;
    MValue*       GetData             (const int iDataID) const;
    int           GetNodeIDFromKey    (const MKey& kKey)  const;
    
    int           Rotate              (int  iNodeID, const eDirection edDirection);
    int           RotateTwice         (int  iNodeID, const eDirection edDirection);
    int           Rebalance           (int  iNodeID);
    int           InsertRecursive     (const MKey& kKey, const MValue& vValue, const int iRoot, int& iChange, bool& bLink);
    int           InsertNode          (const MKey& kKey, const MValue& Value);
    int           LinkNodes           (const int iParentID, const eDirection edDirection, const int iSonID);
    int           ModifyBalance       (int iID, int iHowMany);

    int           AssertCorrectBalance(int iRoot);
    int           Balance             (int iID);    
    
    
    
    

    int                                m_iTreeRoot;
    bool                               m_bAscend;


    MBStaticList<MAVLTreeNode>         m_slNodes;
    MBStaticList<MValue>               m_slData;
};


// Nombre      : 
// Parametros  : 
// Retorno     : 
// Descripcion : 
template <class MKey, class MValue>
MAVLTree<MKey, MValue>::MAVLTree()
{
  m_iTreeRoot=-1;
}

// Nombre      : 
// Parametros  : 
// Retorno     : 
// Descripcion : 
template <class MKey, class MValue>
MAVLTree<MKey, MValue>::~MAVLTree()
{
  Shutdown();
}

template <class MKey, class MValue>
bool MAVLTree<MKey, MValue>::IsLowerOrder(const MKey& KeyA, const MKey& KeyB) const
{
  bool bResult;
  bResult=KeyA<KeyB;
  if (!m_bAscend)
  {
    bResult=!bResult;
  }

  return (bResult);
}

// Nombre      : 
// Parametros  : 
// Retorno     : 
// Descripcion : 
template <class MKey, class MValue>
int MAVLTree<MKey, MValue>::Init(const bool bAscend)
{
  if (m_slNodes.Init()!=0)
  {
    return (-1);
  }

  if (m_slData.Init()!=0)
  {
    return (-1);
  }

  m_iTreeRoot =-1;
  m_bAscend   =bAscend;

  return (0);
}

// Nombre      : 
// Parametros  : 
// Retorno     : 
// Descripcion : 
template <class MKey, class MValue>
int MAVLTree<MKey, MValue>::Shutdown()
{
  m_slNodes.Shutdown();
  m_slData.Shutdown();

  m_iTreeRoot=-1;

  return (0);
}

template <class MKey, class MValue>
MAVLTree<MKey, MValue>::MAVLTreeNode* MAVLTree<MKey, MValue>::GetNode(const int iNodeID) const
{
  MAVLTreeNode* pResult=NULL;
  int           iResult;

  iResult=m_slNodes.Get(iNodeID, pResult);
  assert(iResult!=-1);

  return (pResult);
}

template <class MKey, class MValue>
MValue* MAVLTree<MKey, MValue>::GetData(const int iDataID) const
{
  MValue*       pResult=NULL;  
  int iResult;

  iResult=m_slData.Get(iDataID, pResult);
  assert(iResult!=-1);

  return (pResult);
}

template <class MKey, class MValue>
MValue* MAVLTree<MKey, MValue>::GetDataFromNode(const int iNodeID) const
{
  MAVLTreeNode* pNode;

  pNode=GetNode(iNodeID);
  assert(pNode);

  return (GetData(pNode->m_iDataID));
}

   

// Nombre      : 
// Parametros  : 
// Retorno     : 
// Descripcion : 
template <class MKey, class MValue>
int MAVLTree<MKey, MValue>::First() const
{  
  return (First(m_iTreeRoot));
}

// Nombre      : 
// Parametros  : 
// Retorno     : 
// Descripcion : 
template <class MKey, class MValue>
int MAVLTree<MKey, MValue>::Last() const
{
  return (Last(m_iTreeRoot));
}

// Nombre      : 
// Parametros  : 
// Retorno     : 
// Descripcion : 
template <class MKey, class MValue>
int MAVLTree<MKey, MValue>::First(const int iSubtreeRoot) const
{
  int iIterator=iSubtreeRoot;

  while (iIterator!=-1)
  {
    int iLeft=Left(iIterator);
    if (iLeft==-1)
    {
      return (iIterator);
    }
    else
    {
       iIterator=iLeft;
    }    
  }  

  return (iIterator);
}

// Nombre      : 
// Parametros  : 
// Retorno     : 
// Descripcion : 
template <class MKey, class MValue>
int MAVLTree<MKey, MValue>::Last(const int iSubtreeRoot) const
{
  int iIterator=iSubtreeRoot;

  while (iIterator!=-1)
  {
    int iRight=Right(iIterator);
    if (iRight==-1)
    {
      return (iIterator);
    }
    else
    {
       iIterator=iRight;
    }    
  }  

  return (iIterator);
}
    

// Nombre      : 
// Parametros  : 
// Retorno     : 
// Descripcion : 
template <class MKey, class MValue>
int MAVLTree<MKey, MValue>::Next(const int iIterator) const
{
  int           iActual;
  int           iRight;
  int           iParent;
  
  // Si tenemos subarbol derecho retornamos el primer elemento de ese subarbol
  if ((iRight=Right(iIterator))!=-1)
  {
    return (First(iRight));
  }  

  // Si no lo tenemos tenemos que subir hasta que el nodo sea rama izquierda de algun otro
  iActual=iIterator;
  while ((iParent=Parent(iActual))!=-1)
  {
    if (Left(iParent)==iActual)
    {
      return (iParent);
    }
    
    iActual=iParent;    
  }

  return (-1);
}

// Nombre      : 
// Parametros  : 
// Retorno     : 
// Descripcion : 
template <class MKey, class MValue>
int MAVLTree<MKey, MValue>::Previous(const int iIterator) const
{
  int           iActual;
  int           iLeft;
  int           iParent;
  
  // Si tenemos subarbol derecho retornamos el primer elemento de ese subarbol
  if ((iLeft=Left(iIterator))!=-1)
  {
    return (Last(iLeft));
  }  

  // Si no lo tenemos tenemos que subir hasta que el nodo sea rama derecha de algun otro
  iActual=iIterator;
  while ((iParent=Parent(iActual))!=-1)
  {
    if (Right(iParent)==iActual)
    {
      return (iParent);
    }
    
    iActual=iParent;    
  }

  return (-1);
}

// Nombre      : 
// Parametros  : 
// Retorno     : 
// Descripcion : 
template <class MKey, class MValue>
int MAVLTree<MKey, MValue>::Insert(const MKey& kKey, const MValue& vValue)
{
  int   iChange;
  int   iNewNodeID;
  bool  bLink=false;

  if ( ((iNewNodeID=InsertRecursive(kKey, vValue, m_iTreeRoot, iChange, bLink))!=-1) &&
       (m_iTreeRoot==-1) )
  {
    LinkNodes(m_iTreeRoot, -1
      , iNewNodeID);
  }

  return (iNewNodeID);
}

// Nombre      : 
// Parametros  : 
// Retorno     : 
// Descripcion : 
template <class MKey, class MValue>
int MAVLTree<MKey, MValue>::InsertRecursive(const MKey& kKey, const MValue& vValue, const int iRoot, int& iChange, bool& bLink)
{
  MAVLTreeNode* pNode;
  int           iBalanceChange;
  int           iNewNodeID;
  eDirection    eAdvance;
  bool          bDoLink=bLink;

  if (iRoot==-1)
  {
    // Insertamos el nodo
    iNewNodeID  =InsertNode(kKey, vValue);

    // Hay que linkar
    bLink=true;

    // Hay cambio de altura
    iChange=1;
    return (iNewNodeID);
  }

  // Recursion
  pNode   =GetNode(iRoot);
  bool    bComp=IsLowerOrder(pNode->m_kKey,kKey);
  eAdvance=bComp?ERIGHT:ELEFT;
  
  if ((iNewNodeID=InsertRecursive(kKey, vValue, Son(iRoot, eAdvance), iChange, bDoLink))==-1)
  {
    return (-1);
  }
  else
  {
    if (bDoLink)
    {
      LinkNodes(iRoot, eAdvance, iNewNodeID);
      bLink=false;
    }
  }

  iBalanceChange=iChange*(bComp?1:-1);

  // Cambiamos el balanceo del nodo
  ModifyBalance(iRoot, iBalanceChange);

  // Rebalanceamos si es necesario
  iChange=(iBalanceChange && Balance(iRoot))?1-Rebalance(iRoot):0;

  return (iNewNodeID);
}

// Nombre      : 
// Parametros  : 
// Retorno     : 
// Descripcion : 
template <class MKey, class MValue>
int MAVLTree<MKey, MValue>::DeleteFromKey(const MKey& kKey)
{
  int iNodeID;
  
  if ( (iNodeID=GetNodeIDFromKey(kKey))==-1 )
  {
    return (-1);
  }

  return (DeleteFromID(iNodeID));
}

// Nombre      : 
// Parametros  : 
// Retorno     : 
// Descripcion : 
template <class MKey, class MValue>
int MAVLTree<MKey, MValue>::DeleteFromID(const int iID)
{
  MAVLTreeNode* pNode          ;
  MAVLTreeNode* pSubstitute    ;
  int           iStartRebalance;
  int           iHeightChange=0;
  int           iChange        ;
  
  
  if (iID==-1)
  {
    return (-1);
  }

  pNode=GetNode(iID);

  
  // Si no tiene hijos
  if ( (Left(iID)==-1) && (Right(iID)==-1))
  {
    // Siempre hay cambio
    iHeightChange   =Parent(iID)!=-1?(Left(Parent(iID))==iID?-1:1):0;
    iStartRebalance =Parent(iID);

    // Es hoja. Linkamos el padre a -1
    LinkNodes(Parent(iID), 
              Parent(iID)!=-1?
              (Left(Parent(iID))==iID?ELEFT:ERIGHT):
              -1,
              -1);


    
              
    
  }
  else if ( (Left(iID)==-1) || (Right(iID)==-1) )
  {
    // Siempre hay cambio
    iHeightChange   =Parent(iID)!=-1?(Left(Parent(iID))==iID?-1:1):0;
    iStartRebalance =Parent(iID);

    // Solo tiene un hijo
    LinkNodes(Parent(iID), 
              Parent(iID)!=-1?
              (Left(Parent(iID))==iID?ELEFT:ERIGHT):
              -1,
              Right(iID)==-1?Left(iID):Right(iID));

    
  }
  else
  {
    // Tiene 2 hijos. Le pegamos el cambiazo por el sucesor y lo borramos
    int iSubstitute =Next(iID);
    pSubstitute     =GetNode(iSubstitute);

    iStartRebalance=Parent(iSubstitute)!=iID?Parent(iSubstitute):iSubstitute;
    iHeightChange  =Parent(iSubstitute)!=-1?(Left(Parent(iSubstitute))==iSubstitute?-1:1):0;

    // Linkamos padre de iSubstitute a la parte derecha de iSubstitute. No tiene parte
    // izquierda pq si no no tendria sustituto.
    LinkNodes(Parent(iSubstitute), 
              (Left(Parent(iSubstitute))==iSubstitute?ELEFT:ERIGHT),
              Right(iSubstitute));   

    // Ahora tenemos que sustituir iID por iSubstitute en todos sus aspectos

    // Sustituimos padre
    LinkNodes(Parent(iID), 
    Parent(iID)!=-1?
    (Left(Parent(iID))==iID?ELEFT:ERIGHT):
    -1,
    iSubstitute);   

    // Hacemos que iSubstitute tenga los hijos de iID
    LinkNodes(iSubstitute, ELEFT , pNode->m_iChilds[ELEFT]);
    LinkNodes(iSubstitute, ERIGHT, pNode->m_iChilds[ERIGHT]);       

    // Copiamos otros datos
    pSubstitute->m_iBalance=pNode->m_iBalance;        
  }

  // Aqui siempre entramos con cambio
  iChange=1;

  m_slData.Delete(pNode->m_iDataID);
  m_slNodes.Delete(iID);


  // Subimos hacia arriba rebalanceando
  int iIterator;
  int iParent;
  for (iIterator=iStartRebalance ; iIterator!=-1 ; iIterator=iParent)
  {
    iHeightChange*=iChange;
    iParent=Parent(iIterator);

    if (iHeightChange)
    {
      ModifyBalance(iIterator, -iHeightChange);
    }

    if (iHeightChange)
    {
      iHeightChange=(iParent!=-1?(Left(iParent)==iIterator?-1:1):0);

      if (Balance(iIterator))
      {
        iChange=Rebalance(iIterator);
      }
      else
      {
        iChange=1;
      }
    }  
    else
    {
      iChange=0;
    }    

    if (iChange)
    {
      
    }
    else
    {
      // Hemos acabado
      break;
    }    
  }


    
  #if 0
  assert(AssertCorrectBalance(m_iTreeRoot));
  #endif
  
  return (0);
}

// Nombre      : 
// Parametros  : 
// Retorno     : 
// Descripcion : 
template <class MKey, class MValue>
int MAVLTree<MKey, MValue>::GetFromIDP(const int iID , MValue*& pValue)
{
  pValue=GetDataFromNode(iID);
  

  if (!pValue)
  {
    return (-1);
  }

  return (0);
}

// Nombre      : 
// Parametros  : 
// Retorno     : 
// Descripcion : 
template <class MKey, class MValue>
int MAVLTree<MKey, MValue>::GetFromID(const int   iID , MValue& Value)
{
  MValue* pValue;

  pValue=GetDataFromNode(iID);
  if (!pValue)
  {
    return (-1);
  }

  Value=*pValue;
  return (0);
}

// Nombre      : 
// Parametros  : 
// Retorno     : 
// Descripcion : 
template <class MKey, class MValue>
int MAVLTree<MKey, MValue>::GetFromKeyP(const MKey& kKey, MValue*& pValue)
{
  int iID=GetNodeIDFromKey(kKey);
  if (iID==-1)
  {
    return (-1);
  }

  pValue=GetDataFromNode(iID);

  return (0);
}

// Nombre      : 
// Parametros  : 
// Retorno     : 
// Descripcion : 
template <class MKey, class MValue>
int MAVLTree<MKey, MValue>::GetFromKey(const MKey& kKey, MValue& Value)
{
  MValue* pValue;

  if (GetFromKeyP(kKey, pValue)==0)
  {
    Value=*pValue;
    return(0);
  }

  return (-1);
}

// Nombre      : 
// Parametros  : 
// Retorno     : 
// Descripcion : 
template <class MKey, class MValue>
int MAVLTree<MKey, MValue>::Rotate(int iNodeID, const MAVLTree<MKey, MValue>::eDirection edDirection)
{
  eDirection edOtherDirection=1-edDirection;

  int iPivot        =iNodeID;
  int iNewPivot     =edOtherDirection==ELEFT?Left(iNodeID):Right(iNodeID);
  int iBalanceChange=Balance(iNewPivot)==0?0:1;
  
  // Primer paso. Hacemos que el padre de iPivot apunte a iNewPivot
  LinkNodes(Parent(iPivot), 
            Parent(iPivot)!=-1?
            (Left(Parent(iPivot))==iPivot?ELEFT:ERIGHT):
            -1,
            iNewPivot);

  // Hacemos que el hijo edOtherDirection de iPivot apunte al hijo edDirection the iNewPivot
  LinkNodes(iPivot, 
            edOtherDirection,
            Son(iNewPivot, edDirection));

  // Hacemos que iNewPivot tenga por edDirection a iPivot
  LinkNodes(iNewPivot,
            edDirection,
            iPivot);

  if (edDirection==ELEFT)
  {
    ModifyBalance(iPivot   , -1*(1+MAX(Balance(iNewPivot),0)));
    ModifyBalance(iNewPivot, -1*(1-MIN(Balance(iPivot),0)));
  }
  else
  {
    ModifyBalance(iPivot   , (1-MIN(Balance(iNewPivot),0)));
    ModifyBalance(iNewPivot, (1+MAX(Balance(iPivot),0)));
  }
  
  iNodeID=iNewPivot;
  
  return (iBalanceChange);
}

template <class MKey, class MValue>
int MAVLTree<MKey, MValue>::RotateTwice(int iNodeID, const eDirection edDirection)
{
  eDirection edOtherDirection=1-edDirection;

  int iOther=Son(iNodeID, edOtherDirection);
  Rotate(iOther, edOtherDirection);
  Rotate(iNodeID, edDirection);

  return (1);
}

// Nombre      : 
// Parametros  : 
// Retorno     : 
// Descripcion : 
template <class MKey, class MValue>
int MAVLTree<MKey, MValue>::Rebalance(int  iNodeID)
{
  int iHeightChange=0;
  bool bChange=false;

  if (Balance(iNodeID)<-1)
  {
    // Necesaria rotacion a la derecha
    bChange=true;

    if (Balance(Left(iNodeID))==1)
    {
      // Necesaria rotacion doble
      iHeightChange=RotateTwice(iNodeID, ERIGHT);
    }
    else
    {
      // Solo simple
      iHeightChange=Rotate(iNodeID, ERIGHT);
    }
  }
  else if (Balance(iNodeID)>1)
  {
    if (bChange)
    {
      assert(0);
    }
    // Necesaria rotacion a la izquierda
    if (Balance(Right(iNodeID))==-1)
    {
      // Necesaria rotacion doble
      iHeightChange=RotateTwice(iNodeID, ELEFT);
    }
    else
    {
      // Solo simple
      iHeightChange=Rotate(iNodeID, ELEFT);
    }    
  }
  return (iHeightChange);
}

// Nombre      : 
// Parametros  : 
// Retorno     : 
// Descripcion : 
template <class MKey, class MValue>
int MAVLTree<MKey, MValue>::InsertNode(const MKey& kKey, const MValue& Value)
{
  int iDataID;
  int iNodeID;

  if ((iDataID=m_slData.InsertTail(Value))==-1)
  {
    return (-1);
  }

  MAVLTreeNode avlNode;

  avlNode.m_kKey            =kKey;
  avlNode.m_iDataID         =iDataID;
  avlNode.m_iChilds[ELEFT]  =-1;
  avlNode.m_iChilds[ERIGHT] =-1;
  avlNode.m_iBalance        = 0;
  
  if ((iNodeID=m_slNodes.InsertTail(avlNode))==-1)
  {
    // Borramos dato previamente insertado
    m_slData.Delete(iDataID);
  }
  
  return (iNodeID);
}

template <class MKey, class MValue>
int MAVLTree<MKey, MValue>::GetIDFromKey(const MKey& kKey)  const
{
  return (GetNodeIDFromKey(kKey));
}

// Nombre      : 
// Parametros  : 
// Retorno     : 
// Descripcion : 
template <class MKey, class MValue>
int MAVLTree<MKey, MValue>::GetNodeIDFromKey(const MKey& kKey) const
{
  MAVLTreeNode* pNode;  
  int iIterator;
  
  iIterator=m_iTreeRoot;

  while (iIterator!=-1)
  {
    pNode=GetNode(iIterator);

    if (kKey==pNode->m_kKey)
    {
      return (iIterator);
    }

    iIterator=IsLowerOrder(kKey,pNode->m_kKey)?pNode->m_iChilds[ELEFT]:pNode->m_iChilds[ERIGHT];    
  }

  return (-1);
}


template <class MKey, class MValue>
int MAVLTree<MKey, MValue>::Parent(const int   iID) const
{ 
  MAVLTreeNode* pNode;  

  pNode=GetNode(iID);
  if (!pNode)
  {
    return (-1);
  }
  
  return (pNode->m_iParent);
}

// Nombre      : 
// Parametros  : 
// Retorno     : 
// Descripcion : 
template <class MKey, class MValue>
int MAVLTree<MKey, MValue>::Left(const int   iID) const
{
  MAVLTreeNode* pNode;  

  pNode=GetNode(iID);
  if (!pNode)
  {
    return (-1);
  }
  
  return (pNode->m_iChilds[ELEFT]);
}

// Nombre      : 
// Parametros  : 
// Retorno     : 
// Descripcion : 
template <class MKey, class MValue>
int MAVLTree<MKey, MValue>::Right(const int   iID) const
{
  MAVLTreeNode* pNode;  

  pNode=GetNode(iID);
  if (!pNode)
  {
    return (-1);
  }
  
  return (pNode->m_iChilds[ERIGHT]);
}

// Nombre      : 
// Parametros  : 
// Retorno     : 
// Descripcion : 
template <class MKey, class MValue>
int MAVLTree<MKey, MValue>::Root() const
{
  return (m_iTreeRoot);
}

template <class MKey, class MValue>
int MAVLTree<MKey, MValue>::LinkNodes(const int iParentID, const eDirection edDirection, const int iSonID)
{
  MAVLTreeNode* pParent;  
  MAVLTreeNode* pSon;  

  if (iParentID==-1) // Caso especial de raiz
  {    
    assert(edDirection==-1);
    m_iTreeRoot     =iSonID;

    if (iSonID!=-1)
    {
      pSon            =GetNode(iSonID);
      assert(pSon);
      pSon->m_iParent =iParentID;
    }    
  }
  else if (iSonID==-1)
  {
    assert(edDirection==ELEFT || edDirection==ERIGHT);    
    pParent=GetNode(iParentID);
    pParent->m_iChilds[edDirection]=iSonID;
  }
  else
  {
    assert(edDirection==ELEFT || edDirection==ERIGHT);
    pParent=GetNode(iParentID);
    pSon   =GetNode(iSonID);
    assert(pParent);
    assert(pSon);

    pParent->m_iChilds[edDirection]=iSonID;
    pSon->m_iParent=iParentID;
  }

  return (0);
}

template <class MKey, class MValue>
int MAVLTree<MKey, MValue>::Son(const int iID, const MAVLTree<MKey, MValue>::eDirection edDirection) const
{
  return (GetNode(iID)->m_iChilds[edDirection]);
}
    
template <class MKey, class MValue>
int MAVLTree<MKey, MValue>::Balance(int iID)
{
  if (iID!=-1)
  {
    return (GetNode(iID)->m_iBalance);
  }
  else
  {
    return (0);
  }
}
    
template <class MKey, class MValue>
int MAVLTree<MKey, MValue>::ModifyBalance(int iID, int iHowMany)
{
  return (GetNode(iID)->m_iBalance+=iHowMany);
}

template <class MKey, class MValue>
int MAVLTree<MKey, MValue>::AssertCorrectBalance(int iRoot)
{
  int iResult=1;

  if (iRoot==-1)
  {
    return (1);
  }

  if (Balance(iRoot)<-1 || Balance(iRoot)>1)
  {
    return (0);
  }



  if (Left(iRoot)!=-1)
  {
    iResult= iResult && AssertCorrectBalance(Left (iRoot));
  }

  if (Right(iRoot)!=-1)
  {
    iResult=iResult && AssertCorrectBalance(Right(iRoot));
  }
    

  return (1 && iResult);
}

template <class MKey, class MValue>
int MAVLTree<MKey, MValue>::GetNumberElements() const
{
  return (m_slNodes.GetNumberElements());
}
    
#undef  MAX
#undef  MIN


#endif