/* -*- mode: c++ -*- 
*/
/* 

    GIFT, a flexible content based image retrieval system.
    Copyright (C) 1998, 1999, 2000 CUI, University of Geneva

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

*/
#ifndef _CPSETOFPSETS
#define _CPSETOFPSETS

#include <list>
#include <vector>
#include "CProbabilisticSet.h"
#include "CSelfDestroyPointer.h"
#include "helpers.h"

/**@Doc 
    In order to judge the consistency of comparisons performed
    by the user we have to keep for each comparison (or each group of
    comparisons, as remains to be tested) a set of elements with their
    degree of conistency with the comparison. This part (for a single set)
    is fulfilled by the CProbabilistic Set.
    
    In this structure we keep now a set of such sets, 
    along with functions which
    judge their consistency. 

    In fact this is a set of probabilistic sets together with
    helper functions for hunting pictures.

    todo:
    We can add a set of PSets to the set (these are the different comparisons)
    out of this set is calculated a list of iterator-lists
    
*/
template<class T>
class CPSetOfPSets{
  
  /*@name Type definitions */
  //@{
  ///An element of the content of this set
  typedef CProbabilisticSet<T> CSet;
  ///An element of the content of this set
  typedef pair<double,CSet* > CContentElement;
  ///The content of this set
  typedef vector<CContentElement> CContent;
  ///
  typedef CSet::CSequence CIDSequence;
  
  /** We give the user the possibility to give assumptions.
      An assumption is a set of contentelements.
      
      is, that each of the following sets  might be
      added to this.
      
      We will provide functions to associate a value to an assumption,
      making it possible to pick the best one

  */
  typedef CContentElement CAssumptionElement;

  typedef vector<CAssumptionElement> CAssumption;
  //@}
  
  ///
  CContent mContent;
  ///
  CAssumption* mAssumption;
  /// In order to be able to give back a set
  /// with the same properties as all other elements
  /// in this set of set.
  CSelfDestroyPointer<CSet> mSetPrototype;
  ///The current best guess of this structure.
  CSelfDestroyPointer<CSet> mCurrentBest;
  ///
  int mMaximumSize;
  /// 
  int mForgetAfterNthInconsistency;
  ///This is needed for some calculations
  int mNumberOfImages;
public:
  ///
  CPSetOfPSets(int inMaximumSize=10);
  ///
  ~CPSetOfPSets();
  ///
  CPSetOfPSets(int inMaximumSize,
	       CSet* inSetPrototype);
  ///
  void addElement(const CContentElement& inSet);
  ///
  double evaluateAssumption(CAssumption* inAssumption);
  ///
  double updateWeights(CContent& inContent)const;
  ///
  double evaluateContent(const CContent& inContent,
			 bool inWithOutput=false,
			 string inOutputName="when_adding")const;
  ///
  const T* getCurrentBest()const;
  ///
  int getCurrentBestSize()const;
  ///
  CIDSequence* drawSequenceFromCurrentBest(long inSize)const;
  ///
  CSet* drawSetFromCurrentBest(long inSize)const;
  ///
  void setPrototype(CSet* inPrototype);
  ///
  CSet* getPrototypeClone()const;
  ///
  double combine(CContentElement& inoutChanged,
		 const CContentElement& inChanger);
  ///reducing the size, if there are too many sets stored
  void reduceToTheMax(CContent& inContent);
  ///
  void setMaximumSize(int inMaximumSize);
  ///
  void setForgetAfterNthInconsistency (int inN);
  ///
  int size()const;
  ///
  void clear();
  ///
  void setNumberOfImages(const int& inNumberOfImages);
  ///
  int getNumberOfImages()const;
};

///
template<class T>
void CPSetOfPSets<T>::setMaximumSize(int inMaximumSize){
  mMaximumSize=inMaximumSize;
  reduceToTheMax(mContent);
};
///
template<class T>
int CPSetOfPSets<T>::getCurrentBestSize()const{
  return mCurrentBest->size();
};
///
template<class T>
const T* CPSetOfPSets<T>::getCurrentBest()const{
  return mCurrentBest;
};
///
template<class T>
int CPSetOfPSets<T>::size()const{
  return mContent.size();
};
///
template<class T>
void CPSetOfPSets<T>::clear(){
  mContent.clear();
  mCurrentBest=0;
};

///
template<class T>
CPSetOfPSets<T>::CIDSequence* CPSetOfPSets<T>::drawSequenceFromCurrentBest(long inSize)const{

  cout << "EDO-1" << endl;

  if(mCurrentBest){
    CIDSequence* l=mCurrentBest->drawSequence(inSize);
    return l;
  }
  else
    return 0;
};

///
template<class T>
CPSetOfPSets<T>::CSet* CPSetOfPSets<T>::drawSetFromCurrentBest(long inSize)const{

  cout << "EDO-1" << endl;

  if(mCurrentBest)
    return mCurrentBest->drawSet(inSize);
  else
    return 0;
};

template<class T>
CPSetOfPSets<T>::~CPSetOfPSets(){
  
};

template<class T>
CPSetOfPSets<T>::CPSetOfPSets(int inMaximumSize):
  mAssumption(0),
  mSetPrototype(new CSet()),
  mCurrentBest(),
  mMaximumSize(inMaximumSize)
{
};

template<class T>
CPSetOfPSets<T>::CPSetOfPSets(int inMaximumSize,
			      CSet* inSetPrototype):
  mAssumption(0),
  mSetPrototype(inSetPrototype),
  mMaximumSize(inMaximumSize)
{
  
};

template<class T>
void CPSetOfPSets<T>::setPrototype(CSet* inPrototype){
  mSetPrototype=inPrototype;
}

template<class T>
CPSetOfPSets<T>::CSet* CPSetOfPSets<T>::getPrototypeClone()const{
  return mSetPrototype ? mSetPrototype->clone():0;
}

template<class T>
double CPSetOfPSets<T>::combine(CContentElement& inoutChanged,
				const CContentElement& inChanger){
  if(inChanger.first>0.5){
    return inoutChanged.second->intersect(*inChanger.second);
  }else{
    return 0;
  }
}

template<class T>
void CPSetOfPSets<T>::addElement(const CContentElement& inSet){
  cout << "BEFADD:" 
       << mContent.end()-mContent.begin() 
       << endl;

  cout << "A" << flush;

  mContent.push_back(inSet);
  cout << "BEFADD2:" 
       << mContent.end()-mContent.begin() 
       << endl;
  {
    CProbabilisticSet<int>::CElementVector lDiagnosis;
    mContent.back().second->getContent(lDiagnosis);
    show_ProbabilisticSetElements(lDiagnosis.begin(),
				  lDiagnosis.end(),
				  "Added");
  }

  cout << "B" << flush;


  if(mContent.size()>1){

    updateWeights(mContent);
    evaluateContent(mContent,
		    true,
		    "NewState");
    mCurrentBest=mContent.back().second;
    mContent.pop_back();
    cout << endl
	 << "---Checking Consistency---" 
	 << endl;
    
    
    //pruning: How to get rid of too old elements:
    //combine them to one element
    if(mContent.end()-mContent.begin()>mMaximumSize){


      cout << "+++++flattening+++++" 
	   << endl;

      // Either combine the knowledge of the element 
      // which has to be compressed with existing 
      // older and presently believed knowledge

      reduceToTheMax(mContent);
      
    }
  }else{
    cout << "TAKING" 
	 << endl;
    mCurrentBest=mContent.back().second->clone();
  }
  

  cout << endl
       << "--------------------a new best choice found, size: " 
       << mCurrentBest->size()
       << "Total size"
       << size()
       << endl;
  
  cout << "AFTADD: Number of Distributions: " 
       << mContent.end()-mContent.begin() 
       << ", Number of elements in current back/best distribution:"
       << mContent.back().second->size()
       << "/"
       << mCurrentBest->size()
       << endl;

};

template<class T>
double CPSetOfPSets<T>::updateWeights(CContent& inContent)const{
  assert(inContent.size());

  if(inContent.size()==1){
    inContent.front().first=1.0;
    return 1;
  }

  CContent::iterator lLast=inContent.end();
  lLast--;

  CSelfDestroyPointer<CSet> lLastIntersection=
    inContent.back().second->clone();
  CSet* lCurrentIntersection=inContent.back().second->clone();
  
  cout << "UPDATE" << endl;
  
  double lProbabilityThatFeedback=1;
  if(mCurrentBest && inContent.size()){
    CSelfDestroyPointer<CSet> lCurrentBest(mCurrentBest->clone());
    cout << "HERE" 
	 << lCurrentIntersection->size() 
	 <<endl;
    lProbabilityThatFeedback=lCurrentBest->intersect(*lCurrentIntersection)
      /(lCurrentBest->countAboveThreshold()+1);
  }
  
  // the probability that the user makes this sequence 
  // of feedbacks
  double lCurrentProbability=1;
  double lLastProbability=1;
  
  
  int lZeroCounter=0;
  if(inContent.size()>1){

    CContent::iterator i=lLast;
    while(i!=inContent.begin() 
	  && (lZeroCounter<mForgetAfterNthInconsistency)){
      
      cout << "X" << flush;

      i--;

      cout << "Distance to the beginning:"
	   << i-inContent.begin()
	   << ","
	   << i->first
	   << ","
	   << int(i->second)
	   << endl
	   << flush;


      lCurrentProbability *= lCurrentIntersection->intersect(*(i->second));

      cout << "here" 
	   << endl;
      
#ifndef CLEAN
      if(lCurrentProbability!=0 && !lZeroCounter){
#endif //CLEAN
	i->first=1;
	
	cout << "if1" << endl;
	lLastProbability=lCurrentProbability;
	lLastIntersection=lCurrentIntersection->clone();
	cout << "if1b" << endl;
#ifndef CLEAN
      }else{

	lZeroCounter++;

	//inconsistency detected
	i->first=0;
	
	assert(lLastIntersection);
	{
	  lCurrentProbability=lLastProbability;
	  
	  delete lCurrentIntersection;
	  lCurrentIntersection=lLastIntersection->clone();

	  cout << "###DETECTED INCONSISTENCY###" << endl;
	}
	cout << "if2" << endl;
      }
#endif //CLEAN
    }
    while(i!=inContent.begin()){
      (--i)->first=0;
      cout << "###forgetting###"
	   << endl;
    }
  }

  inContent.push_back(make_pair(lProbabilityThatFeedback/mNumberOfImages,
				lCurrentIntersection));
/* #else */
/*   lCurrentIntersection->intersect(*inContent.front().second); */
/*   inContent.push_back(make_pair(lProbabilityThatFeedback/mNumberofImages, */
/* 				lCurrentIntersection)); */
/* #endif */
  return 1+lProbabilityThatFeedback/mNumberOfImages;
}

template<class T>
double CPSetOfPSets<T>::evaluateContent(const CContent& inContent,
					bool inWithOutput,
					string inOutputName)const{
  double lReturnValue=0;//a measure for the quality of the inContent
  for(CContent::const_iterator i=inContent.begin();
      i!=inContent.end();
      i++){
    //cout << ":" << i->first << endl;
    assert((i->first>=0)&&(i->first<=1));
    lReturnValue+=i->first;
  }

  if(lReturnValue>0){
    lReturnValue = 
      inContent.back().second
      ->calculateInformation()//too much information is bad
      *
      sqrt(lReturnValue);//too much size is bad
  }
  {
    CProbabilisticSet<int>::CElementVector lDiagnosis;
    inContent.back().second->getContent(lDiagnosis);
    if(inWithOutput){
      show_ProbabilisticSetElements(lDiagnosis.begin(),
				    lDiagnosis.end(),
				    inOutputName);
    }
  }

  return lReturnValue ;
}

template<class T>
double CPSetOfPSets<T>::evaluateAssumption(CAssumption* inAssumption){
  mAssumption 
    = 
    inAssumption;
  
  double lReturnValue=0;

  if(mAssumption->size()){
    //allocate a vector
    CContent lContentPlusAssumption(mContent.begin(),
				    mContent.end());

    // add each element of the assumption to the vector
    // which we have just built up. Evaluate its
    // goodness and then make a weighted average of this
    for(CAssumption::const_iterator i=inAssumption->begin();
	i!=mAssumption->end();
	i++){
      cout << "bis hier" 
	   << flush;
      CSet* lCurrentAssumption=i->second->clone();
      lContentPlusAssumption.push_back(make_pair(1,
						 lCurrentAssumption));
      
      double lProbability=updateWeights(lContentPlusAssumption);
      double lAssumptionValue=evaluateContent(lContentPlusAssumption);

      lReturnValue += 
	//a-priory weight given by the user (will maybe stay unused)
	i->first 
	//probability that this element of the assumption 
	//is taken by the user
	* lProbability 
	//value of this assumptionElement
	* lAssumptionValue 
	;

      cout << "Probability :" 
	   << lProbability
	   << endl;


      //very dirty
      delete lContentPlusAssumption.back().second;
      lContentPlusAssumption.pop_back();
      delete lContentPlusAssumption.back().second;
      lContentPlusAssumption.pop_back();

    }
    /*if(lContentPlusAssumption.size())
      for(CContent::iterator i = lContentPlusAssumption.begin();
      i != lContentPlusAssumption.end();
      i++){
      cout << "deleteing" << i->second << endl << flush;
      delete (i->second);
      }*/
  }


  return lReturnValue;
}

template<class T>
void CPSetOfPSets<T>::reduceToTheMax(CContent& inContent){
  if(!mMaximumSize){
    cout << "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%clean pichunter: take what comes" 
	 << endl;
    if(inContent.size()>=2){
      CContent::iterator lSecondOldestElement=inContent.begin();
      lSecondOldestElement++;
      inContent.front().second->intersect(*lSecondOldestElement->second);
      inContent.front().second->normalize();
      inContent.erase(lSecondOldestElement);//newly added
      mCurrentBest=mContent.front().second->clone();
    }
  }else{
    cout << "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%Target tracker case "
	 << endl;
    if(inContent.front().first>0.5){
      CContent::iterator lSecondOldestElement=inContent.begin();
      lSecondOldestElement++;
      combine(inContent.front(),
	      *lSecondOldestElement);
      inContent.erase(lSecondOldestElement);
    }else{
      /// or delete the old unbelieved knowledge
      inContent.erase(inContent.begin());
    }
  }
}

///
template<class T>
void CPSetOfPSets<T>::setForgetAfterNthInconsistency (int inN){
  mForgetAfterNthInconsistency=inN;
};

///
template<class T>
void CPSetOfPSets<T>::setNumberOfImages(const int& inNumberOfImages){
 mNumberOfImages= inNumberOfImages;
};

///
template<class T>
int CPSetOfPSets<T>::getNumberOfImages()const{
 return mNumberOfImages;
};

#endif

Documentation generated by muellerw@pc7170 on Son Okt 8 16:04:40 CEST 2000