/**CFile****************************************************************

  FileName    [vecHsh.h]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [Resizable arrays.]

  Synopsis    [Hashing vector entries.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

  Date        [Ver. 1.0. Started - June 20, 2005.]

  Revision    [$Id: vecHsh.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $]

***********************************************************************/
 
#ifndef ABC__misc__vec__vecHsh_h
#define ABC__misc__vec__vecHsh_h


////////////////////////////////////////////////////////////////////////
///                          INCLUDES                                ///
////////////////////////////////////////////////////////////////////////

#include <stdio.h>

ABC_NAMESPACE_HEADER_START


////////////////////////////////////////////////////////////////////////
///                         PARAMETERS                               ///
////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////
///                         BASIC TYPES                              ///
////////////////////////////////////////////////////////////////////////

typedef struct Hsh_Int1Man_t_ Hsh_Int1Man_t;
struct Hsh_Int1Man_t_
{
    Vec_Int_t *  vData;       // stored data
    Vec_Int_t *  vNext;       // next items
    Vec_Int_t *  vTable;      // hash table
};



typedef struct Hsh_IntObj_t_ Hsh_IntObj_t;
struct Hsh_IntObj_t_
{
    int          iData;
    int          iNext;
};

typedef union Hsh_IntObjWord_t_ Hsh_IntObjWord_t;
union Hsh_IntObjWord_t_
{
    Hsh_IntObj_t wObj;
    word         wWord;
};

typedef struct Hsh_IntMan_t_ Hsh_IntMan_t;
struct Hsh_IntMan_t_
{
    int          nSize;       // data size
    Vec_Int_t *  vData;       // data storage
    Vec_Int_t *  vTable;      // hash table
    Vec_Wrd_t *  vObjs;       // hash objects
};



typedef struct Hsh_VecObj_t_ Hsh_VecObj_t;
struct Hsh_VecObj_t_
{
    int          nSize;
    int          iNext;
    int          pArray[0];
};

typedef struct Hsh_VecMan_t_ Hsh_VecMan_t;
struct Hsh_VecMan_t_
{
    Vec_Int_t *  vTable;      // hash table
    Vec_Int_t *  vData;       // data storage
    Vec_Int_t *  vMap;        // mapping entries into data;
    Vec_Int_t    vTemp;       // temporary array
    Vec_Int_t    vTemp1;      // temporary array
    Vec_Int_t    vTemp2;      // temporary array
};

////////////////////////////////////////////////////////////////////////
///                      MACRO DEFINITIONS                           ///
////////////////////////////////////////////////////////////////////////

static inline unsigned *      Hsh_IntData( Hsh_IntMan_t * p, int iData )  { return (unsigned *)Vec_IntEntryP( p->vData, p->nSize * iData );             }
static inline Hsh_IntObj_t *  Hsh_IntObj( Hsh_IntMan_t * p, int iObj )    { return iObj == -1 ? NULL : (Hsh_IntObj_t *)Vec_WrdEntryP( p->vObjs, iObj ); }
static inline word            Hsh_IntWord( int iData, int iNext )         { Hsh_IntObjWord_t Obj = { {iData, iNext} }; return Obj.wWord;                }

static inline Hsh_VecObj_t *  Hsh_VecObj( Hsh_VecMan_t * p, int i )  { return i == -1 ? NULL : (Hsh_VecObj_t *)Vec_IntEntryP(p->vData, Vec_IntEntry(p->vMap, i));  }

////////////////////////////////////////////////////////////////////////
///                     FUNCTION DEFINITIONS                         ///
////////////////////////////////////////////////////////////////////////

/**Function*************************************************************

  Synopsis    [Hashing data entries composed of nSize integers.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline Hsh_Int1Man_t * Hsh_Int1ManStart( int nEntries )
{
    Hsh_Int1Man_t * p;
    p = ABC_CALLOC( Hsh_Int1Man_t, 1 );
    p->vData  = Vec_IntAlloc( nEntries );
    p->vNext  = Vec_IntAlloc( nEntries );
    p->vTable = Vec_IntStartFull( Abc_PrimeCudd(nEntries) );
    return p;
}
static inline void Hsh_Int1ManStop( Hsh_Int1Man_t * p )
{
    Vec_IntFree( p->vData );
    Vec_IntFree( p->vNext );
    Vec_IntFree( p->vTable );
    ABC_FREE( p );
}
static inline int Hsh_Int1ManEntryNum( Hsh_Int1Man_t * p )
{
    return Vec_IntSize(p->vData);
}


/**Function*************************************************************

  Synopsis    [Hashing individual integers.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
// https://en.wikipedia.org/wiki/Jenkins_hash_function
static inline int Hsh_Int1ManHash( int Data, int nTableSize ) 
{
    unsigned i, hash = 0;
    unsigned char * pDataC = (unsigned char *)&Data;
    for ( i = 0; i < 4; i++ )
    {
        hash += pDataC[i];
        hash += hash << 10;
        hash ^= hash >> 6;
    }
    hash += hash << 3;
    hash ^= hash >> 11;
    hash += hash << 15;
    return (int)(hash % nTableSize);
}
static inline int * Hsh_Int1ManLookupInt( Hsh_Int1Man_t * p, int Data )
{
    int Item, * pPlace = Vec_IntEntryP( p->vTable, Hsh_Int1ManHash(Data, Vec_IntSize(p->vTable)) );
    for ( Item = *pPlace; Item >= 0; Item = Vec_IntEntry(p->vNext, Item) )
        if ( Vec_IntEntry(p->vData, Item) == (int)Data )
            return pPlace;
    assert( Item == -1 );
    return pPlace;
}
static inline int Hsh_Int1ManLookup( Hsh_Int1Man_t * p, int Data )
{
    return *Hsh_Int1ManLookupInt(p, Data);
}
static inline int Hsh_Int1ManAdd( Hsh_Int1Man_t * p, int Data )
{
    int i, Entry, * pPlace;
    if ( Vec_IntSize(p->vData) > Vec_IntSize(p->vTable) )
    {
        Vec_IntFill( p->vTable, Abc_PrimeCudd(2*Vec_IntSize(p->vTable)), -1 );
        Vec_IntForEachEntry( p->vData, Entry, i )
        {
            pPlace = Vec_IntEntryP( p->vTable, Hsh_Int1ManHash(Entry, Vec_IntSize(p->vTable)) );
            Vec_IntWriteEntry( p->vNext, i, *pPlace ); *pPlace = i;
        }
    }
    pPlace = Hsh_Int1ManLookupInt( p, Data );
    if ( *pPlace == -1 )
    {
        *pPlace = Vec_IntSize(p->vData);
        Vec_IntPush( p->vData, Data );
        Vec_IntPush( p->vNext, -1 );
    }
    return *pPlace;
}

/**Function*************************************************************

  Synopsis    [Test procedure.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline void Hsh_Int1ManHashArrayTest()
{
    Hsh_Int1Man_t * p = Hsh_Int1ManStart( 10 );

    printf( "Entry = %d.   Hashed = %d.\n", 5, Hsh_Int1ManAdd(p, 5 ) );
    printf( "Entry = %d.   Hashed = %d.\n", 7, Hsh_Int1ManAdd(p, 7 ) );
    printf( "Entry = %d.   Hashed = %d.\n", 7, Hsh_Int1ManAdd(p, 7 ) );
    printf( "Entry = %d.   Hashed = %d.\n", 7, Hsh_Int1ManAdd(p, 7 ) );
    printf( "Entry = %d.   Hashed = %d.\n", 8, Hsh_Int1ManAdd(p, 8 ) );
    printf( "Entry = %d.   Hashed = %d.\n", 5, Hsh_Int1ManAdd(p, 5 ) );
    printf( "Entry = %d.   Hashed = %d.\n", 3, Hsh_Int1ManAdd(p, 3 ) );
    printf( "Entry = %d.   Hashed = %d.\n", 4, Hsh_Int1ManAdd(p, 4 ) );

    printf( "Size = %d.\n", Hsh_Int1ManEntryNum(p) );

    Hsh_Int1ManStop( p );
}


/**Function*************************************************************

  Synopsis    [Hashing data entries composed of nSize integers.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline Hsh_IntMan_t * Hsh_IntManStart( Vec_Int_t * vData, int nSize, int nEntries )
{
    Hsh_IntMan_t * p;
    p = ABC_CALLOC( Hsh_IntMan_t, 1 );
    p->nSize  = nSize;
    p->vData  = vData;
    p->vTable = Vec_IntStartFull( Abc_PrimeCudd(nEntries) );
    p->vObjs  = Vec_WrdAlloc( nEntries );
    return p;
}
static inline void Hsh_IntManStop( Hsh_IntMan_t * p )
{
    Vec_IntFree( p->vTable );
    Vec_WrdFree( p->vObjs );
    ABC_FREE( p );
}
static inline int Hsh_IntManEntryNum( Hsh_IntMan_t * p )
{
    return Vec_WrdSize(p->vObjs);
}

/**Function*************************************************************

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
// https://en.wikipedia.org/wiki/Jenkins_hash_function
static inline int Hsh_IntManHash( unsigned * pData, int nSize, int nTableSize ) 
{
    int i = 0; unsigned hash = 0;
    unsigned char * pDataC = (unsigned char *)pData;
    nSize <<= 2;
    while ( i != nSize ) 
    {
        hash += pDataC[i++];
        hash += hash << 10;
        hash ^= hash >> 6;
    }
    hash += hash << 3;
    hash ^= hash >> 11;
    hash += hash << 15;
    return (int)(hash % nTableSize);
}
static inline int Hsh_IntManHash2( unsigned * pData, int nSize, int nTableSize )
{
    static int s_Primes[7] = { 4177, 5147, 5647, 6343, 7103, 7873, 8147 };
    unsigned char * pDataC = (unsigned char *)pData;
    int c, nChars = nSize * 4;
    unsigned Key = 0;
    for ( c = 0; c < nChars; c++ )
        Key += pDataC[c] * s_Primes[c % 7];
    return (int)(Key % nTableSize);
}
static inline int * Hsh_IntManLookup( Hsh_IntMan_t * p, unsigned * pData )
{
    Hsh_IntObj_t * pObj;
    int * pPlace = Vec_IntEntryP( p->vTable, Hsh_IntManHash(pData, p->nSize, Vec_IntSize(p->vTable)) );
    for ( ; (pObj = Hsh_IntObj(p, *pPlace)); pPlace = &pObj->iNext )
        if ( !memcmp( pData, Hsh_IntData(p, pObj->iData), sizeof(int) * (size_t)p->nSize ) )
            return pPlace;
    assert( *pPlace == -1 );
    return pPlace;
}
static inline int Hsh_IntManAdd( Hsh_IntMan_t * p, int iData )
{
    int i, * pPlace;
    if ( Vec_WrdSize(p->vObjs) > Vec_IntSize(p->vTable) )
    {
        Vec_IntFill( p->vTable, Abc_PrimeCudd(2*Vec_IntSize(p->vTable)), -1 );
        for ( i = 0; i < Vec_WrdSize(p->vObjs); i++ )
        {
            pPlace = Vec_IntEntryP( p->vTable, Hsh_IntManHash(Hsh_IntData(p, Hsh_IntObj(p, i)->iData), p->nSize, Vec_IntSize(p->vTable)) );
            Hsh_IntObj(p, i)->iNext = *pPlace;  *pPlace = i;
        }
    }
    pPlace = Hsh_IntManLookup( p, Hsh_IntData(p, iData) );
    if ( *pPlace == -1 )
    {
        *pPlace = Vec_WrdSize(p->vObjs);
        Vec_WrdPush( p->vObjs, Hsh_IntWord(iData, -1) );
        return Vec_WrdSize(p->vObjs) - 1;
    }
    return (word *)Hsh_IntObj(p, *pPlace) - Vec_WrdArray(p->vObjs);
}

/**Function*************************************************************

  Synopsis    [Hashes data by value.]

  Description [Array vData contains data entries, each of 'nSize' integers.
  The resulting array contains the indexes of unique data entries.]
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline Vec_Int_t * Hsh_IntManHashArray( Vec_Int_t * vData, int nSize )
{
    Hsh_IntMan_t * p;
    Vec_Int_t * vRes = Vec_IntAlloc( 100 );
    int i, nEntries = Vec_IntSize(vData) / nSize;
    assert( Vec_IntSize(vData) % nSize == 0 );
    p = Hsh_IntManStart( vData, nSize, nEntries );
    for ( i = 0; i < nEntries; i++ )
        Vec_IntPush( vRes, Hsh_IntManAdd(p, i) );
    Hsh_IntManStop( p );
    return vRes;
}
static inline Vec_Int_t * Hsh_WrdManHashArray( Vec_Wrd_t * vDataW, int nSize )
{
    Hsh_IntMan_t * p;
    Vec_Int_t Data = { 2*Vec_WrdCap(vDataW), 2*Vec_WrdSize(vDataW), (int *)Vec_WrdArray(vDataW) };
    Vec_Int_t * vData = &Data;
    Vec_Int_t * vRes = Vec_IntAlloc( 100 );
    int i, nEntries = Vec_IntSize(vData) / (2*nSize);
    assert( Vec_IntSize(vData) % (2*nSize) == 0 );
    p = Hsh_IntManStart( vData, (2*nSize), nEntries );
    for ( i = 0; i < nEntries; i++ )
        Vec_IntPush( vRes, Hsh_IntManAdd(p, i) );
    Hsh_IntManStop( p );
    return vRes;
}
static inline Hsh_IntMan_t * Hsh_WrdManHashArrayStart( Vec_Wrd_t * vDataW, int nSize )
{
    Hsh_IntMan_t * p;
    int i, nEntries = Vec_WrdSize(vDataW) / nSize;
    Vec_Int_t * vData = Vec_IntAlloc( 2*Vec_WrdSize(vDataW) );
    memcpy( Vec_IntArray(vData), Vec_WrdArray(vDataW), sizeof(word)*(size_t)Vec_WrdSize(vDataW) );
    vData->nSize = 2*Vec_WrdSize(vDataW);
/*
    for ( i = 0; i < 30; i++ )
    {
        extern void Extra_PrintHex( FILE * pFile, unsigned * pTruth, int nVars );
        Extra_PrintHex( stdout, (unsigned *) Vec_WrdEntryP(vDataW, i), 6 );  printf( "  " );
        Kit_DsdPrintFromTruth( (unsigned *) Vec_WrdEntryP(vDataW, i), 6 );   printf( "\n" );
    }
*/
    assert( Vec_IntSize(vData) % (2*nSize) == 0 );
    p = Hsh_IntManStart( vData, (2*nSize), nEntries );
    for ( i = 0; i < nEntries; i++ )
        Hsh_IntManAdd( p, i );
    assert( Vec_WrdSize(p->vObjs) == nEntries );
    return p;
}

/**Function*************************************************************

  Synopsis    [Test procedure.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline void Hsh_IntManHashArrayTest()
{
    Vec_Int_t * vData = Vec_IntAlloc( 10 );
    Vec_Int_t * vRes;
    Vec_IntPush( vData, 12 );
    Vec_IntPush( vData, 17 );
    Vec_IntPush( vData, 13 );
    Vec_IntPush( vData, 12 );
    Vec_IntPush( vData, 15 );
    Vec_IntPush( vData, 3 );
    Vec_IntPush( vData, 16 );
    Vec_IntPush( vData, 16 );
    Vec_IntPush( vData, 12 );
    Vec_IntPush( vData, 17 );
    Vec_IntPush( vData, 12 );
    Vec_IntPush( vData, 12 );

    vRes = Hsh_IntManHashArray( vData, 2 );

    Vec_IntPrint( vData );
    Vec_IntPrint( vRes );

    Vec_IntFree( vData );
    Vec_IntFree( vRes );
}





/**Function*************************************************************

  Synopsis    [Hashing integer arrays.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline Hsh_VecMan_t * Hsh_VecManStart( int nEntries )
{
    Hsh_VecMan_t * p;
    p = ABC_CALLOC( Hsh_VecMan_t, 1 );
    p->vTable = Vec_IntStartFull( Abc_PrimeCudd(nEntries) );
    p->vData  = Vec_IntAlloc( nEntries * 4 );
    p->vMap   = Vec_IntAlloc( nEntries );
    return p;
}
static inline void Hsh_VecManStop( Hsh_VecMan_t * p )
{
    Vec_IntFree( p->vTable );
    Vec_IntFree( p->vData );
    Vec_IntFree( p->vMap );
    ABC_FREE( p );
}
static inline int * Hsh_VecReadArray( Hsh_VecMan_t * p, int i )
{
    return (int*)Hsh_VecObj(p, i) + 2;
}
static inline Vec_Int_t * Hsh_VecReadEntry( Hsh_VecMan_t * p, int i )
{
    Hsh_VecObj_t * pObj = Hsh_VecObj( p, i );
    p->vTemp.nSize = p->vTemp.nCap = pObj->nSize;
    p->vTemp.pArray = (int*)pObj + 2;
    return &p->vTemp;
}
static inline Vec_Int_t * Hsh_VecReadEntry1( Hsh_VecMan_t * p, int i )
{
    Hsh_VecObj_t * pObj = Hsh_VecObj( p, i );
    p->vTemp1.nSize = p->vTemp1.nCap = pObj->nSize;
    p->vTemp1.pArray = (int*)pObj + 2;
    return &p->vTemp1;
}
static inline Vec_Int_t * Hsh_VecReadEntry2( Hsh_VecMan_t * p, int i )
{
    Hsh_VecObj_t * pObj = Hsh_VecObj( p, i );
    p->vTemp2.nSize = p->vTemp2.nCap = pObj->nSize;
    p->vTemp2.pArray = (int*)pObj + 2;
    return &p->vTemp2;
}
static inline int Hsh_VecSize( Hsh_VecMan_t * p )
{
    return Vec_IntSize(p->vMap);
}

/**Function*************************************************************

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline int Hsh_VecManHash( Vec_Int_t * vVec, int nTableSize )
{
    static unsigned s_Primes[7] = {4177, 5147, 5647, 6343, 7103, 7873, 8147};
    unsigned Key = 0;
    int i, Entry;
    Vec_IntForEachEntry( vVec, Entry, i )
        Key += (unsigned)Entry * s_Primes[i % 7];
    return (int)(Key % nTableSize);
}
static inline int Hsh_VecManAdd( Hsh_VecMan_t * p, Vec_Int_t * vVec )
{
    Hsh_VecObj_t * pObj;
    int i, Ent, * pPlace;
    if ( Vec_IntSize(p->vMap) > Vec_IntSize(p->vTable) )
    {
        Vec_IntFill( p->vTable, Abc_PrimeCudd(2*Vec_IntSize(p->vTable)), -1 );
        for ( i = 0; i < Vec_IntSize(p->vMap); i++ )
        {
            pPlace = Vec_IntEntryP( p->vTable, Hsh_VecManHash(Hsh_VecReadEntry(p, i), Vec_IntSize(p->vTable)) );
            Hsh_VecObj(p, i)->iNext = *pPlace; *pPlace = i;
        }
    }
    pPlace = Vec_IntEntryP( p->vTable, Hsh_VecManHash(vVec, Vec_IntSize(p->vTable)) );
    for ( ; (pObj = Hsh_VecObj(p, *pPlace)); pPlace = &pObj->iNext )
        if ( pObj->nSize == Vec_IntSize(vVec) && !memcmp( pObj->pArray, Vec_IntArray(vVec), sizeof(int) * (size_t)pObj->nSize ) )
            return *pPlace;
    *pPlace = Vec_IntSize(p->vMap);
    assert( Vec_IntSize(p->vData) % 2 == 0 );
    Vec_IntPush( p->vMap, Vec_IntSize(p->vData) );
    Vec_IntPush( p->vData, Vec_IntSize(vVec) );
    Vec_IntPush( p->vData, -1 );
    Vec_IntForEachEntry( vVec, Ent, i )
        Vec_IntPush( p->vData, Ent );
    if ( Vec_IntSize(vVec) & 1 )
        Vec_IntPush( p->vData, -1 );
    return Vec_IntSize(p->vMap) - 1;
}

/**Function*************************************************************

  Synopsis    [Test procedure.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline void Hsh_VecManHashTest()
{
    Hsh_VecMan_t * p;
    Vec_Int_t * vTemp;
    Vec_Int_t * vRes = Vec_IntAlloc( 1000 );
    int i;
    
    p = Hsh_VecManStart( 5 );
    for ( i = 0; i < 20; i++ )
    {
        vTemp = Vec_IntStartNatural( i );
        Vec_IntPush( vRes, Hsh_VecManAdd( p, vTemp ) );
        Vec_IntFree( vTemp );
    }
    for ( ; i > 0; i-- )
    {
        vTemp = Vec_IntStartNatural( i );
        Vec_IntPush( vRes, Hsh_VecManAdd( p, vTemp ) );
        Vec_IntFree( vTemp );
    }
    Vec_IntPrint( vRes );
    Vec_IntFree( vRes );

    Hsh_VecManStop( p );
}


////////////////////////////////////////////////////////////////////////
///                       END OF FILE                                ///
////////////////////////////////////////////////////////////////////////

ABC_NAMESPACE_HEADER_END

#endif

