#ifndef _SIMILARITY_H
#define _SIMILARITY_H

using namespace std;
typedef long MRESULT;

#define  SUCC_OK      0x00000000
#define  SUCC_FAIL    0x00000001
#define  SUCC_NOTIMPL 0x00000002

#define  SUCCESS(Status) ((MRESULT)(Status)>=0)
#define  FAIL(Status)   ((MRESULT)(Status)<0)

#define  ERR_UNEXPECTED          0x80000FFFL
#define  ERR_FAIL                0x80000000L
#define  ERR_INVALIDARG          0x80000001L
#define  ERR_NOPOINTER           0x80000002L
#define  ERR_OUTOFMEMORY         0x80000003L
#define  ERR_EMPTY               0x80000004L
#define  ERR_INCOMPATIBLE        0x80000005L
#define  ERR_FILE_OPEN           0x80000006L
#define  ERR_FILE_FORMAT         0x80000007L
#define  ERR_FILE_READ           0x80000008L
#define  ERR_FILE_WRITE          0x80000009L
#define  ERR_DATA_MISSING        0x8000000AL
#define  ERR_BAD_DATA            0x8000000BL
#define  ERR_BAD_OBJECT          0x8000000CL
#define  ERR_NOTIMPL             0x8000000DL


template<typename T>
class CSimilarity
{
 public:
    CSimilarity(){};
    virtual ~CSimilarity(){};
    virtual double operator()(const T& , const T&) const  = 0;
    virtual double calculate(const T& , const T&) const = 0;
 };

template<typename T>
class CSimMult : public CSimilarity<T>
{
 public:
    CSimMult(){};
    virtual double operator()(const T& x, const T& y) const { return calculate(x,y);}
    virtual double calculate(const T& x, const T& y) const { return static_cast<double>(x*y);};
 };

 template<typename T>
 class CSimDelta: public CSimilarity<T>
 {
public:
    CSimDelta(){};
    virtual ~CSimDelta(){};
    virtual double operator()(const T& x, const T& y) const { return calculate(x,y);}
    virtual double calculate(const T& x, const T& y) const { return x==y?1.:0.;};
 
 };

template<typename T>
class CSimGauss: public CSimilarity<T>
{
public:
   CSimGauss(const double sigma = 1.): m_sigma(sigma){};
    virtual ~CSimGauss(){};
    virtual double operator()(const T& x, const T& y) const { return calculate(x,y);}
    virtual double calculate(const T& x, const T& y) const 
    {
       double tmp = exp(-(x-y)*(x-y)*0.5/m_sigma/m_sigma);
       return tmp<DBL_EPSILON ? 0: tmp;
    }
private:
   double m_sigma;
 };

template<typename T, typename S>
class CSimPair : public CSimilarity< pair<T, S> >
{
public:
   CSimPair(void* sim1, void* sim2){m_pSim1 = (CSimilarity<T>*)sim1; m_pSim2 = (CSimilarity<S>*)sim2;}
    virtual ~CSimPair(){};
    virtual double operator()(const pair<T, S>& x, const pair<T, S>& y)const{ return calculate(x,y);}
    virtual double calculate(const pair<T, S>& x, const pair<T, S>& y) const
    { 
       
       return static_cast<double>(m_pSim1->calculate(x.first, y.first) * m_pSim2->calculate(x.second, y.second));
    }
private:
   CSimPair(){};
   CSimilarity<T>* m_pSim1;
   CSimilarity<S>* m_pSim2;
 };

typedef enum{ eSimType_Mult, eSimType_Delta, eSimType_Gauss} eSimType;

template<typename T, typename S = double>
class CSimilarityFactory
{
public:
   CSimilarityFactory(){}
   ~CSimilarityFactory(){}
   MRESULT Create(CSimilarity<T>** ppSim, const eSimType type, void* simParam = 0)
   {
      if(!ppSim) return ERR_INVALIDARG;
      CSimilarity<T>* pSim;
      switch(type){
         case eSimType_Mult: 
            pSim = new CSimMult<T>();
            break;
         case eSimType_Delta:
            pSim = new CSimDelta<T>();
            break;
         case eSimType_Gauss:
         {
            double sigma = simParam ? *(double*) simParam : 1.;
            pSim = new CSimGauss<T>(sigma);
            break;
         }
         default:
            return ERR_INVALIDARG;
            break;
      }
      if(!pSim) return ERR_OUTOFMEMORY;
      *ppSim = pSim;
      return SUCC_OK;
   }
   MRESULT Destroy(CSimilarity<T>* pSim)
   {
      if(!pSim) return ERR_INVALIDARG;
      delete pSim;
      pSim = 0;
   }
};
static CSimilarityFactory<double> doubleSimFactory;
static CSimilarityFactory<unsigned char> ucharSimFactory;
static CSimilarityFactory<char> charSimFactory;

#endif