//# VisibilityIterator.h: Step through the MeasurementEquation by visibility
//# Copyright (C) 1996,1997,1998,1999,2000,2001,2002,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 addressed 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: VisibilityIterator.h,v 19.14 2006/02/28 04:48:58 mvoronko Exp $

#ifndef MSVIS_VISIBILITYITERATOR_H
#define MSVIS_VISIBILITYITERATOR_H

#include <casacore/casa/Arrays/Cube.h>
#include <casacore/casa/Arrays/Matrix.h>
#include <casacore/casa/Arrays/Slicer.h>
#include <casacore/casa/BasicSL/String.h>
#include <casacore/casa/Containers/RecordInterface.h>
#include <casacore/casa/Quanta/MVDoppler.h>
#include <casacore/casa/aips.h>
#include <casacore/measures/Measures/MCDoppler.h>
#include <casacore/measures/Measures/MDoppler.h>
#include <casacore/measures/Measures/MeasConvert.h>
#include <casacore/measures/Measures/Stokes.h>
#include <casacore/ms/MSOper/MSDerivedValues.h>
#include <casacore/ms/MeasurementSets/MSIter.h>
#include <casacore/ms/MeasurementSets/MeasurementSet.h>
#include <casacore/scimath/Mathematics/RigidVector.h>
#include <casacore/scimath/Mathematics/SquareMatrix.h>
#include <msvis/MSVis/StokesVector.h>
#include <msvis/MSVis/VisBufferComponents.h>
#include <msvis/MSVis/VisImagingWeight.h>
#include <casacore/tables/Tables/ArrayColumn.h>
#include <casacore/tables/Tables/ScalarColumn.h>

#include <map>
#include <set>
#include <vector>

namespace casa { //# NAMESPACE CASA - BEGIN

//# forward decl

namespace asyncio {

class VLAT;

// <summary>
// The PrefetchColumns class is used to specify a set of columns that
// can be prefetched when the (RO)VisibilityIterator is using asynchronous
// I/O.
// </summary>
//
// <synopsis>
// When creating an ROVisibilityIterator or VisibilityIterator that can
// potentially operate using asynchronous I/O it is necessary to specify
// which columns of the main table are to be prefetched by the lookahead
// thread.  This class provides the way to specify those columns.  The
// class is an STL set object of enum values defined in VisBufferComponents.h.
// These roughly correspond to the different components that can be access
// via a VisBuffer.
//
// </synopsis>
//
// <example>
// This is a simple example showing the construction of a PrefetchColumns
// and its use in a VisibilityIterator constructor.  For more complex cases
// (e.g., where the columns to be prefetched depend on other factors) the
// class provides some additional utility methods.
//
// Usually the file that creates the VI will include VisBuffer which will
// then include VisBufferComponents.h; if not then the user will also need
// to add "#include <msvis/MSVis/VisBufferComponents.h>" to their file.
//
// <code>
//    asyncio::PrefetchColumns prefetchColumns =
//            PrefetchColumns::prefetchColumns (VisBufferComponents::Ant1,
//                                              VisBufferComponents::Ant2,
//                                              VisBufferComponents::ArrayId,
//                                              VisBufferComponents::Direction1,
//                                              VisBufferComponents::Direction2,
//                                              VisBufferComponents::Feed1,
//                                              VisBufferComponents::Feed1_pa,
//                                              VisBufferComponents::Feed2,
//                                              VisBufferComponents::Feed2_pa,
//                                              VisBufferComponents::FieldId,
//                                              VisBufferComponents::FlagCube,
//                                              VisBufferComponents::Flag,
//                                              VisBufferComponents::FlagRow,
//                                              VisBufferComponents::Freq,
//                                              VisBufferComponents::NChannel,
//                                              VisBufferComponents::NCorr,
//                                              VisBufferComponents::NRow,
//                                              VisBufferComponents::ObservedCube,
//                                              VisBufferComponents::PhaseCenter,
//                                              VisBufferComponents::PolFrame,
//                                              VisBufferComponents::SpW,
//                                              VisBufferComponents::Time,
//                                              VisBufferComponents::Uvw,
//                                              VisBufferComponents::UvwMat,
//                                              VisBufferComponents::Weight,
//                                              -1);
//
//     wvi_p = new VisibilityIterator (& prefetchColumns, * wvi_p);
//
//
// </code>
//
// </example
class PrefetchColumns : public std::set<VisBufferComponents::EnumType>{

public:

    PrefetchColumns operator+ (const PrefetchColumns & other);

    static casacore::String columnName (casacore::Int id);
    static PrefetchColumns prefetchColumnsAll ();
    static PrefetchColumns prefetchAllColumnsExcept (casacore::Int firstColumn, ...);
    static PrefetchColumns prefetchColumns (casacore::Int firstColumn, ...);
};

} // end namespace asyncio

class VisBuffer;
class VisibilityIteratorReadImpl;
class VisibilityIteratorWriteImpl;

class SubChunkPair : public std::pair<casacore::Int, casacore::Int>{

public:

    // First component is Chunk and second is subchunk

    SubChunkPair () { resetToOrigin ();}
    SubChunkPair (casacore::Int a , casacore::Int b) : pair<casacore::Int,casacore::Int> (a,b) {}

    casacore::Bool operator== (const SubChunkPair & other){
        return first == other.first && second == other.second;
    }

    casacore::Bool operator< (const SubChunkPair & other){
        return first < other.first ||
               (first == other.first && second < other.second);
    }

    casacore::Bool atOrigin () const { return * this == SubChunkPair ();}
    casacore::Int chunk () const { return first;}
    void incrementSubChunk () { second ++;}
    void incrementChunk () { first ++; second = 0; }

    void resetSubChunk () { second = 0;} // position to start of chunk

    void resetToOrigin () { first = 0; second = 0; }
       // Make a subchunk pair that is positioned to the first subchunk of the
       // first chunk (i.e., 0,0)

    casacore::Int subchunk () const { return second;}
    casacore::String toString () const;

    static SubChunkPair noMoreData ();

private:


};


// <summary>
// ROVisibilityIterator iterates through one or more readonly MeasurementSets
// </summary>
// <use visibility=export>
// <reviewed reviewer="" date="yyyy/mm/dd" tests="" demos="">
// </reviewed>
// <prerequisite>
//   <li> <linkto class="MSIter">MSIter</linkto>
//   <li> <linkto class="casacore::MeasurementSet">casacore::MeasurementSet</linkto>
//   <li> <linkto class="VisSet">VisSet</linkto>
//   <li> <linkto class="PrefetchColumns">PrefetchColumns</linkto>
// </prerequisite>
//
// <etymology>
// The ROVisibilityIterator is a readonly iterator returning visibilities
// </etymology>
//
// <synopsis>
// ROVisibilityIterator provides iteration with various sort orders
// for one or more MSs. It has member functions to retrieve the fields
// commonly needed in synthesis calibration and imaging.
//
// One should use <linkto class="VisBuffer">VisBuffer</linkto>
// to access chunks of data.
//
// ROVisibilityIterators can be either synchronous or asynchronous, depending
// on the constructor used to create them as well as the current value of
// a CASARC file setting.  A synchronous instance is works the same as
// this class ever worked; an asynchronous instance uses a second thread
// (the Visibility Lookahead Thread or VLAT) to fill the VisBuffers in
// advance of their use by the original thread.
//
// To create an asynchronous instance of ROVI you must use one of the two
// constructors which have a pointer to a PrefetchColumns object as the
// first argument.  This object specifies which table columns should be
// prefetched by the VLAT; accessing columns not specified in the PrefetchColumns
// object will result in an exception containing an error message indicating
// that a the VisBuffer does not contain the requested column.  In addition
// to using the appropriate constructor, the CASARC file setting
// VisibilityIterator.
//
// +-------------------+
// |                   |
// | *** Nota Bene *** |
// |                   |
// +-------------------+
//
// Because of the multithreaded nature of asynchronous I/O, the user
// needs to be a bit more careful in the use of the VI and it's attached VisBuffer.
// casacore::Data access operations need to be directed to the VisBuffer.  Additionally
// the user must not attempt to access the data using a separate VI since
// the underlying casacore objects are not threadsafe and bizarre errors
// will likely occur.
//
// CASARC Settings
// ===============
//
// casacore::Normal settings
// ---------------
//
// VisibilityIterator.async.enabled - Boolean value that enables or disables
//     async I/O.  The default value is currently false (i.e., disabled).
// VisibilityIterator.async.nBuffers - The number of lookahead buffers.  This
//     defaults to 2.
//
//
// Debug settings
// --------------
//
// VisibilityIterator.async.doStats: true
// VisibilityIterator.async.debug.logFile: stderr
// VisibilityIterator.async.debug.logLevel: 1
//

// </synopsis>
//
// <example>
// <code>
// //
// </code>
// </example>
//
// <motivation>
// For imaging and calibration you need to access an casacore::MS in some consistent
// order (by field, spectralwindow, time interval etc.). This class provides
// that access.
// </motivation>
//
// <thrown>
//    <li>
//    <li>
// </thrown>
//
// <todo asof="1997/05/30">
//   <li> cleanup the currently dual interface for visibilities and flags
//   <li> sort out what to do with weights when interpolating
// </todo>
class ROVisibilityIterator
{
    friend class AsyncEnabler;
    friend class VisibilityIteratorReadImpl;
    friend class ViReadImplAsync;
    friend class asyncio::VLAT; // allow VI lookahead thread class to access protected functions
                                // VLAT should not access private parts, especially variables
public:

  class Factory {

  public:

      virtual ~Factory () {}

      virtual VisibilityIteratorReadImpl *
      operator() (const asyncio::PrefetchColumns * /*prefetchColumns*/,
                  const casacore::Block<casacore::MeasurementSet>& /*mss*/,
                  const casacore::Block<casacore::Int>& /*sortColumns*/,
                  const casacore::Bool /*addDefaultSortCols*/,
                  casacore::Double /*timeInterval*/) const
      {
          return NULL;
      }
  };

  //# the following is a copy of the enum in MSIter
  //# can't think of a way to get one that known to the outside world from here
  enum PolFrame {
    // Circular polarization
    Circular=0,
    // Linear polarization
    Linear=1
  };

  typedef enum DataColumn {
      Observed=0,  // Observed data
      Model,       // Model data
      Corrected    // Corrected data
  } DataColumn;

  class AsyncEnabler {
  public:
      AsyncEnabler (ROVisibilityIterator &);
      AsyncEnabler (ROVisibilityIterator *);
      ~AsyncEnabler ();
      void release ();
  private:
      casacore::Bool oldEnabledState_p;
      ROVisibilityIterator * roVisibilityIterator_p;
  };

  // Default constructor - useful only to assign another iterator later
  ROVisibilityIterator();
  // Construct from an casacore::MS and a casacore::Block of casacore::MS column enums specifying the 
  // iteration order.  If no order is specified, it uses the default sort
  // order of MSIter, which is not necessarily the raw order of ms!
  // The default ordering is ARRAY_ID, FIELD_ID, DATA_DESC_ID,
  // and TIME, but check MSIter.h to be sure.
  // These columns will be added first if they are not specified.
  //
  // An optional timeInterval (in seconds) can be given to iterate through
  // chunks of time.  The default interval of 0 groups all times together.
  // Every 'chunk' of data contains all data within a certain time interval and
  // with identical values of the other iteration columns (e.g.  DATA_DESC_ID
  // and FIELD_ID).  Using selectChannel(), a number of groups of channels can
  // be requested.  At present the channel group iteration will always occur
  // before the interval iteration.
  ROVisibilityIterator(const casacore::MeasurementSet& ms, 
		       const casacore::Block<casacore::Int>& sortColumns,
		       casacore::Double timeInterval=0,
		       const Factory & factory = Factory());
  // Same as above, but with the option of using the raw order of ms
  // (addDefaultSortCols=false).
  ROVisibilityIterator(const casacore::MeasurementSet& ms, 
		       const casacore::Block<casacore::Int>& sortColumns,
		       const casacore::Bool addDefaultSortCols,
		       casacore::Double timeInterval=0);
 
  // Same as previous constructors, but with multiple MSs to iterate over.
  ROVisibilityIterator(const casacore::Block<casacore::MeasurementSet>& mss,
		       const casacore::Block<casacore::Int>& sortColumns, 
		       casacore::Double timeInterval=0);

  ROVisibilityIterator(const casacore::Block<casacore::MeasurementSet>& mss,
                       const casacore::Block<casacore::Int>& sortColumns,
                       const casacore::Bool addDefaultSortCols,
                       casacore::Double timeInterval=0);

  ROVisibilityIterator(const asyncio::PrefetchColumns * prefetchColumns,
                       const casacore::MeasurementSet& ms,
		       const casacore::Block<casacore::Int>& sortColumns,
		       const casacore::Bool addDefaultSortCols = true,
		       casacore::Double timeInterval = 0);

  ROVisibilityIterator (const asyncio::PrefetchColumns * prefetchColumns,
                        const casacore::Block<casacore::MeasurementSet>& mss,
                        const casacore::Block<casacore::Int>& sortColumns,
                        const casacore::Bool addDefaultSortCols = true,
                        casacore::Double timeInterval = 0);

  // Copy construct. This calls the assigment operator.
  ROVisibilityIterator(const ROVisibilityIterator & other);
  ROVisibilityIterator(const asyncio::PrefetchColumns * prefetchColumns, const ROVisibilityIterator & other);
  // Assigment. Any attached VisBuffers are lost in the assign.
  ROVisibilityIterator & operator=(const ROVisibilityIterator &other);
  // Destructor
  virtual ~ROVisibilityIterator();
  
//  static ROVisibilityIterator * create (const casacore::MeasurementSet & ms,
//                                        const casacore::Block<casacore::Int>& sortColumns,
//                                        const asyncio::PrefetchColumns * prefetchColumns = NULL,
//                                        const casacore::Bool addDefaultSortCols = true,
//                                        casacore::Double timeInterval=0);
//
//  static ROVisibilityIterator * create (const casacore::Block<casacore::MeasurementSet>& mss,
//                                        const casacore::Block<casacore::Int>& sortColumns,
//                                        const asyncio::PrefetchColumns * prefetchColumns = NULL,
//                                        const casacore::Bool addDefaultSortCols = true,
//                                        casacore::Double timeInterval=0);
//
//  static ROVisibilityIterator * create (const ROVisibilityIterator & rovi,
//                                        const asyncio::PrefetchColumns * prefetchColumns,
//                                        casacore::Int nReadAheadBuffers = 2);

  // Members
  
  casacore::Bool isAsynchronous () const;
  static casacore::Bool isAsynchronousIoEnabled();

  casacore::Bool isAsyncEnabled () const;
  casacore::Bool isWritable () const;
  // Reset iterator to origin/start of data (of current chunk)
  void origin();
  // Reset iterator to true start of data (first chunk)
  void originChunks();
 
  // Return the time interval (in seconds) used for iteration.
  // This is not the same as the INTERVAL column.
  casacore::Double getInterval() const;
  // Set or reset the time interval (in seconds) to use for iteration.
  // You should call originChunks() to reset the iteration after 
  // calling this.
  void setInterval(casacore::Double timeInterval);

  // Set the 'blocking' size for returning data.
  // With the default (0) only a single integration is returned at a time, this
  // is what is currently required for the calibration software. With blocking
  // set, up to nRows can be returned in one go. The chunk 
  // size determines the actual maximum.
  void setRowBlocking(casacore::Int nRows=0);
  // Return false if no more data (in current chunk)
  casacore::Bool more() const;
  SubChunkPair getSubchunkId () const;
  // Advance iterator through data
  ROVisibilityIterator & operator++(int);
  ROVisibilityIterator & operator++();
  // Return false if no more 'Chunks' of data left
  casacore::Bool moreChunks() const;
  // Check if ms has change since last iteration
  casacore::Bool newMS() const;
  casacore::Int msId() const;
  VisBuffer * getVisBuffer ();
  //reference to actual ms in interator 
  const casacore::MeasurementSet& ms() const;
 // Advance to the next Chunk of data
  ROVisibilityIterator& nextChunk();
  // Return antenna1
  casacore::Vector<casacore::Int>& antenna1(casacore::Vector<casacore::Int>& ant1) const;
  // Return antenna2
  casacore::Vector<casacore::Int>& antenna2(casacore::Vector<casacore::Int>& ant2) const;
  // Return feed1
  casacore::Vector<casacore::Int>& feed1(casacore::Vector<casacore::Int>& fd1) const;
  // Return feed2
  casacore::Vector<casacore::Int>& feed2(casacore::Vector<casacore::Int>& fd2) const;
  // Return channel numbers in selected VisSet spectrum
  // (i.e. disregarding possible selection on the iterator, but
  //  including the selection set when creating the VisSet)
  virtual casacore::Vector<casacore::Int>& channel(casacore::Vector<casacore::Int>& chan) const;
  // Return feed configuration matrix for specified antenna
  casacore::Vector<casacore::SquareMatrix<casacore::Complex,2> >& 
  CJones(casacore::Vector<casacore::SquareMatrix<casacore::Complex,2> >& cjones) const;
  // Return receptor angles for all antennae and feeds
  // First axis of the cube is a receptor number,
  // 2nd is antennaId, 3rd is feedId
  // Note: the method is intended to provide an access to MSIter::receptorAngles
  // for VisBuffer in the multi-feed case. It may be worth to change the
  // interface of feed_pa to return the information for all feeds.
  const casacore::Cube<casacore::Double>& receptorAngles() const;
  // return a string mount identifier for each antenna
  const casacore::Vector<casacore::String>& antennaMounts() const;
  // Return a cube containing pairs of coordinate offsets for each
  // receptor of each feed (values are in radians, coordinate system is fixed
  // with antenna and is the same one as used to define the BEAM_OFFSET 
  // parameter in the feed table). The cube axes are receptor, antenna, feed.
  const casacore::Cube<casacore::RigidVector<casacore::Double, 2> >& getBeamOffsets() const;
  // true if all elements of the cube returned by getBeamOffsets are zero
  casacore::Bool allBeamOffsetsZero() const;
  // Return feed parallactic angles casacore::Vector(nant) (1 feed/ant)
  casacore::Vector<casacore::Float> feed_pa(casacore::Double time) const;
  static casacore::Vector<casacore::Float> feed_paCalculate(casacore::Double time, casacore::MSDerivedValues & msd,
  									    casacore::Int nAntennas, const casacore::MEpoch & mEpoch0,
									    const casacore::Vector<casacore::Float> & receptor0Angle);
  // Return nominal parallactic angle at specified time
  // (does not include feed position angle offset--see feed_pa)
  // A global value for all antennas (e.g., small array)
  const casacore::Float& parang0(casacore::Double time) const;
  static casacore::Float parang0Calculate (casacore::Double time, casacore::MSDerivedValues & msd, const casacore::MEpoch & epoch0);
  // Per antenna:
  casacore::Vector<casacore::Float> parang(casacore::Double time) const;
  static casacore::Vector<casacore::Float> parangCalculate (casacore::Double time, casacore::MSDerivedValues & msd,
		                                int nAntennas, const casacore::MEpoch mEpoch0);
  // Return the antenna AZ/EL casacore::Vector(nant) 
  casacore::MDirection azel0(casacore::Double time) const;
  static void azel0Calculate (casacore::Double time, casacore::MSDerivedValues & msd,
		                      casacore::MDirection & azel0, const casacore::MEpoch & mEpoch0);
  casacore::Vector<casacore::MDirection> azel(casacore::Double time) const;
  static void azelCalculate (casacore::Double time, casacore::MSDerivedValues & msd, casacore::Vector<casacore::MDirection> & azel,
		                     casacore::Int nAnt, const casacore::MEpoch & mEpoch0);
  // Return the hour angle for the specified time
  casacore::Double hourang(casacore::Double time) const;
  static casacore::Double hourangCalculate (casacore::Double time, casacore::MSDerivedValues & msd, const casacore::MEpoch & mEpoch0);
  // Return the current FieldId
  casacore::Int fieldId() const;
  // Return the current ArrayId
  casacore::Int arrayId() const;
  // Return the current Field Name
  casacore::String fieldName() const;
  // Return the current Source Name
  casacore::String sourceName() const;
  // Return flag for each polarization, channel and row
  virtual casacore::Cube<casacore::Bool>& flag(casacore::Cube<casacore::Bool>& flags) const;
  // Return flag for each channel & row
  casacore::Matrix<casacore::Bool>& flag(casacore::Matrix<casacore::Bool>& flags) const;

  // Determine whether FLAG_CATEGORY is valid.
  casacore::Bool existsFlagCategory() const;

  // Return flags for each polarization, channel, category, and row.
  casacore::Array<casacore::Bool>& flagCategory(casacore::Array<casacore::Bool>& flagCategories) const;
  // Return row flag
  casacore::Vector<casacore::Bool>& flagRow(casacore::Vector<casacore::Bool>& rowflags) const;
  // Return scan number
  casacore::Vector<casacore::Int>& scan(casacore::Vector<casacore::Int>& scans) const;
  // Return the OBSERVATION_IDs
  casacore::Vector<casacore::Int>& observationId(casacore::Vector<casacore::Int>& obsids) const;
  // Return the PROCESSOR_IDs
  casacore::Vector<casacore::Int>& processorId(casacore::Vector<casacore::Int>& procids) const;
  // Return the STATE_IDs
  casacore::Vector<casacore::Int>& stateId(casacore::Vector<casacore::Int>& stateids) const;
  // Return current frequencies (in Hz, acc. to the casacore::MS def'n v.2)
  virtual casacore::Vector<casacore::Double>& frequency(casacore::Vector<casacore::Double>& freq) const;
  // Return frequencies  (in Hz, acc. to the casacore::MS def'n v.2) in selected velocity frame,
  // returns the same as frequency() if there is no vel selection active.
  casacore::Vector<casacore::Double>& lsrFrequency(casacore::Vector<casacore::Double>& freq) const;
  // Return the current phase center as an MDirection
  const casacore::MDirection& phaseCenter() const;
  const casacore::MDirection phaseCenter(const casacore::Int fieldid, const casacore::Double time=-1.0) const;
  // Return frame for polarization (returns PolFrame enum)
  casacore::Int polFrame() const;
  // Return the correlation type (returns casacore::Stokes enums)
  virtual casacore::Vector<casacore::Int>& corrType(casacore::Vector<casacore::Int>& corrTypes) const;
  // Return sigma
  casacore::Vector<casacore::Float>& sigma(casacore::Vector<casacore::Float>& sig) const;
  // Return sigma matrix (pol-dep)
  virtual casacore::Matrix<casacore::Float>& sigmaMat(casacore::Matrix<casacore::Float>& sigmat) const;
  // Return current SpectralWindow
  casacore::Int spectralWindow() const;
  // Return current Polarization Id
  casacore::Int polarizationId() const;
  // Return current DataDescription Id
  casacore::Int dataDescriptionId() const;
  // Return MJD midpoint of interval.
  casacore::Vector<casacore::Double>& time(casacore::Vector<casacore::Double>& t) const;
  // Return MJD centroid of interval.
  casacore::Vector<casacore::Double>& timeCentroid(casacore::Vector<casacore::Double>& t) const;
  // Return nominal time interval
  casacore::Vector<casacore::Double>& timeInterval(casacore::Vector<casacore::Double>& ti) const;
  // Return actual time interval
  casacore::Vector<casacore::Double>& exposure(casacore::Vector<casacore::Double>& expo) const;
  // Return the visibilities as found in the casacore::MS, casacore::Cube(npol,nchan,nrow).
  virtual casacore::Cube<casacore::Complex>& visibility(casacore::Cube<casacore::Complex>& vis,
				    DataColumn whichOne) const;
  // Return FLOAT_DATA as a casacore::Cube(npol, nchan, nrow) if found in the MS.
  casacore::Cube<casacore::Float>& floatData(casacore::Cube<casacore::Float>& fcube) const;
  // Return the visibility 4-vector of polarizations for each channel.
  // If the casacore::MS doesn't contain all polarizations, it is assumed it
  // contains one or two parallel hand polarizations.
  casacore::Matrix<CStokesVector>& visibility(casacore::Matrix<CStokesVector>& vis, 
				    DataColumn whichOne) const;
  // Return the shape of the visibility Cube
  casacore::IPosition visibilityShape() const;
  // Return u,v and w (in meters)
  virtual casacore::Vector<casacore::RigidVector<casacore::Double,3> >& uvw(casacore::Vector<casacore::RigidVector<casacore::Double,3> >& uvwvec) const;
  casacore::Matrix<casacore::Double>& uvwMat(casacore::Matrix<casacore::Double>& uvwmat) const;
  // Return weight
  casacore::Vector<casacore::Float>& weight(casacore::Vector<casacore::Float>& wt) const;
  // Returns the nPol_p x curNumRow_p weight matrix
  virtual casacore::Matrix<casacore::Float>& weightMat(casacore::Matrix<casacore::Float>& wtmat) const;
  // Determine whether WEIGHT_SPECTRUM exists.
  casacore::Bool existsWeightSpectrum() const;
  // Return weightspectrum (a weight for each channel)
  casacore::Cube<casacore::Float>& weightSpectrum(casacore::Cube<casacore::Float>& wtsp) const;
  // Return imaging weight (a weight for each channel)
  //casacore::Matrix<casacore::Float>& imagingWeight(casacore::Matrix<casacore::Float>& wt) const;
  // Return true if FieldId/Source has changed since last iteration
  casacore::Bool newFieldId() const;
  // Return true if arrayID has changed since last iteration
  casacore::Bool newArrayId() const;
  // Return true if SpectralWindow has changed since last iteration
  casacore::Bool newSpectralWindow() const;
  // Return the index of the first channel of the current channel group 
  // in the total (selected) spectrum.
  casacore::Int channelIndex() const;
  // Return the width of the current group of channels, i.e.,
  // the number of channels returned by visibility() and frequency().
  casacore::Int channelGroupSize() const;
  
  // Return the number of correlations in the current iteration
  casacore::Int nCorr() const;;
  // Return the number of rows in the current iteration
  casacore::Int nRow() const;
  // Return the row ids as from the original root table. This is useful 
  // to find correspondance between a given row in this iteration to the 
  // original ms row
  virtual casacore::Vector<casacore::rownr_t>& rowIds(casacore::Vector<casacore::rownr_t>& rowids) const;
  // Return the numbers of rows in the current chunk
  casacore::Int nRowChunk() const;
  // Return the number of sub-intervals in the current chunk
  casacore::Int nSubInterval() const;
  // Call to use the slurp i/o method for all scalar columns. This
  // will set the casacore::BucketCache cache size to the full column length
  // and cause the full column to be cached in memory, if
  // any value of the column is used. In case of out-of-memory,
  // it will automatically fall-back on the smaller cache size.
  // Slurping the column is to be considered as a work-around for the
  // casacore::Table i/o code, which uses casacore::BucketCache and performs extremely bad
  // for random access. Slurping is useful when iterating non-sequentially
  // an casacore::MS or parts of an casacore::MS, it is not tested with multiple MSs.
  void slurp() const;
  // Velocity selection - specify the output channels in velocity:
  // nChan - number of output channels, vStart - start velocity,
  // vInc - velocity increment. So channel i will have velocity 
  // vStart + i*vInc (i=0,nChan-1).
  // Specify velocities as in e.g., casacore::MVRadialVelocity(casacore::Quantity(2001.,"km/s")).
  // The reference type and velocity definition are specified separately.
  // Note that no averaging is performed, the visibilities will be interpolated
  // and sampled at the specified velocities, it's up to you to choose a vInc
  // appropriate to the channel width.
  // The REST_FREQUENCY column in the SPECTRAL_WINDOW subtable is used to
  // determine the velocity-frequency conversion.
  // By default calculations are done for a single velocity with offsets 
  // applied for the others (ok for non-rel velocities with RADIO defn), 
  // set precise to true to do a full conversion for each output channel.(NYI)
  ROVisibilityIterator& 
  selectVelocity(casacore::Int nChan, 
		 const casacore::MVRadialVelocity& vStart, const casacore::MVRadialVelocity& vInc,
		 casacore::MRadialVelocity::Types rvType = casacore::MRadialVelocity::LSR,
		 casacore::MDoppler::Types dType = casacore::MDoppler::RADIO, casacore::Bool precise=false);
  // Select the velocity interpolation scheme.
  // At present the choice is limited to : nearest and linear, linear
  // is the default. 
  // TODO: add cubic, spline and possibly FFT
  ROVisibilityIterator& velInterpolation(const casacore::String& type);
  // Channel selection - only the selected channels will be returned by the
  // access functions. The default spectralWindow is the current one (or 0)
  // This allows selection of the input channels, producing
  // nGroup groups of width output channels. Default is to return all channels
  // in a single group.
  ROVisibilityIterator& selectChannel(casacore::Int nGroup=1, casacore::Int start=0, casacore::Int width=0, 
				      casacore::Int increment=1, casacore::Int spectralWindow=-1);
  //Same as above except when multiple ms's are to be accessed
  ROVisibilityIterator& selectChannel(casacore::Block< casacore::Vector<casacore::Int> >& blockNGroup,
				      casacore::Block< casacore::Vector<casacore::Int> >& blockStart,
				      casacore::Block< casacore::Vector<casacore::Int> >& blockWidth,
				      casacore::Block< casacore::Vector<casacore::Int> >& blockIncr,
				      casacore::Block< casacore::Vector<casacore::Int> >& blockSpw);
  //get the channel selection ...the block over the number of ms's associated
  // with this iterator
  void getChannelSelection(casacore::Block< casacore::Vector<casacore::Int> >& blockNGroup,
			   casacore::Block< casacore::Vector<casacore::Int> >& blockStart,
			   casacore::Block< casacore::Vector<casacore::Int> >& blockWidth,
			   casacore::Block< casacore::Vector<casacore::Int> >& blockIncr,
			   casacore::Block< casacore::Vector<casacore::Int> >& blockSpw);
  // Translate slicesv from the form returned by casacore::MSSelection::getChanSlices()
  // to matv as used by setChanAveBounds().  widthsv is the channel averaging
  // width for each _selected_ spw.
  void slicesToMatrices(casacore::Vector<casacore::Matrix<casacore::Int> >& matv,
                        const casacore::Vector<casacore::Vector<casacore::Slice> >& slicesv,
                        const casacore::Vector<casacore::Int>& widthsv) const;
  // Get the spw, start  and nchan for all the ms's is this Visiter that 
  // match the frequecy "freqstart-freqStep" and "freqEnd+freqStep" range
  // Can help in doing channel selection above..
  // freqFrame is the frame the caller frequency values are in (freqStart and freqEnd)
  // These will be converted to the frame of the selected spw to match
  void getSpwInFreqRange(casacore::Block<casacore::Vector<casacore::Int> >& spw, 
				 casacore::Block<casacore::Vector<casacore::Int> >& start, 
				 casacore::Block<casacore::Vector<casacore::Int> >& nchan, 
				 casacore::Double freqStart, casacore::Double freqEnd, 
				 casacore::Double freqStep, casacore::MFrequency::Types freqFrame=casacore::MFrequency::LSRK);

  //Get the frequency range of the data selection
  void getFreqInSpwRange(casacore::Double& freqStart, casacore::Double& freqEnd, casacore::MFrequency::Types freqframe = casacore::MFrequency::LSRK);

  // Attach a VisBuffer object.
  // Note that while more than one VisBuffer may be attached, only the
  // last one is actively updated. A casacore::Stack is kept internally, so after 
  // a detach, the previous VisBuffer becomes active again.
  void attachVisBuffer(VisBuffer& vb);
  // Detach a VisBuffer object.
  // If the object detached is not the last one attached an exception
  // is thrown.
  void detachVisBuffer(VisBuffer& vb);
  // Access the current casacore::MSColumns object in MSIter
  const casacore::MSColumns& msColumns() const;
  // get back the selected spectral windows and spectral channels for
  // current ms 
  void allSelectedSpectralWindows(casacore::Vector<casacore::Int>& spws, casacore::Vector<casacore::Int>& nvischan);
  // Convert the frequency from the observe frame to lsr frame.
  // Returns true in convert if given spw was not observed 
  // in the LSRK frame
  //when ignoreconv=true...no conversion is done from data frame
  //frequency is served as is
  void lsrFrequency(const casacore::Int& spw, casacore::Vector<casacore::Double>& freq, casacore::Bool& convert, const  casacore::Bool ignoreconv=false);
  //assign a VisImagingWeight object to this iterator
  void useImagingWeight(const VisImagingWeight& imWgt);
  const VisImagingWeight & getImagingWeightGenerator () const;

  //return number  of Ant 
  casacore::Int numberAnt();
  //Return number of rows in all selected ms's
  casacore::Int numberCoh();
  // Return number of spws, polids, ddids
  casacore::Int numberSpw();
  casacore::Int numberPol();
  casacore::Int numberDDId();
//  casacore::ArrayColumn <casacore::Double> & getChannelFrequency () const;
//  casacore::Block<casacore::Int> getChannelGroupNumber () const;
//  casacore::Block<casacore::Int> getChannelIncrement () const;
//  casacore::Block<casacore::Int> getChannelStart () const;
//  casacore::Block<casacore::Int> getChannelWidth () const;
  casacore::Int getDataDescriptionId () const;
  const casacore::MeasurementSet & getMeasurementSet() const;;
  casacore::Int getMeasurementSetId() const;
  casacore::Int getNAntennas () const;
  casacore::MEpoch getEpoch () const;
  ///////////////casacore::MFrequency::Types getObservatoryFrequencyType () const; //???
  ///////////////casacore::MPosition getObservatoryPosition () const;
  ///////////////casacore::MDirection getPhaseCenter () const;
  casacore::Vector<casacore::Float> getReceptor0Angle ();
  casacore::Vector<casacore::rownr_t> getRowIds () const;

  // Returns the columns that the VisibilityIterator is sorting by.
  const casacore::Block<casacore::Int>& getSortColumns() const;

  static void lsrFrequency (const casacore::Int& spw,
                            casacore::Vector<casacore::Double>& freq,
                            casacore::Bool & convert,
                            const casacore::Block<casacore::Int> & chanStart,
                            const casacore::Block<casacore::Int> & chanWidth,
                            const casacore::Block<casacore::Int> & chanInc,
                            const casacore::Block<casacore::Int> & numChanGroup,
                            const casacore::ArrayColumn <casacore::Double> & chanFreqs,
                            const casacore::ScalarColumn<casacore::Int> & obsMFreqTypes,
                            const casacore::MEpoch & ep,
                            const casacore::MPosition & obsPos,
                            const casacore::MDirection & dir);

  static casacore::String getAipsRcBase () { return "VisibilityIterator";}
  static casacore::String getAsyncRcBase () { return getAipsRcBase() + ".async";}

protected:

  ROVisibilityIterator (const asyncio::PrefetchColumns * prefetchColumns,
                        const casacore::Block<casacore::MeasurementSet>& mss,
                        const casacore::Block<casacore::Int>& sortColumns,
                        const casacore::Bool addDefaultSortCols,
                        casacore::Double timeInterval,
                        casacore::Bool writable);

  void construct (const asyncio::PrefetchColumns * prefetchColumns,
                  const casacore::Block<casacore::MeasurementSet>& mss,
                  const casacore::Block<casacore::Int>& sortColumns,
                  const casacore::Bool addDefaultSortCols,
                  casacore::Double timeInterval,
                  casacore::Bool writable,
                  const Factory & factory);

  VisibilityIteratorReadImpl * getReadImpl() const;

  bool existsColumn (VisBufferComponents::EnumType id) const;
  // advance the iteration
  virtual void advance();
  // set the currently selected table
  virtual void setSelTable();
  // set the iteration state
  void setState();
  // get the TOPO frequencies from the selected velocities and the obs. vel.
  void getTopoFreqs();
  void getTopoFreqs(casacore::Vector<casacore::Double> & lsrFreq, casacore::Vector<casacore::Double> & selFreq); // for async i/o
  void getLsrInfo (casacore::Block<casacore::Int> & channelGroupNumber,
                           casacore::Block<casacore::Int> & channelIncrement,
                           casacore::Block<casacore::Int> & channelStart,
                           casacore::Block<casacore::Int> & channelWidth,
                           casacore::MPosition & observatoryPositon,
                           casacore::MDirection & phaseCenter,
                           casacore::Bool & velocitySelection) const;
  std::vector<casacore::MeasurementSet> getMeasurementSets () const;
  const casacore::MSDerivedValues & getMSD () const; // for use by Async I/O *ONLY*
  // update the DATA slicer
  virtual void updateSlicer();
  // attach the column objects to the currently selected table
  virtual void attachColumns(const casacore::Table &t);
  // returns the table, to which columns are attached, 
  // can be overridden in derived classes
  virtual const casacore::Table attachTable() const;
  // get the (velocity selected) interpolated visibilities, flags and weights.
  // It is not really const at all (it seems to use This-> trickery so callers
  // like flag() can be declared const).
//  void getInterpolatedVisFlagWeight(DataColumn whichOne) const;
  // get the (velocity selected) interpolated FLOAT_DATA (as real Floats),
  // flags and weights.
//  void getInterpolatedFloatDataFlagWeight() const;
  // get the visibility data (observed, corrected or model);
  // deals with casacore::Float and casacore::Complex observed data (DATA or FLOAT_DATA)
  void getDataColumn(DataColumn whichOne, const casacore::Slicer& slicer, 
			     casacore::Cube<casacore::Complex>& data) const;
  void getDataColumn(DataColumn whichOne, casacore::Cube<casacore::Complex>& data) const;
  // get FLOAT_DATA as real Floats.
  void getFloatDataColumn(const casacore::Slicer& slicer, casacore::Cube<casacore::Float>& data) const;
  void getFloatDataColumn(casacore::Cube<casacore::Float>& data) const;

  void originChunks(casacore::Bool forceRewind);
  //Re-Do the channel selection in multi ms case 
  void doChannelSelection();
  //Set the tile cache size....when using slice access if tile cache size is 
  // not set memory usage can go wild.  Specifically, the caching scheme is
  // ephemeral and lives for that instance of setting the caching scheme.
  // 
  // If you don't set any then the defaults come into play and caches a few
  // tiles along every axis at the tile you requested...which is a waste when
  // say you know you want to proceed along the row axis for example...and in
  // fact now VisIter just reads one tile (thus the commenting in setTileCache)
  // and lets the OS do the caching rather than than having the table system
  // cache extra tiles.
  virtual void setTileCache();
  //Check if spw is in selected SPW for actual ms
  casacore::Bool isInSelectedSPW(const casacore::Int& spw);
  // Updates, if necessary, rowIds_p member for the current chunk
  void update_rowIds() const;
  void setAsyncEnabled (casacore::Bool enable);
  template<class T>
    void getColScalar(const casacore::ScalarColumn<T> &column, casacore::Vector<T> &array, casacore::Bool resize) const;
  template<class T>
    void getColArray(const casacore::ArrayColumn<T> &column, casacore::Array<T> &array, casacore::Bool resize) const;
  // column access functions, can be overridden in derived classes
  virtual void getCol(const casacore::ScalarColumn<casacore::Bool> &column, casacore::Vector<casacore::Bool> &array, casacore::Bool resize = false) const;
  virtual void getCol(const casacore::ScalarColumn<casacore::Int> &column, casacore::Vector<casacore::Int> &array, casacore::Bool resize = false) const;
  virtual void getCol(const casacore::ScalarColumn<casacore::Double> &column, casacore::Vector<casacore::Double> &array, casacore::Bool resize = false) const;
  virtual void getCol(const casacore::ArrayColumn<casacore::Bool> &column, casacore::Array<casacore::Bool> &array, casacore::Bool resize = false) const;
  virtual void getCol(const casacore::ArrayColumn<casacore::Float> &column, casacore::Array<casacore::Float> &array, casacore::Bool resize = false) const;
  virtual void getCol(const casacore::ArrayColumn<casacore::Double> &column, casacore::Array<casacore::Double> &array, casacore::Bool resize = false) const;
  virtual void getCol(const casacore::ArrayColumn<casacore::Complex> &column, casacore::Array<casacore::Complex> &array, casacore::Bool resize = false) const;
  virtual void getCol(const casacore::ArrayColumn<casacore::Bool> &column, const casacore::Slicer &slicer, casacore::Array<casacore::Bool> &array, casacore::Bool resize = false) const;
  virtual void getCol(const casacore::ArrayColumn<casacore::Float> &column, const casacore::Slicer &slicer, casacore::Array<casacore::Float> &array, casacore::Bool resize = false) const;
  virtual void getCol(const casacore::ArrayColumn<casacore::Complex> &column, const casacore::Slicer &slicer, casacore::Array<casacore::Complex> &array, casacore::Bool resize = false) const;
  //  void getCol(const casacore::String &colName, casacore::Array<casacore::Double> &array,
  //                      casacore::Array<casacore::Double> &all, casacore::Bool resize = false) const;
  //  void getCol(const casacore::String &colName, casacore::Vector<casacore::Bool> &array,
  //                      casacore::Vector<casacore::Bool> &all, casacore::Bool resize = false) const;
  //  void getCol(const casacore::String &colName, casacore::Vector<casacore::Int> &array,
  //                      casacore::Vector<casacore::Int> &all, casacore::Bool resize = false) const;
  //  void getCol(const casacore::String &colName, casacore::Vector<casacore::Double> &array,
  //                      casacore::Vector<casacore::Double> &all, casacore::Bool resize = false) const;
  template<class T>
  void swapyz(casacore::Cube<T>& out, const casacore::Cube<T>& in) const;

  VisibilityIteratorReadImpl * readImpl_p;
};


// <summary>
// VisibilityIterator iterates through one or more writable MeasurementSets
// </summary>
// <use visibility=export>
// <reviewed reviewer="" date="yyyy/mm/dd" tests="" demos="">
// </reviewed>
// <prerequisite>
//   <li> <linkto class="ROVisibilityIterator">ROVisibilityIterator</linkto>
// </prerequisite>
//
// <etymology>
// The VisibilityIterator is a read/write iterator returning visibilities
// </etymology>
//
// <synopsis>
// VisibilityIterator provides iteration with various sort orders
// for one or more MSs. It has member functions to retrieve the fields
// commonly needed in synthesis calibration and imaging. It is 
// derived from the read-only iterator
// <linkto class="ROVisibilityIterator">ROVisibilityIterator</linkto>.
//
// One should use <linkto class="VisBuffer">VisBuffer</linkto>
// to access chunks of data.
//
// As with an ROVI, a VI can be created to only utilize synchronous I/O
// (the "normal" way) or it can potentially use asynchronous I/O.  To create
// an asynchronous instance of VI requires using an appropriate constructor
// (i.e., one that has PrefetchColumns * as its first argument) and setting
// the CASARC setting "VisibilityIterator.async.disabled: false".
//
// When a write operation is performed using the various setX methods of an
// asynchronous instance of this class, the data is shipped over to the VLAT
// which writes out when it is not in the middle of performing a read ahead
// of a VisBuffer.
//
// </synopsis>
//
// <example>
// <code>
// //
// </code>
// </example>
//
// <motivation>
// For imaging and calibration you need to access an casacore::MS in some consistent
// order (by field, spectralwindow, time interval etc.). This class provides
// that access.
// </motivation>
//
// #<thrown>
//
// #</thrown>
//
// <todo asof="1997/05/30">
//   <li> cleanup the currently dual interface for visibilities and flags
//   <li> sort out what to do with weights when interpolating
// </todo>
class VisibilityIterator : public ROVisibilityIterator
{

    friend class VisibilityIteratorWriteImpl;

public:
  // Constructors.
  // Note: The VisibilityIterator is not initialized correctly by default, you
  // need to call origin() before using it to iterate.
  VisibilityIterator();
  VisibilityIterator(casacore::MeasurementSet & ms, const casacore::Block<casacore::Int>& sortColumns, 
       casacore::Double timeInterval=0);
  VisibilityIterator(casacore::MeasurementSet & ms, const casacore::Block<casacore::Int>& sortColumns, 
		     const casacore::Bool addDefaultSortCols,
		     casacore::Double timeInterval=0);
  // Same as previous constructor, but with multiple MSs to iterate over.
  VisibilityIterator(const casacore::Block<casacore::MeasurementSet>& mss,
		       const casacore::Block<casacore::Int>& sortColumns, 
		       casacore::Double timeInterval=0);
  VisibilityIterator(const casacore::Block<casacore::MeasurementSet>& mss,
		     const casacore::Block<casacore::Int>& sortColumns, const casacore::Bool addDefaultSortCols, 
		       casacore::Double timeInterval=0);
  VisibilityIterator (const asyncio::PrefetchColumns * prefetchColumns,
                      const casacore::Block<casacore::MeasurementSet>& mss,
                      const casacore::Block<casacore::Int>& sortColumns,
                      const casacore::Bool addDefaultSortCols = true,
                      casacore::Double timeInterval = 0);
  VisibilityIterator (const asyncio::PrefetchColumns * prefetchColumns,
                      casacore::MeasurementSet & mss,
                      const casacore::Block<casacore::Int>& sortColumns,
                      const casacore::Bool addDefaultSortCols = true,
                      casacore::Double timeInterval = 0);
  VisibilityIterator(const VisibilityIterator & MSI);
  VisibilityIterator(const asyncio::PrefetchColumns * prefetchColumns, const VisibilityIterator & other);

  
  virtual ~VisibilityIterator();

  VisibilityIterator & operator=(const VisibilityIterator &MSI);


  // Members
  
  casacore::Bool isWritable () const;

  // Advance iterator through data
  VisibilityIterator & operator++(int);
  VisibilityIterator & operator++();
  // Set/modify the flags in the data.
  // This will flag all channels in the original data that contributed to
  // the output channel in the case of channel averaging.
  // All polarizations have the same flag value.
  void setFlag(const casacore::Matrix<casacore::Bool>& flag);
  // Set/modify the flags in the data.
  // This sets the flags as found in the casacore::MS, casacore::Cube(npol,nchan,nrow),
  // where nrow is the number of rows in the current iteration (given by
  // nRow()).
  virtual void setFlag(const casacore::Cube<casacore::Bool>& flag);
  // Set/modify the flag row column; dimension casacore::Vector(nrow)
  virtual void setFlagRow(const casacore::Vector<casacore::Bool>& rowflags);

  void setFlagCategory(const casacore::Array<casacore::Bool>& fc);

  // Set/modify the visibilities.
  // This is possibly only for a 'reference' casacore::MS which has a new DATA column.
  // The first axis of the matrix should equal the selected number of channels
  // in the original MS.
  // If the casacore::MS does not contain all polarizations, only the parallel
  // hand polarizations are used.
  void setVis(const casacore::Matrix<CStokesVector>& vis, DataColumn whichOne);
  // Set/modify the visibilities
  // This sets the data as found in the casacore::MS, casacore::Cube(npol,nchan,nrow).
  virtual void setVis(const casacore::Cube<casacore::Complex>& vis, DataColumn whichOne);
  // Set the visibility and flags, and interpolate from velocities if needed
  virtual void setVisAndFlag(const casacore::Cube<casacore::Complex>& vis, const casacore::Cube<casacore::Bool>& flag,
	   	             DataColumn whichOne);
  // Set/modify the weights
  void setWeight(const casacore::Vector<casacore::Float>& wt);
  // Set/modify the weightMat
  virtual void setWeightMat(const casacore::Matrix<casacore::Float>& wtmat);
  // Set/modify the weightSpectrum
  virtual void setWeightSpectrum(const casacore::Cube<casacore::Float>& wtsp);
  // Set/modify the Sigma
  void setSigma(const casacore::Vector<casacore::Float>& sig);
  // Set/modify the ncorr x nrow SigmaMat.
  void setSigmaMat(const casacore::Matrix<casacore::Float>& sigmat);
  
  //This puts a model into the descriptor of the current ms in the iterator
  //Set iscomponentlist to true if the record represent a componentlist
  //if false then it is a FTMachine casacore::Record that holds the model image 
  //note the spw and fields selected are going to be associated with this model
  //incremetal =true implies add the model to previous any existant model 
  //in the ms for the spw and fields 
  //false means any existant model will be replaces.
  void putModel(const casacore::RecordInterface& rec, casacore::Bool iscomponentlist=true, casacore::Bool incremental=false);


  void writeBack (VisBuffer *);

protected:

  virtual void attachColumns(const casacore::Table &t);

  void construct ();


  VisibilityIteratorWriteImpl * getWriteImpl() const;

  void initializeBackWriters ();

  VisibilityIteratorWriteImpl * writeImpl_p;
};

} //# NAMESPACE CASA - END

#ifndef AIPS_NO_TEMPLATE_SRC
#include <msvis/MSVis/VisibilityIterator.tcc>
#endif //# AIPS_NO_TEMPLATE_SRC

#endif