#include <vector>
#include <iostream>
#ifndef __WeightedAttribute_H
#define __Weightedattribute_H


template < class AttributeT, class Func>
/*
CLASS
  WeightedAttribute
  
  This template class that inherits the ParticleAttribute class enhance it in 
  adding a specific weight to each attribute, and a preference that allows
  you to compute a scoring of several on a preferential bases. 
  
  
KEYWORDS
  particle, attribute, weight, preference, vector

AUTHORS
  Zipi Fligelman (zipo@math.tau.ac.il)

  Copyright: SAMBA group, Tel-Aviv Univ. Israel 1998

CHANGES LOG
<UL>
<LI>
</UL>

GOALS

  This class should be used when you want to weight several attribute on 
  preference bases. It has an applying operator() the same as particle 
  attribute but it returns a weighted measure i.e. the measure function
  applied on both attributes times the weight. Please notice if you invoke 
  this class with no information it will receive the max preference (100).
  The default weighting is 1.0 of course. You can apply the fmeasure function
  inherited from ParticleAttribute, in many ways, please note the different
  configuration of the operator().
  Also one can work on a vector of attributes or two vectors of attribute
  and get a weighted information from them. One function measure all the 
  attribute couples in a given vector. One function measures each attribute in 
  the vector against its right neighbour (the beginning of the vector being 
  the right neighbour of the end of the vector). One function measure the
  attributes vector vs. vector according to their place until the end of
  the shorter of the two.
 
USAGE 
  Here is an Example that will demonstrate the usage on the Euclidean Case
  (see also documentation of particle attribute)
  File: "Euclidean2.h"
  EXAMPLE
  #include <Vector3.h>
  #include <math.h>

  class VectorDist {
  public:
     float operator()(const Vector3& vec1, const Vector3& vec2) {
     return vec1.dist(vec2);
  };
  
  class VectorProd {
  public:   
     float operator()(const Vector3& vec1, const Vector3& vec2) {
     return vec1*vec2;
  }; 
  
  class AngleBetween {
  public: 
     float operator()(const Vector3& vec1, const Vector3& vec2) {
     return (vec1^vec2);
  };
  END
  
  Now for invoking it you should do something like:
  
  EXAMPLE
  #include "WeightedAttribute.h"
  #include "Euclidean.h"
  #include "SurfacePoint.h"
  #include "macros.h"
  #include <vector>
  
  struct MatchValue {
  END

  here we make the difference in place twice the impact the 
  normals difference but if the normal are more then pi from 
  each other we return a negative weight

  EXAMPLE
  WeightedAttribute<Vector3, VecDistance> place_diff(5,2);
  WeightedAttribute<Vector3, VecProduct> normal_diff(6);

   float operator()(const SurfacePoint& pnt1, const SurfacePoint& pnt2)
   {
      if (normal_diff(pnt1.nrom(), pnt2.norm()) < 0)
          return -1;
      return (place_diff(pnt1.place(), pnt2.place())
              +normal_diff(pnt1.nrom(), pnt2.norm());
   }
  };
  END
  
  Now assume we have a class called Hydro that given two types of residues
  return +1 if they are both hydrophobic -1 if they are both hydrophilic
  and 0 if they are neither. 
  We can define a general weighting on a match list :
  
  EXAMPLE:
  #include <vector>
  #include <RigidTrans3.h>
  
  WeightedAttribute<SurfacePoint, MatchValue> match_euclid(5,6);
  WeightedAttribute<SurfacePoint, Hydro> match_hydrophobicity(4, 2);
  float calculate_fit(const vector<SurfaceParticle>& list1, 
                      const vector<SurfaceParticle>& list2)
  {
    return match_euclid.apply_sum_weights(list, list2)
          +match_hydrophobicity(list,list2);
  }
  END
*/
class WeightedAttribute : 
  public ParticleAttribute< AttributeT,Func> 
{

  const float MAX_PREF=100.0;

public:

  // GROUP: Constructors
  
  //// Empty Constructor
  WeightedAttribute();
  
  //// Constructor: Empty in the ParticleAttribute constructor, preference
  // as requested and default weight is 1.0
  WeightedAttribute(float pref);
  
  //// Constructor: Empty in the ParticleAttribute constructor, preference
  // as requested and weight as requested
  WeightedAttribute(float pref, float weight);
  
  //// Weight=1.0 Preference=MAX ParticleAttribute built on parameters
  WeightedAttribute(const AttributeT& att, const Func& func);
  
  //// Same as above on requested preference 
  WeightedAttribute(const AttributeT& att, const Func& func, float pref);
  
  //// Same as above with also requested weight
  WeightedAttribute(const AttributeT& att, const Func& func, float pref,
                    float wght);
  
  //// Constructor with Particle Attribute copy constructor
  //  and float preference default weight 1.0
  WeightedAttribute(const ParticleAttribute< AttributeT,Func>& new_att, float pref);
  
  //// Constructor with Particle Attribute copy constructor
  // and float preference and a given weight
  WeightedAttribute(const ParticleAttribute< AttributeT,Func>& new_att, 
                    float pref, float wght);
  ////Copy Constructor
  WeightedAttribute(const WeightedAttribute& watt);
   
  // GROUP: Information Retrieving
  
  //// Returning the preference
  float Pref() const {return preference;}
  
  //// Returning the associated weight
  float Weight() const {return weight;}
  
  // GROUP: Operators
  
  //// Applying the weighted attribute measure against ourselves, meaning:
  // Func(our_att,att)*weight 
  float operator()(const AttributeT& attrib);
  
  ////Applying the weighted attribute measure against ourselves
  // with preference as a normalizing constant 
  // Func(our_att, att)*weight*(preference/norm_pref)
  float operator()(const AttributeT& attrib, float norm_pref);
  
  //// Applying the weighted attribute measure, meaning: 
  //  Func(att1, att2)*weight
  float operator()(const AttributeT& att1, const AttributeT& att2);
  
  //// Applying the weighted attribute measure with preference
  // as a normalizing constant, meaning:
  //  Func(att1, att2)*weight*(preference/nor_pref)
  float operator()(const AttributeT& att1,
                   const AttributeT& att2, float norm_pref);
  
  //// Sorting only according to the preference 
  bool operator< (const WeightedAttribute& prt) const;
  
  //// Sorting only according to the preference 
  bool operator>(const WeightedAttribute& prt) const;
     
 
  // GROUP: apply functions
  
  //// in this function we do the following 
  // weight all the attributes in the list one against the other using the 
  // fun held in weighted attribute. the cumulative function is summation
  // might want to borden this where the cumulative function is something else
  float apply_all_sum_weighted(const vector< AttributeT>& list); 
  
  //// The same only each attribute is compared with its consecutive neighbour 
  // meaning : (att1, att2) + ...+ (att_i,att_i+1) + ... + (att_n, att1)
  // the notice about the cumulative function is the same as before
  float apply_circ_sum_weighted(const vector< AttributeT>& list);
  
  //// the same as above only operating on two lists assuming their length 
  // is the same  comparing every time list1(i-th element) with 
  // list2(i-th element)
  float apply_sum_weights(const vector< AttributeT>& list1, const vector< AttributeT>& list2);

  
private:
  float preference;
  float weight;
};


template < class AttributeT, class Func>
WeightedAttribute< class AttributeT, class Func>::WeightedAttribute()
  : ParticleAttribute< AttributeT,Func>(), preference(MAX_PREF), weight(1.0)
{}

template< class AttributeT, class Func>
WeightedAttribute< class AttributeT,class Func>::WeightedAttribute(const AttributeT& att, const Func& func) 
  : ParticleAttribute< AttributeT,Func>(att, func), preference(0.0), weight(0.0)
{}

template< class AttributeT, class Func>
WeightedAttribute< class AttributeT,class Func>::WeightedAttribute(float pref) 
  : ParticleAttribute< AttributeT,Func>(), preference(pref), weight(1.0)
{}
template< class AttributeT, class Func>
WeightedAttribute< class AttributeT,class Func>::WeightedAttribute(float pref,
float wght) 
  : ParticleAttribute< AttributeT,Func>(), preference(pref), weight(wght)
{}

template< class AttributeT, class Func>
WeightedAttribute< class AttributeT,class Func>::WeightedAttribute(const AttributeT& att, const Func& func, float pref) 
  : ParticleAttribute< AttributeT,Func>(att, func), preference(pref), weight(1.0)
{}

template< class AttributeT, class Func>
WeightedAttribute< class AttributeT,class Func>::WeightedAttribute(const AttributeT& att, const Func& func, float pref, float wght) 
  : ParticleAttribute< AttributeT,Func>(att, func), preference(pref), 
    weight(wght)
{}

template < class AttributeT, class Func>
WeightedAttribute< class AttributeT,class Func>::WeightedAttribute(const ParticleAttribute< AttributeT,Func>& new_att, float pref)
  :  ParticleAttribute< AttributeT,Func>(new_att), preference(pref), weight(1.0)
{}

template < class AttributeT, class Func>
WeightedAttribute< AttributeT,Func>::WeightedAttribute(const ParticleAttribute< AttributeT,Func>& new_att, float pref, float wght)
  : ParticleAttribute< AttributeT,Func>(new_att),  preference(pref), weight(wght)
{}

template< class AttributeT, class Func>
WeightedAttribute< AttributeT,Func>::WeightedAttribute(const WeightedAttribute& watt): ParticleAttribute< AttributeT,Func>(watt.attribute(),watt.measure()), preference(watt.Pref()), weight(watt.Weight())
{} 

template < class AttributeT, class Func>
float WeightedAttribute< AttributeT,Func>::operator()(const AttributeT& attrib)
{
  return (ParticleAttribute< AttributeT,Func>::operator()(attribute(), attrib)*weight);
}

template< class AttributeT, class Func>
float WeightedAttribute< class AttributeT, class Func>::operator()(const AttributeT& attrib, float norm_pref)
{
  return (ParticleAttribute< AttributeT,Func>::operator()(att.attribute(), attrib)*weight*(preference/norm_pref)); 
}

template< class AttributeT, class Func>
float WeightedAttribute<class AttributeT, class Func>::operator()(const AttributeT& att1, const AttributeT& att2)
{
  return (ParticleAttribute< AttributeT,Func>::operator()(att1,att2)*weight);
}
 
template< class AttributeT, class Func>
float WeightedAttribute< class AttributeT, class Func>::operator()(const AttributeT& att1, const AttributeT& att2,
                                                                  float norm_pref)
{
  return (ParticleAttribute< AttributeT,Func>::operator()(att1,att2)*weight*(preference/norm_pref));
} 

template < class AttributeT, class Func>
bool WeightedAttribute< class AttributeT,class Func>::operator<(const WeightedAttribute& prt) const 
{
  return (preference < prt.Perf()); 

}

template < class AttributeT, class Func>
bool WeightedAttribute< class AttributeT,class Func>::operator>(const WeightedAttribute& prt) const 
{
  return (preference > prt.Pref); 
}


template < class AttributeT,class Func>
float WeightedAttribute< class AttributeT,class Func>::apply_all_sum_weighted(const vector< AttributeT>& list)
{
  float sum_of_weights=0.0;
  for (vector< AttributeT>::const_iterator it=list.begin(); it!=list.end(); ++it)
    for (vector< AttributeT>::const_iterator jt=list.begin()+1; jt!=list.end(); ++jt)
      {
        sum_of_weights+=ParticleAttribute< AttributeT,Func>::operator()(*it,*jt);
      }
  return (sum_of_weights*weight);
}

template < class AttributeT, class Func>
float WeightedAttribute< class AttributeT, class Func>::apply_circ_sum_weighted(const vector< AttributeT>&  list)
{
  float sum_of_weights=0.0;
  vector< AttributeT>::const_iterator it;
  for(it=list.begin(); it!=list.end()-1; ++it)
    sum_of_weights+=ParticleAttribute< AttributeT,Func>::operator()(*it, *(it+1));
  // when out of the for loop it=list.end()
  sum_of_weights+=ParticleAttribute< AttributeT,Func>::operator()(*(it-1),*(list.begin()));
  return (sum_of_weights*weight);
}

template < class AttributeT, class Func>
float WeightedAttribute< class AttributeT, class Func>::apply_sum_weights(const vector< AttributeT>& list1, 
                                                                         const vector< AttributeT>& list2)
{
  float sum_of_weights=0.0;
  vector< AttributeT>::const_iterator it1,it2;
  for(it1=list1.begin(),it2=list2.begin(); it1!=list1.end() || it2!=list2.end(); ++it1, ++it2)
    {
      sum_of_weights+=ParticleAttribute< AttributeT,Func>::operator()(*it1, *it2);
    }
  return (sum_of_weights*weight);
}


#endif











