/**
 * Non-metric Space Library
 *
 * Authors: Bilegsaikhan Naidan (https://github.com/bileg), Leonid Boytsov (http://boytsov.info).
 * With contributions from Lawrence Cayton (http://lcayton.com/) and others.
 *
 * For the complete list of contributors and further details see:
 * https://github.com/searchivarius/NonMetricSpaceLib 
 * 
 * Copyright (c) 2014
 *
 * This code is released under the
 * Apache License Version 2.0 http://www.apache.org/licenses/.
 *
 */

#ifndef _PROJECTION_H_
#define _PROJECTION_H_

#include "distcomp.h"
#include "space.h"
#include "object.h"

#include <string>
#include <cstddef>

// Classic random projections using random orthonormal vectors
#define PROJ_TYPE_RAND            "rand"          
// Distance to random reference points
#define PROJ_TYPE_RAND_REF_POINT  "randrefpt"     
// FastMap (project on lines defined by two randomly selected points)
#define PROJ_TYPE_FAST_MAP        "fastmap"       
// Integer value permutations
#define PROJ_TYPE_PERM            "perm"
// Integer value truncted permutations
#define PROJ_TYPE_PERM_TRUNC      "permtrunc"
// Binarized permutations: note that the result is nevertheless saved as floating-point vector
#define PROJ_TYPE_PERM_BIN        "permbin"
// Dense vectors remain unchanged, sparsed vectors are "hashed" into dense ones
#define PROJ_TYPE_VECTOR_DENSE    "densevect"
// Trivial projection
#define PROJ_TYPE_NONE            "none"

namespace similarity {

template <class dist_t>
class Projection {
public:
  /*
   * Create a projection helper class that inherits from the current one.
   */
  static Projection* createProjection(const Space<dist_t>& space,
                        const ObjectVector& data,
                        std::string projType,
                        size_t nProjDim,
                          /*
                             For sparse vector spaces and random projections
                             is used to create an intermediate dense vector representations.
                             It can be set to zero (or otherwise) if
                              1) The source space is a dense vector space
                              2) A projection is of the following type:
                                i)   FastMap;
                                ii)  Random reference points;
                                iii) Permutations.
                        */
                        size_t nDstDim,
                        unsigned binThreshold);
  /*
   * A function to create a projection. It should be implemented in child classes.
   * Note that the following:
   *
   * 1) we only support projection to single-precision floating point vectors.
   * 2) Space objects can be used to compute distances only during indexing time.
   *    At search time, all distance computations are proxied through a query object.
   *    During the indexing time, the query parameter can be NULL, but during
   *    search time, one needs to supply the actual query.
   *
   * TODO: @leo This is actually not such a good approach, we need a separate
   *       object to proxy distance computations. It should not be the query!
   *       Then, functions like compProj will not need to use both the pObj (at
   *       index time) and the pQuery (at search time).
   *
   */
  virtual void  compProj(const Query<dist_t>* pQuery,
                         const Object* pObj,
                         float* pDstVect) const = 0;

protected:
  static dist_t DistanceObjLeft(const Space<dist_t>& space,
                                const Query<dist_t>* pQuery,
                                const Object* pRefObj, // reference object
                                const Object* pObj // the object to project
                                ) {
    /*
     * Object (in this case pivot) is the left argument.
     * At search time, the right argument of the distance will be the query point
     * and pivot again is the left argument.
     */
    return NULL == pQuery ?
        space.IndexTimeDistance(pRefObj, pObj)
        :
        pQuery->DistanceObjLeft(pRefObj);
  }
  static void fillIntermBuffer(const  Space<dist_t>& space,
                               const  Object* pObj,
                               size_t nIntermDim,
                               vector<dist_t>& intermBuffer) {

    /*
     *  For dense vector spaces CreateDenseVectFromObj does nothing useful
     *  (and intermDim_ == srcDim_),
     *  however, we introduced this function to have the
     *  uniform interface for sparse and dense vector spaces.
     */
    space.CreateDenseVectFromObj(pObj, &intermBuffer[0], nIntermDim);
  }
};

}


#endif
