/***************************************************************************
 *   Copyright (C) 2005 by Robot Group Leipzig                             *
 *    martius@informatik.uni-leipzig.de                                    *
 *    fhesse@informatik.uni-leipzig.de                                     *
 *    der@informatik.uni-leipzig.de                                        *
 *                                                                         *
 *   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.             *
 *                                                                         *
 *   $Log: noisegenerator.h,v $
 *   Revision 1.13  2006/08/04 15:16:13  martius
 *   documentation
 *
 *   Revision 1.12  2006/07/14 12:23:59  martius
 *   selforg becomes HEAD
 *
 *   Revision 1.10.6.1  2005/11/22 14:55:21  martius
 *   added math.h
 *
 *   Revision 1.10  2005/10/06 17:07:16  martius
 *   removed MAXINT
 *
 *   Revision 1.9  2005/09/11 11:20:21  martius
 *   virtual destructors
 *
 *   Revision 1.8  2005/08/22 20:32:29  martius
 *   sine noise has phaseShift
 *
 *   Revision 1.7  2005/08/06 20:47:54  martius
 *   Commented
 *
 *   Revision 1.6  2005/08/03 20:31:40  martius
 *   sinenoise
 *   no random initialisation anymore
 *
 *   Revision 1.5  2005/07/21 15:11:19  martius
 *   normalised noise strength for colored noise
 *
 *   Revision 1.4  2005/07/14 16:07:12  fhesse
 *   cmath included
 *
 *   Revision 1.3  2005/07/06 16:05:34  martius
 *   noise generator is splitted into sub classes with a common interface
 *
 *   Revision 1.2  2005/06/15 16:04:56  martius
 *   de-templatified
 *                                                                 *
 ***************************************************************************/
#ifndef __NOISEGENERATOR_H
#define __NOISEGENERATOR_H

#include <stdlib.h>
#include <time.h>
#include <math.h>

/** Interface and basic class for noise generator.
    It is suitable for single noise channels but also multidimensional noise. 
 */
class NoiseGenerator{
public:
  NoiseGenerator() {
    dimension=0;
  };    

  virtual ~NoiseGenerator(){}

  /** initialization with the the given dimension for multidimensional noise
      @see add()
   */
  virtual void init(unsigned int dimension) {
    this->dimension = dimension;
  };

  /** generate somehow distributed random number parameterized with min and max.
      valid only for ONE random number, use \ref add() for 
      adding this kind of noise to several channels
   */
  virtual double generate(double min, double max) = 0; 

  /** adds multidimensional noise to the value field.
      Generic implementation calls generate for each channel.
      Overload this if you need different behavior.
      @param value field where noise is added. Must have length dimension (\ref init())
      @param p1 first parameter for random number distribution
      @param p2 second parameter for random number distribution
   */
  virtual void add(double *value, double p1, double p2){
    for (unsigned int i = 0; i < dimension; i++){
      value[i]+=generate(p1,p2);
    }    
  }

protected:
  //generates white (no averaging) uniformly distributed random number between "min" and "max"  
  double uniform(double min=-0.1, double max=0.1){
    return( (double(rand())/RAND_MAX)*(max-min)+min);
  }
  unsigned int dimension;
};

/// generates white (no averaging) uniformly distributed random number between "min" and "max"  
class WhiteUniformNoise : public NoiseGenerator{
public:
  WhiteUniformNoise(){}
  virtual ~WhiteUniformNoise(){}
  virtual double generate(double min, double max) {
    return uniform(min,max);
  }; 
};

/// generates white and normal distributed random numbers. p1: mean, p2: variance
class WhiteNormalNoise : public NoiseGenerator{
public:
  WhiteNormalNoise(){}
  virtual ~WhiteNormalNoise(){}
  virtual double generate(double mean, double variance) {
    double x1=uniform(0, 1);
    double x2=uniform(0, 1);
    return( (sqrt(-2*log(x1)) *cos(2*M_PI*x2))  * variance + mean) ; 
  };
};

/// generated colored noise. This is obtained by using time average of uniform distributed noise.
class ColorUniformNoise : public NoiseGenerator{
public:
  /** @param tau time averaging factor (1/window)
      (1: smoothing (white) 0.1: strong color, 0 no noise anymore
  */
  ColorUniformNoise(double tau=0.3)
    : tau(tau){
    mean1channel=0.0;
    mean=0;
    // we can consider the distribution as a sum of individual distributions.
    // following the central limit theorem the variance is 1/sqrt(n)*V 
    // we assume n=1/tau
    factor=sqrt(1/(tau+0.01)); 
  }
  virtual ~ColorUniformNoise(){}
  virtual void init(unsigned int dimension){
    NoiseGenerator::init(dimension);
    mean = (double*)malloc(sizeof(double) * dimension);    
    for (unsigned int i=0; i<dimension; i++){
	mean[i]=0.0;
    }	
  }

  virtual double generate(double min, double max) {
    
    mean1channel+=tau * (uniform(min*factor,  max*factor) - mean1channel);
    return(mean1channel);
  } 

  /** adds multidimensional noise to the value field.
      @param value field where noise is added. Must have length dimension (\ref init())
      @param min lower bound of interval
      @param max upper bound of interval 
   */
  virtual void add(double *value, double min, double max){
    for (unsigned int i = 0; i < dimension; i++){     
      mean[i]+=tau * (uniform(min*factor,  max*factor) - mean[i]);
      value[i]+=mean[i];
    }    
  }   

protected:
  double tau; // smoothing paramter
  double* mean;  
  double mean1channel;
  double factor;
};

/// like ColorUniformNoise but averaging over normal distributed noise instead.
class ColorNormalNoise : public WhiteNormalNoise{
public:
  ColorNormalNoise(double tau=0.3)
    : tau(tau){
    mean = 0;
    mean1channel=0.0;
    // we can consider the distribution as a sum of individual distributions.
    // following the central limit theorem the variance is 1/sqrt(n)*V 
    // we assume n=1/tau
    factor=sqrt(1/(tau+0.01)); 
  }

  virtual ~ColorNormalNoise(){}

  virtual void init(unsigned int dimension){
    WhiteNormalNoise::init(dimension);
    mean = (double*)malloc(sizeof(double) * dimension);    
    for (unsigned int i=0; i<dimension; i++){
	mean[i]=0.0;
    }	
  }

  virtual double generate(double variance, double meanvalue) {
    mean1channel += tau * WhiteNormalNoise::generate(variance*factor, meanvalue) - mean1channel;
    return(mean1channel);
  } 

  /** adds multidimensional noise to the value field.
      @param value field where noise is added. Must have length dimension (\ref init())
      @param variance variance of normal distribution
      @param meanvalue mean value  of normal distribution
   */
  virtual void add(double *value, double variance, double meanvalue){
    for (unsigned int i = 0; i < dimension; i++){     
      mean[i]+=tau * (WhiteNormalNoise::generate(variance*factor, meanvalue) - mean[i]);
      value[i]+=mean[i];
    }    
  }   

protected:
  double tau; // smoothing paramter
  double* mean;  
  double mean1channel;
  double factor;
};

/// Sine wave noise. Produces a 90 degree phase shifted sine wave or white noise
class SineWhiteNoise : public NoiseGenerator{
public:
  /** @param omega anglerate
      @param amplitude amplitude of the sine wave in respect to the noise strength
      @param phaseShift phase shift between channels in rad
      @param channels number of channel for sine noise (and the rest get white noise)
   */
  SineWhiteNoise(double omega, double amplitude, double phaseShift = M_PI/2, 
		 unsigned int channels = 0xFFFF)
    : omega(omega), amplitude(amplitude)
    , channels(channels), phaseShift(phaseShift){
    t=0;
  }

  virtual ~SineWhiteNoise(){}

  virtual double generate(double min, double max) {        
    t ++;
    return uniform(min,  max) + sin(t*omega)*amplitude*(max-min);
  } 

  /** adds multidimensional noise to the value field.
      @param value field where noise is added. Must have length dimension (\ref init())
      @param min lower bound of interval
      @param max upper bound of interval 
   */
  virtual void add(double *value, double min, double max){

    for (unsigned int i = 0; i < dimension; i++){     
      if(i < channels)
	value[i]+=sin(t*omega+i*phaseShift)*amplitude*(max-min);
      value[i]+=uniform(min,  max);
    }    
    t ++;
  }   
  void setOmega(double omega){
    this->omega=omega;
  }
  void setPhaseShift(double phaseShift){
    this->phaseShift=phaseShift;
  }
  
protected:
  long int t;        // time
  double omega;     // angle velocity
  double amplitude; // factor for noise strength  
  unsigned int channels;     // number of channels with sine
  double phaseShift; // phase shift

};


#endif

