//# StokesVector.h: A fast casacore::RigidVector with casacore::Stokes conversions
//# Copyright (C) 1996,1999,2001,2003
//# Associated Universities, Inc. Washington DC, USA.
//#
//# This library is free software; you can redistribute it and/or modify it
//# under the terms of the GNU Library General Public License as published by
//# the Free Software Foundation; either version 2 of the License, or (at your
//# option) any later version.
//#
//# This library 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 Library General Public
//# License for more details.
//#
//# You should have received a copy of the GNU Library General Public License
//# along with this library; if not, write to the Free Software Foundation,
//# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA.
//#
//# Correspondence concerning AIPS++ should be adressed as follows:
//#        Internet email: aips2-request@nrao.edu.
//#        Postal address: AIPS++ Project Office
//#                        National Radio Astronomy Observatory
//#                        520 Edgemont Road
//#                        Charlottesville, VA 22903-2475 USA
//#
//#
//# $Id$

#ifndef MSVIS_STOKESVECTOR_H
#define MSVIS_STOKESVECTOR_H

#include <casacore/casa/aips.h>
#include <casacore/casa/IO/AipsIO.h>
//#include <casacore/tables/Tables/TableRecord.h>
#include <casacore/casa/BasicSL/Complex.h>
#include <casacore/casa/Arrays/Vector.h>
#include <casacore/casa/Arrays/Matrix.h>
#include <casacore/casa/Arrays/MatrixMath.h>
#include <casacore/scimath/Mathematics/RigidVector.h>
#include <casacore/scimath/Mathematics/SquareMatrix.h>
#include <casacore/casa/Arrays/IPosition.h>
#include <casacore/casa/BasicMath/Math.h>
#include <casacore/casa/iostream.h>

namespace casa { //# NAMESPACE CASA - BEGIN

//# Forward Declarations
class StokesVector;
// <summary>
// Two specialized 4-vector classes for polarization handling
// </summary>

// <use visibility=export>

// <reviewed reviewer="" date="yyyy/mm/dd" tests="" demos="">
// </reviewed>
// <prerequisite>
//   <li> RigidVector
//   <li> Stokes
// </prerequisite>
//
// <etymology>
// StokesVector and CStokesVector (casacore::Complex StokesVector) are two classes
// designed to handle a 4-vector of polarization values (I,Q,U,V or
// e.g., RR,RL,LR,LL).
// </etymology>
//
// <synopsis>
// StokesVectors are RigidVectors of length 4. They have a few special
// member functions to do polarization conversions efficiently.
// CStokesVector also has a real() member function to get at the real
// part of the components.
// </synopsis>
//
// <example>
// <srcblock>
// // Create a real valued I,Q,U,V StokesVector
// StokesVector pixel(4.0,2.0,1.0,0.1);
// // convert to a casacore::Complex valued vector of linear polarizations
// CStokesVector cohVec=applySlin(pixel);
// // convert back to I,Q,U,V
// cohVec.applySlinInv();
// // Write out the real part
// cout<< cohVec.real() <<endl;
// </srcblock>
// </example>
//
// <motivation>
// Full polarization processing of interferometry data uses real and complex
// valued 4-vectors. The StokesVector specialization handles this more 
// efficiently than a standard casacore::Vector of size 4.
// </motivation>
//
// <thrown>
//    <li> No exceptions
// </thrown>
//
// <todo asof="1996/11/07">
//   <li> we may want to add Stokesvector Eigenvalues
// </todo>


class CStokesVector:public casacore::RigidVector<casacore::Complex,4> {
public:

  static casacore::String dataTypeId() {return "CStokesVector";};
  //  CStokesVector(casacore::Int n):RigidVector<casacore::Complex,4>(n) {} 
  // The casacore::Complex data members are automatically initialized to 0.
  CStokesVector():RigidVector<casacore::Complex,4>() {} 
  // Construct from scalar, setting all values to a constant.
  explicit CStokesVector(const casacore::Complex& c):RigidVector<casacore::Complex,4>(c) {}
  // Construct with four values specified.
  CStokesVector(const casacore::Complex& v0, const casacore::Complex& v1, 
		const casacore::Complex& v2, const casacore::Complex& v3):
    casacore::RigidVector<casacore::Complex,4>(v0,v1,v2,v3) {}
  // Construct from c-array
  CStokesVector(const casacore::Complex v[4]):RigidVector<casacore::Complex,4>(v) {}
  // Construct from casacore::Vector (should have length 4)
//  CStokesVector(const casacore::Vector<casacore::Complex> & v):RigidVector<casacore::Complex,4>(v) {}
  // Copy constructor with copy semantics.
  CStokesVector(const CStokesVector& v):RigidVector<casacore::Complex,4>(v){}
  // Construct from RigidVector
//  CStokesVector(const casacore::RigidVector<casacore::Complex,4>& v):RigidVector<casacore::Complex,4>(v) {}
  // Assignment
  CStokesVector& operator=(const CStokesVector& v) {
    casacore::RigidVector<casacore::Complex,4>::operator=(v); return *this;
  }
  // Assign from a Vector
  CStokesVector& operator=(const casacore::Vector<casacore::Complex>& v) {
    casacore::RigidVector<casacore::Complex,4>::operator=(v); return *this;
  }
  // Assign from a scalar, setting all values to a constant
  CStokesVector& operator=(const casacore::Complex& c) {
    casacore::RigidVector<casacore::Complex,4>::operator=(c); return *this;
  }
  // Negation
  CStokesVector& operator-() {
    casacore::RigidVector<casacore::Complex,4>::operator-(); return *this;
  }
  // Addition
  CStokesVector& operator+=(const CStokesVector& v) {
    casacore::RigidVector<casacore::Complex,4>::operator+=(v); return *this;
  }
  // Subtraction
  CStokesVector& operator-=(const CStokesVector& v) {
    casacore::RigidVector<casacore::Complex,4>::operator-=(v); return *this;
  }
  CStokesVector& operator*=(const CStokesVector& v) {
    casacore::RigidVector<casacore::Complex,4>::operator*=(v); return *this;
  }
  // casacore::Matrix multiplication - v*=m is equivalent to v=m*v
  CStokesVector& operator*=(const casacore::SquareMatrix<casacore::Complex,4>& m) {
    casacore::RigidVector<casacore::Complex,4>::operator*=(m); return *this;
  }
  CStokesVector& operator*=(casacore::Float f) {
    v_p[0]*=f; v_p[1]*=f; v_p[2]*=f; v_p[3]*=f; return *this;
  }
  // Equality
  casacore::Bool operator==(const CStokesVector& v) const {
    return (v_p[0]==v.v_p[0] && v_p[1]==v.v_p[1] &&
		  v_p[2]==v.v_p[2] && v_p[3]==v.v_p[3]);
  }
  // Inequality
  casacore::Bool operator!=(const CStokesVector& v) const {
    return (v_p[0]!=v.v_p[0] || v_p[1]!=v.v_p[1] ||
		  v_p[2]!=v.v_p[2] || v_p[3]!=v.v_p[3]);
  }

  // Apply conversion matrix from casacore::Stokes to linear, in place.
  CStokesVector& applySlin() {
    casacore::Complex i=v_p[0],q=v_p[1], u=v_p[2],iv=v_p[3]*casacore::Complex(0,1);
    v_p[0]=(i+q);  v_p[1]=(u+iv); 
    v_p[2]=(u-iv); v_p[3]=(i-q);
    return *this;
  }
  // Apply conversion matrix from casacore::Stokes to circular, in place
  CStokesVector& applyScirc() {
    casacore::Complex i=v_p[0],q=v_p[1],iu=v_p[2]*casacore::Complex(0,1),v=v_p[3];
    v_p[0]=(i+v);  v_p[1]=(q+iu); 
    v_p[2]=(q-iu); v_p[3]=(i-v);
    return *this;
  }
  // Apply conversion matrix from linear to casacore::Stokes, in place
  CStokesVector& applySlinInv() {
      using namespace casacore;
    casacore::Complex xx=v_p[0],xy=v_p[1],yx=v_p[2],yy=v_p[3];
    v_p[0]=(xx+yy)/2; v_p[1]=(xx-yy)/2;
    v_p[2]=(xy+yx)/2; v_p[3]=casacore::Complex(0,1)*(yx-xy)/2;
    return *this;
  }
  // Apply conversion matrix from circular to casacore::Stokes, in place
  CStokesVector& applyScircInv() {
      using namespace casacore;
    casacore::Complex rr=v_p[0],rl=v_p[1],lr=v_p[2],ll=v_p[3];
    v_p[0]=(rr+ll)/2; v_p[3]=(rr-ll)/2;
    v_p[1]=(rl+lr)/2; v_p[2]=casacore::Complex(0,1)*(lr-rl)/2;
    return *this;
  }
  // Return a StokesVector with the real part.
//  StokesVector real();
  // inner product of two complex vectors
  friend casacore::Complex innerProduct(const CStokesVector& l,
			      const CStokesVector& r) {
    return l.v_p[0]*conj(r.v_p[0])+ l.v_p[1]*conj(r.v_p[1])+
      l.v_p[2]*conj(r.v_p[2])+ l.v_p[3]*conj(r.v_p[3]);
  }
  friend double norm(const CStokesVector& l) {
      using namespace casacore;
    return ::sqrt(square(l.v_p[0].real())+square(l.v_p[0].imag())+
		  square(l.v_p[1].real())+square(l.v_p[1].imag())+
		  square(l.v_p[2].real())+square(l.v_p[2].imag())+
		  square(l.v_p[3].real())+square(l.v_p[3].imag()));
}
  // Write out a CStokesVector using the casacore::Vector output method.
  friend std::ostream& operator<<(std::ostream& os, const CStokesVector& v) {
    os << v.vector();
    return os;
  }
};

// Multiplication of CStokesVector by a casacore::Complex SquareMatrix
inline CStokesVector operator*(const casacore::SquareMatrix<casacore::Complex,4>& m,
			       const CStokesVector& v) {
  CStokesVector result(v);
  return result*=m;
}

inline void defaultValue(CStokesVector& v) {
  v=casacore::Complex(0.0,0.0);
}

class StokesVector:public casacore::RigidVector<casacore::Float,4> {
  
public:
  static casacore::String dataTypeId() {return "StokesVector";};
  //  StokesVector(casacore::Int n):RigidVector<casacore::Float,4>(n) {} 
  // Default constructor zeroes vector.
  StokesVector():RigidVector<casacore::Float,4>() {}
  // Construct from scalar, setting all values to a constant.
  StokesVector(casacore::Float f):RigidVector<casacore::Float,4>(f) {};
  // Construct with four values specified.
  StokesVector(casacore::Float v0, casacore::Float v1, casacore::Float v2, casacore::Float v3): casacore::RigidVector<casacore::Float,4>(v0,v1,v2,v3){}
  // Construct from casacore::Vector (should have length 4)
//  StokesVector(const casacore::Vector<casacore::Float> & v):RigidVector<casacore::Float,4>(v) {}
  // Copy constructor with copy semantics.
  StokesVector(const StokesVector& v):RigidVector<casacore::Float,4>(v) {}
  // Construct from RigidVector
//  StokesVector(const casacore::RigidVector<casacore::Float,4>& v):RigidVector<casacore::Float,4>(v) {}
  // Assignment
  StokesVector& operator=(const StokesVector& v) {
    casacore::RigidVector<casacore::Float,4>::operator=(v); return *this;
  }
  // Assign from a Vector
  StokesVector& operator=(const casacore::Vector<casacore::Float>& v) {
    casacore::RigidVector<casacore::Float,4>::operator=(v); return *this;
  }
  // Assign from a scalar, setting all values to a constant
  StokesVector& operator=(casacore::Float f) {
    casacore::RigidVector<casacore::Float,4>::operator=(f); return *this;
  }
  // Negation
  StokesVector& operator-() {
    casacore::RigidVector<casacore::Float,4>::operator-(); return *this;
  }
  // Addition
  StokesVector& operator+=(const StokesVector& v) {
    casacore::RigidVector<casacore::Float,4>::operator+=(v); return *this;
  }
  // Subtraction
  StokesVector& operator-=(const StokesVector& v) {
    casacore::RigidVector<casacore::Float,4>::operator-=(v); return *this;
  }
  StokesVector& operator*=(casacore::Float f) {
    casacore::RigidVector<casacore::Float,4>::operator*=(f); return *this;
  }
  StokesVector& operator*=(const StokesVector& v) {
    casacore::RigidVector<casacore::Float,4>::operator*=(v); return *this;
  }
  // casacore::Matrix multiplication - v*=m is equivalent to v=m*v
  StokesVector& operator*=(const casacore::SquareMatrix<casacore::Float,4>& m) {
    casacore::RigidVector<casacore::Float,4>::operator*=(m); return *this;
  }
  // Equality
  casacore::Bool operator==(const StokesVector& v) const {
    return (v_p[0]==v.v_p[0] && v_p[1]==v.v_p[1] &&
		  v_p[2]==v.v_p[2] && v_p[3]==v.v_p[3]);
  }
  // Inequality
  casacore::Bool operator!=(const StokesVector& v) const {
    return (v_p[0]!=v.v_p[0] || v_p[1]!=v.v_p[1] ||
		  v_p[2]!=v.v_p[2] || v_p[3]!=v.v_p[3]);
  }
  // Compute the maximum EigenValue
  casacore::Float maxEigenValue() const;
  // Compute the minimum EigenValue
  casacore::Float minEigenValue() const;
  // Compute the determinant of the coherence matrix
  casacore::Float determinant() const;

  // The innerproduct of 2 StokesVectors
  friend casacore::Float innerProduct(const StokesVector& l, const StokesVector& r) {
    return l.v_p[0]*r.v_p[0]+ l.v_p[1]*r.v_p[1]+
      l.v_p[2]*r.v_p[2]+ l.v_p[3]*r.v_p[3];
  }
  // Write out a StokesVector using the casacore::Vector output method.
  friend std::ostream& operator<<(std::ostream& os, const StokesVector& v) {
    os << v.vector();
    return os;
  }

};

inline void defaultValue(StokesVector& v) {
  v=0.0f;
}

// Multiply by a scalar
inline StokesVector operator*(casacore::Float f, const StokesVector& v) {
  StokesVector r(v);
  return r*=f;
}
// Multiply by a scalar
inline StokesVector operator*(const StokesVector& v, casacore::Float f) {
  StokesVector r(v);
  return r*=f;
}

// Multiplication of StokesVector by a SquareMatrix
inline StokesVector operator*(const casacore::SquareMatrix<casacore::Float,4>& m,
			      const StokesVector& v) {
  StokesVector result(v);
  return result*=m;
}

// Apply conversion matrix from casacore::Stokes to linear(returns result)
inline CStokesVector& applySlin(CStokesVector& result, 
				const StokesVector& v) {
  casacore::Complex t=casacore::Complex(0.,v(3)); 
  result(0)=v(0)+v(1);
  result(1)=v(2)+t;
  result(2)=v(2)-t;
  result(3)=v(0)-v(1);
  return result;
}
// Apply conversion matrix from casacore::Stokes to linear.
inline CStokesVector applySlin(const StokesVector& v) {
  CStokesVector result;
  return applySlin(result,v);
}
// Apply conversion matrix from casacore::Stokes to circular.
inline CStokesVector& applyScirc(CStokesVector& result,
				 const StokesVector& v) {
  casacore::Complex t=casacore::Complex(0.,1.0)*v(2);
  result(0)=v(0)+v(3);
  result(1)=v(1)+t;
  result(2)=v(1)-t;
  result(3)=v(0)-v(3);
  return result;
}
// Apply conversion matrix from casacore::Stokes to circular.
inline CStokesVector applyScirc(const StokesVector& v) {
  CStokesVector result;
  return applyScirc(result,v);
}

// Apply conversion matrix from linear to Stokes.
inline StokesVector& applySlinInv(StokesVector& result, const CStokesVector& v) {
    using namespace casacore;
  result(0)=real(v(0)+v(3))/2;
  result(1)=real(v(0)-v(3))/2;
  result(2)=real(v(1)+v(2))/2;
  result(3)=real(casacore::Complex(0.,1.0)*(v(2)-v(1))/2);
  return result;
}

// Apply conversion matrix from linear to Stokes.
inline StokesVector applySlinInv(const CStokesVector& v) {
  StokesVector result;
  return applySlinInv(result,v);
}

// Apply conversion matrix from circular to Stokes.
inline StokesVector& applyScircInv(StokesVector& result, const CStokesVector& v) {
    using namespace casacore;
  result(0)=real(v(0)+v(3))/2;
  result(1)=real(v(1)+v(2))/2;
  result(2)=real(casacore::Complex(0.,1.0)*(v(2)-v(1))/2);
  result(3)=real(v(0)-v(3))/2;
  return result;
}

// Apply conversion matrix from circular to Stokes.
inline StokesVector applyScircInv(const CStokesVector& v) {
  StokesVector result;
  return applyScircInv(result,v);
}

// The following are needed until Image no longer has
// sigma images
//StokesVector& sqrt(const StokesVector& v);

//CStokesVector& sqrt(const CStokesVector& v);


} //# NAMESPACE CASA - END

#endif