// ===================================================================
// $Id: color.h,v 1.6 2003/10/30 16:36:35 havran Exp $
//
// color.h
//     Header file for class representing a color.
//
// Class: CValue, CStandardValues
//
// Licence and usage conditions in November 2003:
// This file can be used and modified only with written permission of
// Vlastimil Havran, Pohodli 27, 57001 Litomysl, the Czech Republic.
// This library is granted to be used by Polina Kondratieva within
// her MPI HIWI job working on BRDFLIB library. The usage condition
// may change in the next version of the library. At the moment,
// the library development is supported by Max-Planck-Institut for
// Informatik, Stuhlsatzenhausweg 85, 66123 Saarbruecken Germany
// See WWW: http://www.mpi-sb.mpg.de
// REPLACEMENT_STRING
//
// Initial coding by Vlastimil Havran, November, 1999.
//
// Given to BRDFLIB project for the Polina Kondratieva's
// HIWI JOB assignment in November 2003.

#ifndef __COLOR_H__
#define __COLOR_H__

// BRDFLIB headers
#include "configh.h"
#include "basic.h"
#include "errorg.h"
#include "spectrum.h"


__BEGIN_BRDFLIB_HEADER

// forward declarations


// the base division of the type of the values
enum EValuesBaseType {
  EE_ValuesColor = 0x200000,
  EE_ValuesWeigth = 0x400000
};

// -------------------------------------------------------------------------
// This is general class representing a vector of N values, that can be used
// for color computations in meaning:
// a) color   = energy at particular wavelengths,
// b) weigths = the coefficients preferably in range <0.0; 1.0>
//              to represent albedo etc.
//
// The current use is specified by the type inside the class
//
class CValues
{
public:
  // The type of the information is in two higher bytes
  // 0x1  = 1 component
  // 0x3  = 2 components
  // 0x7  = 3 components .. RGB
  // 0xF  = 4 components
  // 0x1F = N components, then N is specified by 2 lower bytes
  // 0x20 = Color - energy at given bands
  // 0x40 = Weigth - only the coefficients
  // The number of components is in two lower bytes, up to 65535
  enum EType {
    // colors
    EE_Color1 = 0x210001,
    EE_Color2 = 0x230002,
    EE_Color3 = 0x270003,
    // EE_Color4 = 0x2F0004,
    EE_ColorN = 0x3F0000,
    // weigths
    EE_Weigth1 = 0x410001,
    EE_Weigth2 = 0x430002,
    EE_Weigth3 = 0x470003,
    EE_WeigthN = 0x5F0000,
    EE_Weight4 = 0x5F0004
  };

public:
  // implicit constructor for Color3
  CValues():_type(EE_Weigth3) {
    _vals[0] = _vals[1] = _vals[2] = 0.0f;
  }

  CValues(float val):_type(EE_Color3) {
    _vals[0] = val; _vals[1] = val; _vals[2] = val;
  }

  // this is explicit constructor for Color3 given three values
  CValues(float r, float g, float b):_type(EE_Color3) {
    _vals[0] = r; _vals[1] = g; _vals[2] = b;
  }


  // this is explicit constructor for Color3 given three values
  CValues(EType type, const CSpectrum *spectrum = NULL):
    _type(EType(-1))
  {
    _vals[0] = _vals[1] = _vals[2] = 0.0;
    SetRepresentation(type, spectrum);
  }

  // implicit constructor to construct a color according to the spectrum
  // 1) Color1 2) Color2 3) Color3 4) Color4 5) ColorN
  // When constructing Color3, the spectrum for RGB is changed to the new one
  CValues(const CSpectrum &spectrum);

  // implicit constructor to construct weights:
  // 1) Weigth1 2) Weigth2 3) Weigth3 4) Weigth4 5) WeigthN
  CValues(int N);

  // copy constructor
  CValues(const CValues &val);

  // destructor
  ~CValues();

  // returns the type of the class
  EType GetType() const { return _type; }

  // returns the base type of the class
  EValuesBaseType GetBaseType() const {
    return (EValuesBaseType)(_type & 0x600000);
  }

  // returns the number of values in a concrete representation
  int GetCount() const { return int(_type & 0xFFFF); }

  // returns the value given an index
  inline const float & Get(int index) const;

  // sets the value for a given index
  inline void Set(int index, float newValue);

  // sets all the components to a given value;
  inline void Set(float newValue);

  // sets each element to its absolute value
  inline void Absolutize(void);

  // adds the value for a given index
  inline void Add(int index, float newValue);

  // adds the color if it has the same number of components
  // or only one component. Otherwise the error is reported.
  inline void Add(const CValues &a);

  // multiplies value for the value for a given index
  inline void Mul(int index, float newValue);

  // returns the sum of all the components
  inline float Sum() const;

  // returns the luminance of this color
  inline float Luminance() const;

  // returns the average of all channels
  inline float MeanValue() const;

  // returns the sume of absolute values of all components
  inline float AbsSum() const;

  // returns the maximum component in the vector
  inline float Max() const;

  // returns the minimum component in the vector
  inline float Min() const;

  // zero all the components
  inline void Init();

  // clip the values represented to given interval
  inline void Clip(const float &lowerBound, const float &upperBound);
  inline void ClipLower(const float &lowerBound);
  inline void ClipUpper(const float &upperBound);

  // This enables to set to another representation, the values
  // are preserved if the number of the components is the same.
  // When the change is performed to color with other than 3 components,
  // corresponding spectrum must be provided !
  // Otherwise, the content is not guaranted to have a meaning, please
  // use explicit call of Init() above.
  void SetRepresentation(CValues::EType newType,
			 const CSpectrum *newSpectrum = NULL);

  // this enables to set the spectrum for RGB color. This spectrum has
  // to have exactly three components, otherwise spectrum is not changed.
  // Returns true on success, in case of incorrect new spectrum returns false.
  // static bool SetSpectrumRGB(const CSpectrum *newSpectrum);


  // returns the type for the color given the number of components
  static CValues::EType GetColorType(int channels);

  // returns the type for the weigth given the number of components
  static CValues::EType GetWeigthType(int channels);

  // this returns the spectrum of the common colour in general
  const CSpectrum* GetSpectrum() const {
    // if (_type & 0x400000)
    //   return NULL; // no spectrum is assumed for weights
    // for 3 component values RGB spectrum
    if ( (_type & 0xFFFF) == C_FixedCount)
      return _spectrumRGB; // is assumed
    return v._spectrum; // the spectrum for other number of components
  }

  // -------------------------------------------------------------------
  // Operators

  // assignment operator
  CValues& operator=(const CValues &val);

  // assignment operator
  inline CValues& operator=(const float &val);

  // comparison operator
  inline bool operator==(const CValues &val);

  // to get the component for write/read access
  inline float& operator[] (int indx);

  // to get the component for read access only
  inline const float& operator[] (int indx) const;

  // to increase the energy in all components by specified value
  inline CValues& operator += (const float contribution);

  // to multiply the energy in all components by specified coefficient
  inline CValues& operator *= (const float mulCoeff);

  // scaling the value by another one, including the case that
  // multiplier has only one component.
  inline CValues& Scale(const CValues &multiplier);

  // scaling the value by another value, including the case that
  // multiplier has only one component. It computes:
  // this = (1.0 - multiplier) * this. Multiplier should be in
  // range <0.0 ; 1.0> for all the components.
  inline CValues& ScaleOneMinusMultiplier(const CValues &multiplier);

  // to add to values .. quick in execution, operands must be of the same type.
  // 1) Color + Color 2) Weigth + Weigth, no other meaning make sense.
  inline CValues& operator += (const CValues &color);

  // to subtract two values .. quick in execution, operands must be of
  // the same type.
  // 1) Color - Color 2) Weigth - Weigth, no other meaning make sense.
  inline CValues& operator -= (const CValues &color);

  // to multiply the energy in all components by weight, the only possible
  // interpretation is Color *= weigth . The weigth can have one component
  // only, or the same number of components as the color.
  inline CValues& operator *= (const CValues &weigth);

  // ------------------------------------------------------------------
  // the binary operator functions

  // compare the values of two instances, the maximum error for one component
  // must be specified by variable eps
  friend inline bool Equal(const CValues &val1, const CValues &val2,
			   float eps);

  // compare the representations of two instances
  friend inline bool EqualRepresentation(const CValues &val1,
                                         const CValues &val2);

  // Add two colors or two weigths. In boths cases operands must be
  // of the same type.
  friend inline CValues operator+(const CValues &val1, const CValues &val2);

  // Subtract two colors or two weigths. In boths cases operands must be
  // of the same type.
  friend inline CValues operator-(const CValues &val1, const CValues &val2);

  // multiply the colors by weigth or multiply the weigth by weigth.
  // The second argument val2 must have the same number of components
  // as val1, or number of components equal to one
  friend inline CValues operator*(const CValues &val1, const CValues &val2);

  // multiply the colors by float constant or
  // multiply the weigth by float constant
  friend inline CValues operator*(const CValues &val1, const float t);
  friend inline CValues operator*(const float t, const CValues &val1);

  // the input/output operators
  friend ostream& operator<< (ostream &s, const CValues &A);
  // istream& operator>> (istream &s, CValues &A)

  // the number of components stored in the class explicitely withou allocation
  enum { C_FixedCount = 3 };
  // the number of components for which the _spectrumRGB is defined
  enum { C_FixedSpectrumCount = 3 };

private:
  // the value to recognize, if value is in allocated or common array
  enum { ALLOCATED_VALUES = 0x100000};

  // this function allocates the array in _valsN
  inline void _Alloc(int N) {
    v._valsN = new float[N];
    assert(v._valsN);
  }

  // union enables to use the class in more concepts
  union {
    // the values, when class contains just three vectors - basic RGB concept
    float _vals[C_FixedCount];
    // the same representation in case of N-th multiple value means something
    // different
    struct {
      // the pointer to the values that should be allocated
      float *_valsN;
//#define D32bit
#define D64bit
      // C_FixedCount * sizeof(float)-sizeof(void*)-sizeof(float*) > 0
#ifdef D32bit
#if (C_FixedCount * 4 - 8) > 0
#define INCLUDE_PADDING_COLOR
#endif
#endif      
#ifdef D64bit
#if (C_FixedCount * 8 - 16) > 0
#define INCLUDE_PADDING_COLOR
#endif
#endif      
#if INCLUDE_PADDING_COLOR
      // just padding, when number of components is two, the space is used.
      char _padding[C_FixedCount * sizeof(float)-sizeof(void*)-sizeof(float*)];
#endif
      // the pointer to the spectrum for this color concept
      const CSpectrum *_spectrum;
    } v;
  };
  // this is the type of the value, 32 bits variable
  EType _type;

  // this is the static variable for spectrum, when the number of
  // components is three, the most typical case.
  static const CSpectrum * _spectrumRGB;
};


// This class offers standard values for given number of channels and
// for color values and for weigth values
//
// weigth values can be automatically generated according the requests
// color values must be explicitly set by the user (the user must enter
// the spectrum)
class CStandardValues
{
private:
  // sizes of arrays "_colorValues" and "_weigthValues"
  int _colorValuesNum;
  int _weigthValuesNum;
  // array with standard color values
  CValues **_colorValues;
  // array with the spectrums used by standard color values
  CSpectrum **_spectrums;
  // array with standard weigth values
  CValues **_weigthValues;

public:
  // constructor and destructor
  CStandardValues();
  ~CStandardValues();

  // sets new standard value
  void SetValue(const CValues &newValue);
  // returns standard value for given number of channels
  // and for given type of the values
  const CValues *GetValue(int channels, EValuesBaseType type);
};


// --------------------------------------------------------------------
// inline member functions

// ------------------------------------------------------------------
// inline member functions

inline const float &
CValues::Get(int i) const
{
  assert( i < GetCount());
  if (_type & ALLOCATED_VALUES)
    return v._valsN[i]; // the allocated array
  return _vals[i]; // the common array of values
}

// sets the value for a given index
inline void
CValues::Set(int i, float newValue)
{
  assert( i < GetCount());
  if (_type & ALLOCATED_VALUES) {
    v._valsN[i] = newValue; // the allocated array
    return;
  }
  _vals[i] = newValue; // the common array of values
}

// sets all the components to a given value
inline void
CValues::Set(float newValue)
{
  int i; // loop variable
  if (_type & ALLOCATED_VALUES) { // the allocated array
    float *val = v._valsN;
    for (i = GetCount(); i--;)
      *val++ = newValue;
    return;
  }
  else {
    float *val = _vals;
    for (i = GetCount(); i--;)
      *val++ = newValue; // the common array of values
    return;
  }
}

// sets each element to its absolute value
inline void
CValues::Absolutize(void)
{
  int i; // loop variable

  float * valP = (_type & ALLOCATED_VALUES) ? v._valsN : _vals;

  for (i = GetCount(); i--;) {
    *valP = fabsf(*valP);
    valP++;
  }
}

// adds the value for a given index
inline void
CValues::Add(int index, float newValue)
{
  assert( index < GetCount());
  if (_type & ALLOCATED_VALUES) {
    v._valsN[index] += newValue; // the allocated array
    return;
  }
  _vals[index] += newValue; // the common array of values
}

// multiplies the value for a given index
inline void
CValues::Mul(int index, float newValue)
{
  assert( index < GetCount());
  if (_type & ALLOCATED_VALUES) {
    v._valsN[index] *= newValue; // the allocated array
    return;
  }
  _vals[index] *= newValue; // the common array of values
}


// returns the sum of all the components - can be used as Luminance
// @@@ this should be modified so that it will really return the luminance
inline float
CValues::Luminance() const
{
  //int i; // loop variable
  float sum = 0.0; // the result

  if (_type & ALLOCATED_VALUES) { // the allocated array
    FATAL << "Not yet implemented" << endl;
    FATAL_ABORT;
#if 0
    float * valP= v._valsN;
    for (i = GetCount(); i--;)  sum += *valP++;
#endif
  }
  else { // the common array of values
    if ( (_type & 0xFFFF) == C_FixedCount) {
      // RGB color is assumed, luminance is computed as conversion to grayscale
      const float *valP = _vals;
      sum = (*valP++)*0.299f; // R
      sum += (*valP++)*0.587f; // G
      sum += (*valP)*0.114f; //B
      return sum;
    }
    if(GetCount()==1) {
      if (_type & ALLOCATED_VALUES)
	return v._valsN[0];
      else
	return _vals[0];
    }

    FATAL << "Not yet implemented" << endl;
    FATAL_ABORT;
#if 0
    const float *valP = _vals;
    for (i = GetCount(); i--;) sum += *valP++;
#endif
  }
  return sum;
}

// returns the sum of all the components - can be used as Luminance
inline float
CValues::MeanValue() const
{
  return Sum()/(float) GetCount();
}

// returns the sum of all the components - can be used as Luminance
inline float
CValues::Sum() const
{
  int i; // loop variable
  float sum = 0.0; // the result

  if (_type & ALLOCATED_VALUES) { // the allocated array
    float * valP= v._valsN;
    for (i = GetCount(); i--;)
      sum += *valP++;
  }
  else {
    const float *valP = _vals;
    for (i = GetCount(); i--;)
      sum += *valP++; // the common array of three values
  }
  return sum;
}


// returns the sum of all the components - can be used as Luminance
inline float
CValues::AbsSum() const
{
  int i; // loop variable
  float sum = 0.0; // the result

  if (_type & ALLOCATED_VALUES) { // the allocated array
    float * valP= v._valsN;
    for (i = GetCount(); i--;)
      sum += Abs(*valP++);
  }
  else {
    const float *valP = _vals;
    for (i = GetCount(); i--;)
      sum += Abs(*valP++); // the common array of three values
  }
  return sum;
}

// WARNING: This hack may not be portable, WARNING !
// WARNING: uncommenting this can slowdown the program (tested 29/2/2000)
// #define __CVALUES_HACK_COPY

// zero all the components
inline void
CValues::Init()
{
  if (_type & ALLOCATED_VALUES) { // the allocated array
#ifndef __CVALUES_HACK_COPY
    float *valP = v._valsN;
    for (int i = GetCount(); i--;)
      *valP++ = 0.0;
#else
    // try bzero also
    memset(&(v._valsN[0]), 0, GetCount() * sizeof(float));
#endif
    return;
  }
#ifndef __CVALUES_HACK_COPY
  float *valP = &(_vals[0]);
  for (int i = GetCount(); i--;)
    *valP++ = 0.0; // the common array of three values
#else
  // try bzero also
  memset(&(_vals[0]), 0, C_FixedCount * sizeof(float));
#endif
  return;
}

// returns the maximum component in the vector
inline float
CValues::Max() const
{
  float max = Get(0); // the result

  if (_type & ALLOCATED_VALUES) { // the allocated array
    float *valP = v._valsN;
    for (int i = GetCount(); i--; valP++)
      if (max < *valP)
	max = *valP;
  }
  else {
    const float *valP = _vals;
    for (int i = GetCount(); i--; valP++)
      if (max < *valP)
        max = *valP ; // the common array of three values
  }

  return max;
}

// returns the minimum component in the vector
inline float
CValues::Min() const
{
  float min = Get(0); // the result

  if (_type & ALLOCATED_VALUES) { // the allocated array
    float *valP = v._valsN;
    for (int i = GetCount(); i--; valP++)
      if (min > *valP)
	min = *valP;
  }
  else {
    const float *valP = _vals;
    for (int i = GetCount(); i--; valP++)
      if (min > *valP)
        min = *valP ; // the common array of three values
  }

  return min;
}

// performs the clipping of values to given interval
inline void
CValues::Clip(const float &lowerBound, const float &upperBound)
{
  if (_type & ALLOCATED_VALUES) { // the allocated array
    float *valP = v._valsN;
    for (int i = GetCount(); i--; valP++) {
      if (*valP < lowerBound)
	*valP = lowerBound;
      if (*valP > upperBound)
	*valP = upperBound;
    }
  }
  else { // the common array of three values
    float *valP = _vals;
    for (int i = GetCount(); i--; valP++) {
      if (*valP < lowerBound)
	*valP = lowerBound;
      if (*valP > upperBound)
	*valP = upperBound;
    } // for
  }
}

// performs the clipping of values to given interval
inline void
CValues::ClipLower(const float &lowerBound)
{
  if (_type & ALLOCATED_VALUES) { // the allocated array
    float *valP = v._valsN;
    for (int i = GetCount(); i--; valP++) {
      if (*valP < lowerBound)
	*valP = lowerBound;
    }
  }
  else { // the common array of three values
    float *valP = _vals;
    for (int i = GetCount(); i--; valP++) {
      if (*valP < lowerBound)
	*valP = lowerBound;
    } // for
  }
}

// performs the clipping of values to given interval
inline void
CValues::ClipUpper(const float &upperBound)
{
  if (_type & ALLOCATED_VALUES) { // the allocated array
    float *valP = v._valsN;
    for (int i = GetCount(); i--; valP++) {
      if (*valP > upperBound)
	*valP = upperBound;
    }
  }
  else { // the common array of three values
    float *valP = _vals;
    for (int i = GetCount(); i--; valP++) {
      if (*valP > upperBound)
	*valP = upperBound;
    } // for
  }
}

// -----------------------------------------------------------
// inline binary operators


// assignment operator
inline CValues&
CValues::operator=(const float &val)
{
  // now only copy the values to this instance
  if (_type & ALLOCATED_VALUES) {
    // values in the allocated array
    float *dst = &(v._valsN[0]);
    for (int i = GetCount(); i--;)
      *dst++ = val;
  }
  else {
    // the common array of values
    float *dst = &(_vals[0]);
    for (int i = GetCount(); i--;)
      *dst++ = val;
  }
  return *this; // OK, everything finished
}

// comparison operator
inline bool
CValues::operator==(const CValues &val)
{
  if (_type != val._type)
    return false; // surely not equal

  // we have to compare values one by one

  if (_type & ALLOCATED_VALUES) { // the allocated array of values
    const float *p = v._valsN;
    const float *q = val.v._valsN;
    for (int i = GetCount();i--;)
      if ( *p++ != *q++)
       return false;
  }
  else { // the common array of values
    const float *p = _vals;
    const float *q = val._vals;
    for (int i = GetCount();i--;)
      if ( *p++ != *q++ )
	return false;
  }

  // all the components are the same
  return true;
}

// to get the component for write/read access
inline float&
CValues::operator[] (int index)
{
  assert(index < GetCount());
  if (_type & ALLOCATED_VALUES)
    return v._valsN[index]; // from 4 values the allocated array
  return _vals[index]; // the common array of three values
}

// to get the component for read access only
inline const float&
CValues::operator[] (int index) const
{
  assert( index < GetCount());
  if (_type & ALLOCATED_VALUES)
    return v._valsN[index]; // from 4 values the allocated array
  return _vals[index]; // the common array of three values
}


// to increase the energy in all components by specified value
inline CValues&
CValues::operator += (const float contribution)
{
  if (_type & ALLOCATED_VALUES) {
    for (int i = GetCount();i--;)
      v._valsN[i] += contribution; // the allocated array
  }
  else {
    for (int i = GetCount();i--;)
      _vals[i] += contribution; // the common array of values
  }
  return *this;
}

// adds the color if it has the same number of components
// or only one component. Otherwise the error is reported.
inline void
CValues::Add(const CValues &arg)
{
  if (arg.GetCount() == GetCount()) {
#ifdef _DEBUG
    if ( (_type != arg._type) || // type must be the same
	 ( (_type & 0x200000) && //in case of color the spectrum must be the same
	   ( !(*(GetSpectrum()) == *(arg.GetSpectrum())) ) ) ) {
      FATAL << "The types of color for adding are incompatible.";
      FATAL_ABORT;
    }
#endif // _DEBUG
    if (_type & ALLOCATED_VALUES) {
      for (int i = GetCount();i--;)
	v._valsN[i] += arg.v._valsN[i]; // the allocated array
    }
    else {
      for (int i = GetCount();i--;)
	_vals[i] += arg._vals[i]; // the common array of values
    }
  }
  else {
    if (arg.GetCount() != 1) {
#ifdef _DEBUG
      FATAL << "The types of color for adding does not match even weekly";
#endif // _DEBUG
      FATAL_ABORT;
    }
    float a = arg.Get(0); // take the component from color
    if (_type & ALLOCATED_VALUES) {
      for (int i = GetCount();i--;)
	v._valsN[i] += a; // the allocated array
    }
    else {
      for (int i = GetCount();i--;)
	_vals[i] += a; // the common array of values
    }
  }

  return;
}

// to multiply the energy in all components by specified coefficient
inline CValues&
CValues::operator *= (const float mulCoeff)
{
  if (_type & ALLOCATED_VALUES) {
    for (int i = GetCount();i--;)
      v._valsN[i] *= mulCoeff; // the allocated array
  }
  else {
    for (int i = GetCount();i--;)
      _vals[i] *= mulCoeff; // the common array of values
  }
  return *this;
}

// to add to values .. quick in execution, operands must be of the same type.
// 1) Color + Color 2) Weigth + Weigth, no other meaning make sense.
inline CValues&
CValues::operator += (const CValues &arg)
{
#ifdef _DEBUG
  if ( (_type != arg._type) || // type must be the same
       ( (_type & 0x200000) && //in case of color the spectrum must be the same
	 ( !(*(GetSpectrum()) == *(arg.GetSpectrum())) ) ) ) {
	   FATAL << "The types of color for adding are incompatible.";
	   FATAL_ABORT;
	 }
#endif // _DEBUG

  if (_type & ALLOCATED_VALUES) {
    for (int i = GetCount();i--;)
      v._valsN[i] += arg.v._valsN[i]; // the allocated array
  }
  else {
    for (int i = GetCount();i--;)
      _vals[i] += arg._vals[i]; // the common array of values
  }
  return *this;
}

// to subtract two values .. quick in execution, operands must be
// of the same type.
// 1) Color + Color 2) Weigth + Weigth, no other meaning make sense.
inline CValues&
CValues::operator -= (const CValues &arg)
{
#ifdef _DEBUG
  if ( (_type != arg._type) || // type must be the same
       ( (_type & 0x200000) && //in case of color the spectrum must be the same
	 ( !(*(GetSpectrum()) == *(arg.GetSpectrum())) ) ) ) {
	   FATAL << "the types of color for substraction are incompatible.\n";
	   FATAL_ABORT;
	 }
#endif // _DEBUG

  if (_type & ALLOCATED_VALUES) {
    for (int i = GetCount();i--;)
      v._valsN[i] -= arg.v._valsN[i]; // the allocated array
  }
  else {
    for (int i = GetCount();i--;)
      _vals[i] -= arg._vals[i]; // the common array of values
  }
  return *this;
}

// to multiply the energy in all components by weight, the only possible
// interpretation is Color *= weigth or Weigth *= weigth . The weigth on
// the rigth side can have one component only, or the same number of
// components as the first component.
inline CValues&
CValues::operator *= (const CValues &weigth)
{
#ifdef _DEBUG
  if (!(weigth._type & 0x400000)) { // the second operand must be a weigth
    FATAL << "operator*=(const CValues &mul) - operand mul must be weigth.\n";
    FATAL_ABORT;
  }
#endif // _DEBUG
  if (weigth.GetCount() == 1) {
    // multiply by a constant
    return this->operator *= (weigth._vals[0]);
  }

  if ( GetCount() != weigth.GetCount()) {
    // nothing else is possible than multiplying by a constant
    assert( weigth.GetCount() == 1);
    return (*this) *= weigth.Get(0);
  }
  // we have to multiply component by component
  if (_type & ALLOCATED_VALUES) {
    for (int i = GetCount();i--;)
      v._valsN[i] *= weigth.v._valsN[i]; // the allocated array
  }
  else {
    for (int i = GetCount();i--;)
      _vals[i] *= weigth._vals[i]; // the common array of values
  }
  return *this;
}

// scaling the value by another one, including the case that
// multiplier has only one component
inline CValues&
CValues::Scale(const CValues &multiplier)
{
  if (multiplier.GetCount() == GetCount()) {
    // multiply the resulting BSDF by max(0, 1-kr) in every wavelength
    for(int i = multiplier.GetCount(); i-- ; ) {
      float c = multiplier.Get(i);
      this->Mul(i, c);
    } // for i
  }
  else {
    assert(multiplier.GetCount() == 1);
    // multiply the resulting BSDF by max(0, 1-kr) in every wavelength
    float c = multiplier.Get(0);
    for(int i = GetCount(); i-- ; ) {
      this->Mul(i, c);
    } // for i
  }

  return *this;
}

// scaling the value by another one, including the case that
// multiplier has only one component
inline CValues&
CValues::ScaleOneMinusMultiplier(const CValues &multiplier)
{
  if (multiplier.GetCount() == GetCount()) {
    // multiply the resulting BSDF by max(0, 1-kr) in every wavelength
    for(int i = multiplier.GetCount(); i-- ; ) {
      float c = multiplier.Get(i);
      this->Mul(i, c >= 1.0f ? 0.0f : 1.0f - c);
    } // for i
  }
  else {
    assert(multiplier.GetCount() == 1);
    // multiply the resulting BSDF by max(0, 1-kr) in every wavelength
    float c = multiplier.Get(0);
    for(int i = GetCount(); i-- ; ) {
      this->Mul(i, c >= 1.0f ? 0.0f : 1.0f - c);
    } // for i
  }

  return *this;
}

// ------------------------------------------------------------------
// the binary operator functions

// compare the values of two instances, the maximum error for one component
// must be specified by variable eps
inline bool
Equal(const CValues &val1, const CValues &val2, float eps)
{
  if (val1.GetCount() != val2.GetCount())
    return false; // cannot be equal

  // the type can be different, but the values must be the same

  // we have to multiply component by component
  if ((val1._type) & CValues::ALLOCATED_VALUES) {
    // the allocated array of values
    for (int i = val1.GetCount();i--;)
      if (Abs(val1.v._valsN[i] - val2.v._valsN[i]) > eps)
	return false;
  }
  else { // the common array of values
    for (int i = val1.GetCount();i--;)
      if ((val1._vals[i] - val2._vals[i]) > eps)
	return false;
  }
  return true; // alll the values are the same within epsilon
}

// compare the representations of two instances
inline bool
EqualRepresentation(const CValues &val1, const CValues &val2)
{
  if ( (val1._type == val2._type) &&
       ( (val1._type & 0x400000) ||
         (*(val1.GetSpectrum()) == *(val2.GetSpectrum())) ) )
    // the representions are equal
    return true;
  // the representations aren't equal
  return false;
}

// Add two colors or two weigths. The operands must be of the same type
inline CValues operator+(const CValues &val1, const CValues &val2)
{
  CValues val = val1;
  return val+= val2;
}

// Subtract two colors or two weigths. The operands must be of the same type
inline CValues operator-(const CValues &val1, const CValues &val2)
{
  CValues val = val1;
  return val-= val2;
}

// multiply the colors by weigth or multiply the weigth by weigth.
// The second argument val2 must have the same number of components
// as val1, or number of components equal to one
inline CValues operator*(const CValues &val1, const CValues &val2)
{
  CValues val = val1;
  return val *= val2;
}

// multiply the colors by weigth or multiply the weigth by weigth.
// The second argument val2 must have the same number of components
// as val1, or number of components equal to one
inline CValues operator*(const CValues &val1, const float t)
{
  CValues val = val1;
  return val *= t;
}

inline CValues operator*(const float t, const CValues &val1)
{
  CValues val = val1;
  return val *= t;
}


__END_BRDFLIB_HEADER

#endif // __COLOR_H__

