/// @file
///
/// Provides utility functions for simulations package
///
/// @copyright (c) 2007 CSIRO
/// Australia Telescope National Facility (ATNF)
/// Commonwealth Scientific and Industrial Research Organisation (CSIRO)
/// PO Box 76, Epping NSW 1710, Australia
/// atnf-enquiries@csiro.au
///
/// This file is part of the ASKAP software distribution.
///
/// The ASKAP software distribution is free software: you can redistribute it
/// and/or modify it under the terms of the GNU General Public License as
/// published by the Free Software Foundation; either version 2 of the License,
/// or (at your option) any later version.
///
/// This program is distributed in the hope that it will be useful,
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
/// GNU General Public License for more details.
///
/// You should have received a copy of the GNU General Public License
/// along with this program; if not, write to the Free Software
/// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
///
/// @author Matthew Whiting <matthew.whiting@csiro.au>
///
#include <askap_simulations.h>

#include <simulationutilities/SimulationUtilities.h>
#include <simulationutilities/FluxGenerator.h>
#include <mathsutils/MathsUtils.h>
#include <askap/AskapLogging.h>
#include <askap/AskapError.h>

#include <casacore/scimath/Functionals/Gaussian1D.h>
#include <casacore/scimath/Functionals/Gaussian2D.h>
#include <casacore/scimath/Functionals/Gaussian3D.h>
#include <casacore/casa/namespace.h>
#include <boost/shared_ptr.hpp>

#include <wcslib/wcs.h>
#include <wcslib/wcsunits.h>
#include <wcslib/wcsfix.h>
#define WCSLIB_GETWCSTAB // define this so that we don't try to redefine wtbarr
// (this is a problem when using wcslib-4.2)

#include <Common/ParameterSet.h>

#include <duchamp/Utils/Section.hh>

#include <iostream>
#include <iomanip>
#include <fstream>
#include <sstream>
#include <vector>
#include <utility>
#include <string>
#include <stdlib.h>
#include <math.h>

ASKAP_LOGGER(logger, ".simutils");

namespace askap {

namespace simulations {

const std::string locationString(duchamp::Section &subsection)
{

    std::stringstream locationString;
    locationString << "_";
    for (uint i = 0; i < subsection.getStartList().size(); i++) {
        locationString << "_" << subsection.getStart(i);
    }
    locationString << "__";

    return locationString.str();
}

struct wcsprm *parsetToWCS(const LOFAR::ParameterSet& theParset,
                           const std::vector<unsigned int> &theAxes,
                           const float &theEquinox,
                           const float &theRestFreq,
                           duchamp::Section &theSection)
{

    const unsigned int theDim = theAxes.size();

    struct wcsprm *wcs = (struct wcsprm *)calloc(1, sizeof(struct wcsprm));
    wcs->flag = -1;
    int status = wcsini(true , theDim, wcs);
    ASKAPCHECK(status == 0, "WCSINI returned non-zero result - " << status <<
               " = " << wcs_errmsg[status]);
    wcs->flag = 0;
    std::vector<std::string> ctype, cunit;
    std::vector<float> crval, crpix, cdelt, crota;
    ctype = theParset.getStringVector("ctype");

    if (ctype.size() != theDim)
        ASKAPTHROW(AskapError, "Dimension mismatch: dim = " << theDim <<
                   ", but ctype has " << ctype.size() << " dimensions.");

    cunit = theParset.getStringVector("cunit");

    if (cunit.size() != theDim)
        ASKAPTHROW(AskapError, "Dimension mismatch: dim = " << theDim <<
                   ", but cunit has " << cunit.size() << " dimensions.");

    crval = theParset.getFloatVector("crval");

    if (crval.size() != theDim)
        ASKAPTHROW(AskapError, "Dimension mismatch: dim = " << theDim <<
                   ", but crval has " << crval.size() << " dimensions.");

    crpix = theParset.getFloatVector("crpix");

    if (crpix.size() != theDim)
        ASKAPTHROW(AskapError, "Dimension mismatch: dim = " << theDim <<
                   ", but crpix has " << crpix.size() << " dimensions.");

    cdelt = theParset.getFloatVector("cdelt");

    if (cdelt.size() != theDim)
        ASKAPTHROW(AskapError, "Dimension mismatch: dim = " << theDim <<
                   ", but cdelt has " << cdelt.size() << " dimensions.");

    crota = theParset.getFloatVector("crota");

    if (crota.size() != theDim)
        ASKAPTHROW(AskapError, "Dimension mismatch: dim = " << theDim <<
                   ", but crota has " << crota.size() << " dimensions.");

    for (uint i = 0; i < theDim; i++) {
        wcs->crpix[i] = crpix[i] - theSection.getStart(i) + 1;
        wcs->cdelt[i] = cdelt[i];
        wcs->crval[i] = crval[i];
        wcs->crota[i] = crota[i];
        strcpy(wcs->cunit[i], cunit[i].c_str());
        strcpy(wcs->ctype[i], ctype[i].c_str());
    }

    wcs->equinox = theEquinox;
    if (theRestFreq > 0.) wcs->restfrq = theRestFreq;
    else wcs->restfrq = 0.;
    wcs->restwav = 0.;
    wcsset(wcs);

    int stat[NWCSFIX];
    int *axes = new int[theDim];

    for (uint i = 0; i < theDim; i++) axes[i] = theAxes[i];
    wcsfix(1, (const int*)axes, wcs, stat);
    wcsset(wcs);

    delete [] axes;

    return wcs;
    // int nwcs = 1;

    // if (isImage) {
    //   if (this->itsWCSAllocated) wcsvfree(&nwcs, &this->itsWCS);

    //   this->itsWCS = (struct wcsprm *)calloc(1, sizeof(struct wcsprm));
    //   this->itsWCSAllocated = true;
    //   this->itsWCS->flag = -1;
    //   wcsini(true, wcs->naxis, this->itsWCS);
    //   wcsfix(1, (const int*)axes, wcs, stat);
    //   wcscopy(true, wcs, this->itsWCS);
    //   wcsset(this->itsWCS);
    // } else {
    //   if (this->itsWCSsourcesAllocated)  wcsvfree(&nwcs, &this->itsWCSsources);

    //   this->itsWCSsources = (struct wcsprm *)calloc(1, sizeof(struct wcsprm));
    //   this->itsWCSsourcesAllocated = true;
    //   this->itsWCSsources->flag = -1;
    //   wcsini(true, wcs->naxis, this->itsWCSsources);
    //   wcsfix(1, (const int*)axes, wcs, stat);
    //   wcscopy(true, wcs, this->itsWCSsources);
    //   wcsset(this->itsWCSsources);
    // }

    // wcsvfree(&nwcs, &wcs);

}

bool doAddGaussian(std::vector<unsigned int> axes, casa::Gaussian2D<casa::Double> gauss)
{
    float majorSigma = FWHMtoSIGMA(gauss.majorAxis());
    float zeroPoint = majorSigma * sqrt(-2.*log(1. / (MAXFLOAT * gauss.height())));
    int xmin = std::max(lround(gauss.xCenter() - zeroPoint), 0L);
    int xmax = std::min(lround(gauss.xCenter() + zeroPoint), long(axes[0] - 1));
    int ymin = std::max(lround(gauss.yCenter() - zeroPoint), 0L);
    int ymax = std::min(lround(gauss.yCenter() + zeroPoint), long(axes[1] - 1));
    // ASKAPLOG_DEBUG_STR(logger, axes[0] << " " << axes[1] << " " << gauss << " " << gauss.height() << " " << majorSigma << " " << zeroPoint << " " << xmin << " " << xmax << " " << ymin << " " << ymax);
    return ((xmax >= xmin) && (ymax >= ymin));

}

bool doAddPointSource(std::vector<unsigned int> axes, std::vector<double> pix)
{

    int xpix = lround(pix[0]);
    int ypix = lround(pix[1]);

    return (xpix >= 0 && xpix < int(axes[0]) && ypix >= 0 && ypix < int(axes[1]));
}

bool doAddDisc(std::vector<unsigned int> axes, Disc &disc)
{
    // ranges of pixels that will have flux added to them
    int xmin = std::max(disc.xmin(), 0);
    int xmax = std::min(disc.xmax(), int(axes[0] - 1));
    int ymin = std::max(disc.ymin(), 0);
    int ymax = std::min(disc.ymax(), int(axes[1] - 1));

    return (xmax >= xmin) && (ymax >= ymin);

}

// bool addGaussian(std::vector<float>array, std::vector<unsigned int> axes, casa::Gaussian2D<casa::Double> gauss, FluxGenerator &fluxGen, bool integrate, bool verbose)
bool addGaussian(boost::shared_ptr<float[]>array, std::vector<unsigned int> axes, casa::Gaussian2D<casa::Double> gauss, FluxGenerator &fluxGen, bool integrate, bool verbose)
{

    float majorSigma = FWHMtoSIGMA(gauss.majorAxis()) ;
    float zeroPointMax = majorSigma * sqrt(-2.*log(1. / (MAXFLOAT * gauss.height())));
    float minorSigma = FWHMtoSIGMA(gauss.minorAxis());
    float zeroPointMin = minorSigma * sqrt(-2.*log(1. / (MAXFLOAT * gauss.height())));
    // Assume a round-number in pixel location is the *centre* of the
    // pixel, so we round the floating-point pixel location to get the
    // pixel it falls in
    int xmin = std::max(lround(gauss.xCenter() - zeroPointMax), 0L);
    int xmax = std::min(lround(gauss.xCenter() + zeroPointMax), long(axes[0] - 1));
    int ymin = std::max(lround(gauss.yCenter() - zeroPointMax), 0L);
    int ymax = std::min(lround(gauss.yCenter() + zeroPointMax), long(axes[1] - 1));
    if (verbose)
        ASKAPLOG_DEBUG_STR(logger, "(x,y)=(" << gauss.xCenter() << "," << gauss.yCenter() <<
                           "), FWHMmaj=" << gauss.majorAxis() <<
                           ", FWHMmin=" << gauss.minorAxis() <<
                           ", gauss.height()=" << gauss.height() <<
                           ", sig_maj=" << majorSigma <<
                           ", sig_min=" << minorSigma <<
                           ", ZPmax=" << zeroPointMax <<
                           ", ZPmin=" << zeroPointMin <<
                           "   xmin=" << xmin << " xmax=" << xmax <<
                           " ymin=" << ymin << " ymax=" << ymax);

    bool addSource = (xmax >= xmin) && (ymax >= ymin);
    if (addSource) {  // if there are object pixels falling within the image boundaries

        std::stringstream ss;

        if (axes.size() > 0) ss << axes[0];

        for (size_t i = 1; i < axes.size(); i++) ss << "x" << axes[i];

        // Test to see whether this should be treated as a point source
        float minSigma = FWHMtoSIGMA(std::min(gauss.majorAxis(), gauss.minorAxis()));
//        float delta = std::min(0.01,pow(10., floor(log10(minSigma/5.))));
//        float delta = pow(10.,floor(log10(minSigma))-1.);
        float delta = std::min(1. / 32.,
                               pow(10., floor(log10(minSigma / 5.) / log10(2.)) * log10(2.)));

        if (verbose)
            ASKAPLOG_DEBUG_STR(logger, "Adding Gaussian " << gauss <<
                               " with flux=" << gauss.flux() <<
                               " and bounds [" << xmin << ":" << xmax <<
                               "," << ymin << ":" << ymax
                               << "] (zeropoints = " << zeroPointMax << "," << zeroPointMin <<
                               ") (dimensions of array=" << ss.str() <<
                               ")  delta=" << delta << ", minSigma = " << minSigma);

        if (xmax == xmin && ymax == ymin) { // single pixel only - add as point source
            std::vector<double> pix(2);
            pix[0] = gauss.xCenter();
            pix[1] = gauss.yCenter();
            if (verbose) {
                ASKAPLOG_DEBUG_STR(logger, "Single pixel only, so adding as point source.");
            }
            return addPointSource(array, axes, pix, fluxGen, verbose);
        }
//                else if (delta < 1.e-3 && integrate) { // if it is really thin and we're integrating, use the 1D approximation
        else if (zeroPointMin < 1. && integrate) {
            if (verbose) {
                // ASKAPLOG_DEBUG_STR(logger, "Since delta = " << delta << "( 1./" << 1. / delta
                //                    << ")  (minSigma=" << minSigma << ")  we use the 1D Gaussian function");
                ASKAPLOG_DEBUG_STR(logger, "Since zeroPointMin=" << zeroPointMin <<
                                   ", we use the 1D Gaussian function. Have delta=" <<
                                   delta << ", minSigma=" << minSigma);
            }
            add1DGaussian(array, axes, gauss, fluxGen, verbose);
        } else {
            // In this case, we need to add it as a Gaussian.

            // Loop over all affected pixels and find the overall normalisation for each pixel

            if (integrate && verbose)
                ASKAPLOG_DEBUG_STR(logger, "Integrating over " <<
                                   (xmax - xmin + 1) * (ymax - ymin + 1) <<
                                   " pixels with delta=" << delta <<
                                   " (1./" << 1. / delta <<
                                   ")  (minSigma=" << minSigma << ")");
            int nstep = int(1. / delta);
            float inputGaussFlux = gauss.flux();
            // make it a unit Gaussian.
            // We then scale by the correct flux for each frequency channel.
            gauss.setFlux(1);

            float dx[2], dy[2], du[4], dv[4];
            float mindu, mindv, separation, xpos, ypos;
            float pixelVal;
            float xScaleFactor, yScaleFactor;

            size_t pix = 0;

            for (int x = xmin; x <= xmax; x++) {

                dx[0] = x - 0.5 - gauss.xCenter();
                dx[1] = x + 0.5 - gauss.xCenter();

                for (int y = ymin; y <= ymax; y++) {

                    pixelVal = 0.;

                    // Need to check whether a given pixel is affected by the Gaussian. To
                    // do this, we look at the "maximal" ellipse - given by where the
                    // Gaussian function drops to below the smallest float. If the closest
                    // corner of the pixel to the centre of the Gaussian lies within this
                    // ellipse, or if the Gaussian passes through the pixel, we do the
                    // integration.

                    dy[0] = y - 0.5 - gauss.yCenter();
                    dy[1] = y + 0.5 - gauss.yCenter();

                    for (int i = 0; i < 4; i++) {
                        du[i] = dx[i % 2] * cos(gauss.PA()) + dy[i / 2] * sin(gauss.PA());
                        dv[i] = dy[i / 2] * cos(gauss.PA()) - dx[i % 2] * sin(gauss.PA());
                        // dv[i] = dx[i%2] * cos(gauss.PA()) + dy[i/2] * sin(gauss.PA());
                        // du[i] = dy[i/2] * cos(gauss.PA()) - dx[i%2] * sin(gauss.PA());
                    }

                    mindu = fabs(du[0]);
                    for (int i = 1; i < 4; i++) {
                        if (fabs(du[i]) < mindu) {
                            mindu = fabs(du[i]);
                        }
                    }

                    mindv = fabs(dv[0]);
                    for (int i = 1; i < 4; i++) {
                        if (fabs(dv[i]) < mindv) {
                            mindv = fabs(dv[i]);
                        }
                    }

                    separation = mindv * mindv / (zeroPointMax * zeroPointMax) +
                                 mindu * mindu / (zeroPointMin * zeroPointMin);

                    float xlim1, xlim2, ylim1, ylim2;
                    findEllipseLimits(zeroPointMax, zeroPointMin, gauss.PA(),
                                      xlim1, xlim2, ylim1, ylim2);
//              ASKAPLOG_DEBUG_STR(logger, "separation = " << separation << " (needs to be less than 1, or dx within xlim etc");
//              ASKAPLOG_DEBUG_STR(logger, "xlims,ylims: " << xlim1 << " " << xlim2 << " " << ylim1 << " " << ylim2);
//              ASKAPLOG_DEBUG_STR(logger, "dxs, dys: " << dx[0] << " " << dx[1] << " " << dy[0] << " " << dy[1]);

                    if (separation <= 1. ||
                            ((dx[0] <= xlim1 && dx[1] >= xlim2) &&
                             (dy[0] <= ylim1 && dy[1] >= ylim2))) {
//                                     ((du[0]*du[1] < 0 || du[0]*du[2] < 0 || du[0]*du[3] < 0) && mindv < zeroPointMax) ||
//                                     ((dv[0]*dv[1] < 0 || dv[0]*dv[2] < 0 || dv[0]*dv[3] < 0) && mindu < zeroPointMin)) {
                        // only consider pixels within the maximal ellipse

                        if (integrate) {

                            xpos = x - 0.5 - delta;

                            for (int dx = 0; dx <= nstep; dx++) {
                                xpos += delta;
                                ypos = y - 0.5 - delta;

                                for (int dy = 0; dy <= nstep; dy++) {
                                    ypos += delta;

                                    // This is integration using Simpson's rule. In each direction, the
                                    // end points get a factor of 1, then odd steps get a factor of 4, and
                                    // even steps 2. The whole sum then gets scaled by delta/3. for each
                                    // dimension.

                                    if (dx == 0 || dx == nstep) xScaleFactor = 1;
                                    else xScaleFactor = (dx % 2 == 1) ? 4. : 2.;

                                    if (dy == 0 || dy == nstep) yScaleFactor = 1;
                                    else yScaleFactor = (dy % 2 == 1) ? 4. : 2.;

                                    pixelVal += gauss(xpos, ypos) * (xScaleFactor * yScaleFactor);
//                  if(gauss(xpos,ypos)>0.) ASKAPLOG_DEBUG_STR(logger, xpos << " " << ypos << " " << gauss(xpos,ypos));

                                }
                            }

                            pixelVal *= (delta * delta / 9.);
                        } else {
                            pixelVal = gauss(x, y);
                        }
                    }

//              ASKAPLOG_DEBUG_STR(logger, du[0] << " " << du[1] << " " << du[2] << " " << du[3] << "     " << dv[0] << " " << dv[1] <<" " << dv[2] << " " << dv[3]);
//              ASKAPLOG_DEBUG_STR(logger, separation << " " << mindu << " " << mindv << " " << nstep << " " << delta << " " << delta*delta/9. << " " << pixelVal);

                    // For this pixel, loop over all channels and assign the correctly-scaled pixel value.
                    for (size_t istokes = 0; istokes < fluxGen.nStokes(); istokes++) {
                        for (size_t z = 0; z < fluxGen.nChan(); z++) {
                            pix = x + y * axes[0] + istokes * axes[0] * axes[1] +
                                  z * axes[0] * axes[1] * axes[2];
//              ASKAPLOG_DEBUG_STR(logger, "Adding flux of " << pixelVal*fluxGen.getFlux(z,istokes) << " (from pixelval="<<pixelVal<<" and fluxGen(z)="<<fluxGen.getFlux(z,istokes)<<") to (x,y,z)=("<<x<<","<<y<<","<<z<<")");
                            array[pix] += pixelVal * fluxGen.getFlux(z, istokes);
                        }
                    }

                }
            }

            gauss.setFlux(inputGaussFlux);

        }

    }

    return addSource;
}

// void add1DGaussian(std::vector<float>array,
//                    std::vector<unsigned int> axes,
//                    casa::Gaussian2D<casa::Double> gauss,
//                    FluxGenerator &fluxGen,
//                    bool verbose)
void add1DGaussian(boost::shared_ptr<float[]>array,
                   std::vector<unsigned int> axes,
                   casa::Gaussian2D<casa::Double> gauss,
                   FluxGenerator &fluxGen,
                   bool verbose)
{

    enum DIR {VERTICAL = 0, HORIZONTAL};
    DIR direction = VERTICAL;
    bool specialCase = true;
    double pa = gauss.PA();
    pa = fmod(pa, M_PI);   // in case we get a value of PA outside [0,pi)
    double sinpa = sin(pa);
    double cospa = cos(pa);
    int sign = pa < M_PI / 2. ? -1 : 1;

    if (cospa == 0.) direction = HORIZONTAL;
    else if (sinpa == 0.) direction = VERTICAL;
    else specialCase = false;

    double majorSigma = FWHMtoSIGMA(gauss.majorAxis());
    double zeroPointMax = majorSigma * sqrt(-2.*log(1. / (MAXFLOAT * gauss.height())));
    double length = 0.;
    double increment = 0.;
    double x = gauss.xCenter() - zeroPointMax * sinpa;
    double y = gauss.yCenter() + zeroPointMax * cospa;
    if (verbose) {
        ASKAPLOG_DEBUG_STR(logger, "Adding a 1D Gaussian: majorSigma = " << majorSigma <<
                           ", zpmax = " << zeroPointMax
                           << ", (xcentre,ycentre)=(" << gauss.xCenter() <<
                           "," << gauss.yCenter() << ")" <<
                           ", pa=" << pa << ", sign=" << sign
                           << ", (xstart,ystart)=(" << x << "," << y <<
                           ") and axes=[" << axes[0] << "," << axes[1] << "]");
    }
    int xref = lround(x);
    int yref = lround(y);
    int spatialPixel = xref + axes[0] * yref;
    ASKAPLOG_DEBUG_STR(logger, "add1DGaussian: x=" << x << ", xref=" << xref << ", y=" << y <<
                       ", yref=" << yref << ", axes[0]=" << axes[0] <<
                       ", spatialPixel=" << spatialPixel);
    size_t pix = 0;
    double pixelVal = 0.;
    bool addPixel = true;

    while (length < 2.*zeroPointMax) {

        // is the current pixel in the bounds of the flux array?
        addPixel = (xref >= 0 && xref < int(axes[0])) &&
                   (yref >= 0 && yref < int(axes[1]));

        if (!specialCase) {
            direction = (fabs((yref + 0.5 * sign - y) / cospa) < fabs((xref + 0.5 - x) / sinpa)) ?
                        VERTICAL : HORIZONTAL;
        }

        if (direction == VERTICAL) { // Moving vertically
            increment = std::min(2.*zeroPointMax - length, fabs((yref + sign * 0.5 - y) / cospa));
            ASKAPCHECK(increment > 0.,
                       "Vertical increment negative: increment=" << increment <<
                       ", sign=" << sign << ", yref=" << yref << ", y=" << y <<
                       ", cospa=" << cospa << ", length=" << length <<
                       ", zpmax=" << zeroPointMax << ", pa=" << pa << "=" << pa * 180. / M_PI);
            yref += sign;
        } else if (direction == HORIZONTAL) { // Moving horizontally
            increment = std::min(2.*zeroPointMax - length, fabs((xref + 0.5 - x) / sinpa));
            ASKAPCHECK(increment > 0.,
                       "Horizontal increment negative: increment=" << increment <<
                       ", xref=" << xref << ", x=" << x << ", sinpa=" << sinpa <<
                       ", length=" << length << ", zpmax=" << zeroPointMax <<
                       ", pa=" << pa << "=" << pa * 180. / M_PI);
            xref++;
        }

        if (addPixel) { // only add points if we're in the array boundaries
            pixelVal = 0.5 * (erf((length + increment - zeroPointMax) / (M_SQRT2 * majorSigma)) -
                              erf((length - zeroPointMax) / (M_SQRT2 * majorSigma)));

            for (size_t istokes = 0; istokes < fluxGen.nStokes(); istokes++) {
                for (size_t z = 0; z < fluxGen.nChan(); z++) {
                    pix = spatialPixel + istokes * axes[0] * axes[1] +
                          z * axes[0] * axes[1] * axes[2];
                    array[pix] += pixelVal * fluxGen.getFlux(z, istokes);
                }
            }
        }

        x += increment * sinpa;
//                y -= sign * increment * cospa;
        y -= increment * cospa;
        spatialPixel = xref + axes[0] * yref;
        length += increment;
        ASKAPLOG_DEBUG_STR(logger, "add1DGaussian: x=" << x << ", xref=" << xref <<
                           ", y=" << y << ", yref=" << yref <<
                           ", axes[0]=" << axes[0] << ", spatialPixel=" << spatialPixel <<
                           ", PIXELVAL=" << pixelVal << ", increment=" << increment <<
                           ", direction=" << direction << ", length=" << length);

    }
}

// bool addPointSource(std::vector<float>array,
//                     std::vector<unsigned int> axes,
//                     std::vector<double> pix,
//                     FluxGenerator &fluxGen,
//                     bool verbose)
bool addPointSource(boost::shared_ptr<float[]>array,
                    std::vector<unsigned int> axes,
                    std::vector<double> pix,
                    FluxGenerator &fluxGen,
                    bool verbose)
{

    int xpix = lround(pix[0]);
    int ypix = lround(pix[1]);

    bool addSource = (xpix >= 0 && xpix < int(axes[0]) && ypix >= 0 && ypix < int(axes[1]));

    if (addSource)  {

        if (verbose) {
            ASKAPLOG_DEBUG_STR(logger, "Adding Point Source with x=" << pix[0] <<
                               " & y=" << pix[1] << " and flux0=" << fluxGen.getFlux(0) <<
                               " to  axes = [" << axes[0] << "," << axes[1] << "]");
        }

        size_t loc = 0;
        for (size_t istokes = 0; istokes < fluxGen.nStokes(); istokes++) {
            for (size_t z = 0 ; z < fluxGen.nChan(); z++) {

                loc = xpix + axes[0] * ypix + istokes * axes[0] * axes[1] +
                      z * axes[0] * axes[1] * axes[2];

                array[loc] += fluxGen.getFlux(z, istokes);

            }
        }

    }

    return addSource;

}

// bool addDisc(std::vector<float>array,
//              std::vector<unsigned int> axes,
//              Disc &disc,
//              FluxGenerator &fluxGen,
//              bool verbose)
bool addDisc(boost::shared_ptr<float[]>array,
             std::vector<unsigned int> axes,
             Disc &disc,
             FluxGenerator &fluxGen,
             bool verbose)
{

    // ranges of pixels that will have flux added to them
    int xmin = std::max(disc.xmin(), 0);
    int xmax = std::min(disc.xmax(), int(axes[0] - 1));
    int ymin = std::max(disc.ymin(), 0);
    int ymax = std::min(disc.ymax(), int(axes[1] - 1));

    bool addSource  = (xmax >= xmin) && (ymax >= ymin);
    if (addSource) {  // if there are object pixels falling within the image boundaries

        if (verbose)
            ASKAPLOG_DEBUG_STR(logger, "Adding Disc " << disc <<  " with x=[" << xmin <<
                               "," << xmax << "] & y=[" << ymin << "," << ymax <<
                               "] and flux0=" << fluxGen.getFlux(0) <<
                               " to  axes = [" << axes[0] << "," << axes[1] << "]");

        for (int y = ymin; y <= ymax; y++) {
            for (int x = xmin; x <= xmax; x++) {

                double discFlux = disc.flux(x, y);

                for (size_t istokes = 0; istokes < fluxGen.nStokes(); istokes++) {
                    for (size_t z = 0 ; z < fluxGen.nChan(); z++) {
                        size_t loc = x + axes[0] * y + istokes * axes[0] * axes[1] +
                                     z * axes[0] * axes[1] * axes[2];
                        array[loc] += discFlux * fluxGen.getFlux(z, istokes);
                    }
                }

            }
        }

    }

    return addSource;

}

}

}


//
// CPPINTS: A C++ Program to Generate Analytical Integrals Based on Gaussian
// Form Primitive Functions
//
// Copyright (C) 2015 The State University of New York at Buffalo
// This softare uses the MIT license as below:
//
//	Permission is hereby granted, free of charge, to any person obtaining
//	a copy of this software and associated documentation files (the "Software"),
//	to deal in the Software without restriction, including without limitation
//	the rights to use, copy, modify, merge, publish, distribute, sublicense,
//	and/or sell copies of the Software, and to permit persons to whom the Software
//	is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
//	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
//	INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
//	PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
//	FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
//	ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//
#include "boost/lexical_cast.hpp"
#include <boost/algorithm/string.hpp>   // string handling
#include <boost/filesystem.hpp>
#include "inttype.h"
#include "derivinfor.h"
#include "rr.h"
#include "nonrr.h"
#include "printing.h"
#include "vrrinfor.h"
#include "hrrinfor.h"
#include "nonrrinfor.h"
#include "sqints.h"
using boost::lexical_cast;
using namespace boost::filesystem;
using namespace boost;
using namespace derivinfor;
using namespace printing;
using namespace inttype;
using namespace rr;
using namespace nonrr;
using namespace vrrinfor;
using namespace hrrinfor;
using namespace nonrrinfor;
using namespace sqints;

void SQInts::intCodeGeneration()
{
	// this is the output shell quartet list and unsolved
	// integral list passing from one code section to another
	vector<ShellQuartet> outputSQList;
	vector<set<int> > unsolvedList;

	// initilize the output sq list and unsolved integral list
	// for deriv job, we initilize it from the derivSQList
	// else we will do it from inputSQList in SQIntsInfor
	if (infor.getJobOrder() > 0) {
		outputSQList = infor.getDerivSQList();
	}else{
		outputSQList = infor.getInputSQList();
	}
	for(int iSQ=0; iSQ<(int)outputSQList.size(); iSQ++) {
		set<int> list;
		outputSQList[iSQ].getIntegralList(list);
		unsolvedList.push_back(list);
	}

	///////////////////////////////////////////////////////////////////////
	// some note before we proceed the real work:                        //
	// the code generation is in reverse order of the real cpp file.     //
	// the result is generated first, then to it's previous section;     //
	// finally the generation will stop at the VRR section               //
	//                                                                   //
	//      %%%             CODE GENERATION STEP                         //
	///////////////////////////////////////////////////////////////////////

	////////////////////////////
	//   derivative section   //
	////////////////////////////
	NONRR derivJob(outputSQList,unsolvedList);
	size_t nLHSDeriv = 0;
	if (infor.getJobOrder() > 0) {

		// generate the integrals
		derivJob.buildRRSQList();

		// add the deriv section to infor
		int moduleName = DERIV;
		infor.appendCodeSection(moduleName);

		// let's carry the shell quartets to the next section
		// here the unsolved list must be in integral index
		// form, so do it before array index transformation
		outputSQList.clear();
		unsolvedList.clear();
		outputSQList = derivJob.getBottomSQList();
		unsolvedList = derivJob.getBottomIntList();

		// update nLHS
		nLHSDeriv = (size_t)derivJob.countLHSIntNumbers();
	}
	NONRRInfor derivJobInfor(infor,derivJob);
	infor.updateWithArray(derivJobInfor.fileSplit());

	////////////////////////////
	// possible NON-RR section//
	////////////////////////////
	NONRR nonRRJob(outputSQList,unsolvedList);
	size_t nLHSNonRR = 0;
	if (isNONRROper(infor.getOper())) {

		// build the rrsq
		nonRRJob.buildRRSQList();

		// let's carry the shell quartets to the next section
		// here the unsolved list must be in integral index
		// form, so do it before array index transformation
		outputSQList.clear();
		unsolvedList.clear();
		outputSQList = nonRRJob.getBottomSQList();
		unsolvedList = nonRRJob.getBottomIntList();

		// add the non-RR section to infor
		int moduleName = NON_RR;
		infor.appendCodeSection(moduleName);

		// update nLHS
		nLHSNonRR  = (size_t)nonRRJob.countLHSIntNumbers();
	}
	NONRRInfor nonRRJobInfor(infor,nonRRJob);
	infor.updateWithArray(nonRRJobInfor.fileSplit());

	////////////////////////////
	// second HRR section     //
	////////////////////////////
	RR hrr2(HRR2,HRR,outputSQList,unsolvedList);
	size_t nLHSHRR2 = 0;
	int firstSide  = NULL_POS;
	int secondSide = NULL_POS;
	if (infor.hasHRR()) {

		// determine the first and second side
		hrr2.sideDeterminationInHRR(firstSide,secondSide);

		// now do the second side HRR
		// for generated code, second side is performed after
		// first side; so in generation of code second side
		// is prior to the first side
		if (secondSide != NULL_POS) {

			// generat rrsq
			hrr2.generateRRSQList(secondSide);

			// let's carry the shell quartets to the next section
			// here the unsolved list must be in integral index
			// form, so do it before array index transformation
			outputSQList.clear();
			unsolvedList.clear();
			outputSQList = hrr2.getHRRBottomSQList();
			unsolvedList = hrr2.getHRRBottomIntList();

			// add the HRR2 section to infor
			int moduleName = HRR2;
			infor.appendCodeSection(moduleName);

			// update nLHS
			nLHSHRR2   = (size_t)hrr2.countLHSIntNumbers();
		}
	}
	HRRInfor HRR2JobInfor(infor,hrr2);
	infor.updateWithArray(HRR2JobInfor.fileSplit());

	////////////////////////////
	//  first HRR section     //
	////////////////////////////
	RR hrr1(HRR1,HRR,outputSQList,unsolvedList);
	size_t nLHSHRR1 = 0;
	if (firstSide != NULL_POS) {

		// now do the first side
		hrr1.generateRRSQList(firstSide);

		// now rewrite the shell quartet list
		outputSQList.clear();
		unsolvedList.clear();
		outputSQList = hrr1.getHRRBottomSQList();
		unsolvedList = hrr1.getHRRBottomIntList();

		// add the HRR1 section to infor
		int moduleName = HRR1;
		infor.appendCodeSection(moduleName);

		// update nLHS
		nLHSHRR1   = (size_t)hrr1.countLHSIntNumbers();
	}
	HRRInfor HRR1JobInfor(infor,hrr1);
	infor.updateWithArray(HRR1JobInfor.fileSplit());

	////////////////////////////
	//      VRR section       //
	////////////////////////////
	RR vrr(VRR,infor.getVRRMethod(),outputSQList,unsolvedList);
	vrr.generateRRSQList(NULL_POS);

	// update nLHS
	size_t nLHSVRR = (size_t)vrr.countLHSIntNumbers();

	// add the VRR section to infor
	int moduleName = VRR;
	infor.appendCodeSection(moduleName);

	// now build the vrr infor
	VRRInfor vrrInfor(infor,vrr);
	infor.updateWithArray(vrrInfor.fileSplit());

	///////////////////////////////////////////////////////////////////////
	// now let's analyze the situation of file split. All modules has    //
	// been set up, and now it's time to see whether we need to make     //
	// some changes                                                      //
	//      %%%%            FILE SPLIT DETERMINATION STEP                //
	///////////////////////////////////////////////////////////////////////

	// do we need to re-evalate the file split status
	// for all of codes?
	// we do this when the main driver cpp file becomes too large
	size_t nLHSMainCPP = 0;
	const vector<int>& secInfor = infor.getSectionInfor();
	for(int iSec=0; iSec<(int)secInfor.size(); iSec++) {
		int sec = secInfor[iSec];
		if (sec == DERIV) {
			if (! derivJobInfor.fileSplit()) {
				nLHSMainCPP = nLHSMainCPP + nLHSDeriv;
			}
		}else if (sec == NON_RR) {
			if (! nonRRJobInfor.fileSplit()) {
				nLHSMainCPP = nLHSMainCPP + nLHSNonRR;
			}
		}else if (sec == HRR2) {
			if (! HRR2JobInfor.fileSplit()) {
				nLHSMainCPP = nLHSMainCPP + nLHSHRR2;
			}
		}else if (sec == HRR1) {
			if (! HRR1JobInfor.fileSplit()) {
				nLHSMainCPP = nLHSMainCPP + nLHSHRR1;
			}
		}else if (sec == VRR) {
			if (! vrrInfor.fileSplit()) {
				nLHSMainCPP = nLHSMainCPP + nLHSVRR;
			}
		}
	}

	// do we need to split the single cpp file?
	bool doSplit = false;
	double nTotalLHSCoef = infor.totalLHSScaleFac;
	int nTotalLHSLimit   = infor.nTotalLHSLimit;
	if (nLHSMainCPP>(size_t)(nTotalLHSLimit*(1+nTotalLHSCoef))) {
		doSplit = true;
		infor.updateWithArray(true);
	}

	// now let's update the infor for sections
	// from the last section to VRR
	if (doSplit) {
		size_t nLHS = nLHSMainCPP;
		for(int iSec=0; iSec<(int)secInfor.size(); iSec++) {

			// now let's update
			int sec = secInfor[iSec];
			if (sec == DERIV) {
				if (! derivJobInfor.fileSplit()) {
					derivJobInfor.updateFileSplit();
					nLHS = nLHS - nLHSDeriv;
				}
			}else if (sec == NON_RR) {
				if (! nonRRJobInfor.fileSplit()) {
					nonRRJobInfor.updateFileSplit();
					nLHS = nLHS - nLHSNonRR;
				}
			}else if (sec == HRR2) {
				if (! HRR2JobInfor.fileSplit()) {
					HRR2JobInfor.updateFileSplit();
					nLHS = nLHS - nLHSHRR2;
				}
			}else if (sec == HRR1) {
				if (! HRR1JobInfor.fileSplit()) {
					HRR1JobInfor.updateFileSplit();
					nLHS = nLHS - nLHSHRR1;
				}
			}else if (sec == VRR) {
				if (! vrrInfor.fileSplit()) {
					vrrInfor.updateFileSplit();
					nLHS = nLHS - nLHSVRR;
				}
			}

			// do we get to a break?
			if (nLHS<(size_t)(nTotalLHSLimit*(1+nTotalLHSCoef))) {
				break;
			}
		}
	}

	///////////////////////////////////////////////////////////////////////
	//  based on the file split status, we can figure out the input      //
	//  and output shell quartet status, in array or var?                //
	//    %%%%    SET UP MODULE INPUT/OUPUT STATUS                       //
	///////////////////////////////////////////////////////////////////////

	// firstly for VRR, update it's output when it's not in file split
	if (! vrrInfor.fileSplit()) {
		for(int iSec=0; iSec<(int)secInfor.size(); iSec++) {
			int sec = secInfor[iSec];
			if (sec == VRR) break;
			if (sec == DERIV && derivJobInfor.fileSplit()) {
				const vector<ShellQuartet>& output = derivJobInfor.getInputSQList();
				vrrInfor.updateOutputSQInArray(output);
			}else if (sec == NON_RR && nonRRJobInfor.fileSplit()) {
				const vector<ShellQuartet>& output = nonRRJobInfor.getInputSQList();
				vrrInfor.updateOutputSQInArray(output);
			}else if (sec == HRR2 && HRR2JobInfor.fileSplit()) {
				const vector<ShellQuartet>& output = HRR2JobInfor.getInputSQList();
				vrrInfor.updateOutputSQInArray(output);
			}else if (sec == HRR1 && HRR1JobInfor.fileSplit()) {
				const vector<ShellQuartet>& output = HRR1JobInfor.getInputSQList();
				vrrInfor.updateOutputSQInArray(output);
			}
		}
	}else{
		vrrInfor.subFilesForming(infor,vrr);
	}

	// form output sq which are in array status
	vector<ShellQuartet> sqlist;
	sqlist.reserve(500);
	const vector<ShellQuartet>& vrrOutputSQ = vrrInfor.getOutputSQList();
	const vector<int>& vrrOutputSQStatus    = vrrInfor.getOutputSQStatus();
	for(int iSQ=0; iSQ<(int)vrrOutputSQ.size(); iSQ++) {
		if (inArrayStatus(vrrOutputSQStatus[iSQ])) {
			sqlist.push_back(vrrOutputSQ[iSQ]);
		}
	}

	// now VRR output sq has fixed, reversely update all of other modules
	// which has VRR module results. We note, that we only do it to these modules
	// when they are not in file split status
	if (sqlist.size() > 0) {
		for(int iSec=0; iSec<(int)secInfor.size(); iSec++) {
			int sec = secInfor[iSec];
			if (sec == VRR) break;
			if (sec == DERIV && ! derivJobInfor.fileSplit()) {
				derivJobInfor.updateInputSQInArray(sqlist);
			}else if (sec == NON_RR && ! nonRRJobInfor.fileSplit()) {
				nonRRJobInfor.updateInputSQInArray(sqlist);
			}else if (sec == HRR2 && ! HRR2JobInfor.fileSplit()) {
				HRR2JobInfor.updateInputSQInArray(sqlist);
			}else if (sec == HRR1 && ! HRR1JobInfor.fileSplit()) {
				HRR1JobInfor.updateInputSQInArray(sqlist);
			}
		}
	}

	// now let's do HRR1, we may not have HRR
	if (infor.hasSection(HRR1)) {

		// update the module output from all of following modules
		if (! HRR1JobInfor.fileSplit()) {
			for(int iSec=0; iSec<(int)secInfor.size(); iSec++) {
				int sec = secInfor[iSec];
				if (sec == HRR1) break;
				if (sec == DERIV && derivJobInfor.fileSplit()) {
					const vector<ShellQuartet>& output = derivJobInfor.getInputSQList();
					HRR1JobInfor.updateOutputSQInArray(output);
				}else if (sec == NON_RR && nonRRJobInfor.fileSplit()) {
					const vector<ShellQuartet>& output = nonRRJobInfor.getInputSQList();
					HRR1JobInfor.updateOutputSQInArray(output);
				}else if (sec == HRR2 && HRR2JobInfor.fileSplit()) {
					const vector<ShellQuartet>& output = HRR2JobInfor.getInputSQList();
					HRR1JobInfor.updateOutputSQInArray(output);
				}
			}
		}else{
			HRR1JobInfor.formSubFiles(infor,hrr1);
		}

		// form output sq which are in array status
		sqlist.clear();
		const vector<ShellQuartet>& outputSQ = HRR1JobInfor.getOutputSQList();
		const vector<int>& outputSQStatus    = HRR1JobInfor.getOutputSQStatus();
		for(int iSQ=0; iSQ<(int)outputSQ.size(); iSQ++) {
			if (inArrayStatus(outputSQStatus[iSQ])) {
				sqlist.push_back(outputSQ[iSQ]);
			}
		}

		// now HRR1 output sq has fixed, reversely update all of other modules
		// which has HRR1 module results. We note, that we only do it to these modules
		// when they are not in file split status
		for(int iSec=0; iSec<(int)secInfor.size(); iSec++) {
			int sec = secInfor[iSec];
			if (sec == HRR1) break;
			if (sec == DERIV && ! derivJobInfor.fileSplit()) {
				derivJobInfor.updateInputSQInArray(sqlist);
			}else if (sec == NON_RR && ! nonRRJobInfor.fileSplit()) {
				nonRRJobInfor.updateInputSQInArray(sqlist);
			}else if (sec == HRR2 && ! HRR2JobInfor.fileSplit()) {
				HRR2JobInfor.updateInputSQInArray(sqlist);
			}
		}
	}

	// now let's do HRR2, we may not have HRR2
	if (infor.hasSection(HRR2)) {

		// update the module output from all of following modules
		if (! HRR2JobInfor.fileSplit()) {
			for(int iSec=0; iSec<(int)secInfor.size(); iSec++) {
				int sec = secInfor[iSec];
				if (sec == HRR2) break;
				if (sec == DERIV && derivJobInfor.fileSplit()) {
					const vector<ShellQuartet>& output = derivJobInfor.getInputSQList();
					HRR2JobInfor.updateOutputSQInArray(output);
				}else if (sec == NON_RR && nonRRJobInfor.fileSplit()) {
					const vector<ShellQuartet>& output = nonRRJobInfor.getInputSQList();
					HRR2JobInfor.updateOutputSQInArray(output);
				}
			}
		}else{
			HRR2JobInfor.formSubFiles(infor,hrr2);
		}

		// form output sq which are in array status
		sqlist.clear();
		const vector<ShellQuartet>& outputSQ = HRR2JobInfor.getOutputSQList();
		const vector<int>& outputSQStatus    = HRR2JobInfor.getOutputSQStatus();
		for(int iSQ=0; iSQ<(int)outputSQ.size(); iSQ++) {
			if (inArrayStatus(outputSQStatus[iSQ])) {
				sqlist.push_back(outputSQ[iSQ]);
			}
		}

		// now HRR2 output sq has fixed, reversely update all of other modules
		// which has HRR2 module results. We note, that we only do it to these modules
		// when they are not in file split status
		if (sqlist.size() > 0) {
			for(int iSec=0; iSec<(int)secInfor.size(); iSec++) {
				int sec = secInfor[iSec];
				if (sec == HRR2) break;
				if (sec == DERIV && ! derivJobInfor.fileSplit()) {
					derivJobInfor.updateInputSQInArray(sqlist);
				}else if (sec == NON_RR && ! nonRRJobInfor.fileSplit()) {
					nonRRJobInfor.updateInputSQInArray(sqlist);
				}
			}
		}
	}

	// now let's do NON-RR
	if (infor.hasSection(NON_RR)) {

		// update the module output for DERIV module
		if (! nonRRJobInfor.fileSplit()) {
			if (infor.hasSection(DERIV) && derivJobInfor.fileSplit()) {
				const vector<ShellQuartet>& output = derivJobInfor.getInputSQList();
				nonRRJobInfor.updateOutputSQInArray(output);
			}
		}else{
			nonRRJobInfor.formSubFiles(infor,nonRRJob);
		}

		// form output sq which are in array status
		sqlist.clear();
		const vector<ShellQuartet>& outputSQ = nonRRJobInfor.getOutputSQList();
		const vector<int>& outputSQStatus    = nonRRJobInfor.getOutputSQStatus();
		for(int iSQ=0; iSQ<(int)outputSQ.size(); iSQ++) {
			if (inArrayStatus(outputSQStatus[iSQ])) {
				sqlist.push_back(outputSQ[iSQ]);
			}
		}

		// we only need to consider the DERIV module
		if (sqlist.size() > 0) {
			if (infor.hasSection(DERIV) && ! derivJobInfor.fileSplit()) {
				derivJobInfor.updateInputSQInArray(sqlist);
			}
		}
	}

	// for deriv job we do not need to update the input and output
	// all of input shell quartet updating are finished
	// and output sq must be global results
	if (infor.hasSection(DERIV) && derivJobInfor.fileSplit()) {
		derivJobInfor.formSubFiles(infor,derivJob);
	}

	///////////////////////////////////////////////////////////////////////
	//               %%%%     PRINT OUT THE CODES                        //
	///////////////////////////////////////////////////////////////////////
	vrr.vrrPrint(infor,vrrInfor);
	if (infor.hasSection(HRR1)) {
		hrr1.hrrPrint(infor,HRR1JobInfor);
	}
	if (infor.hasSection(HRR2)) {
		hrr2.hrrPrint(infor,HRR2JobInfor);
	}
	if (infor.hasSection(NON_RR)) {
		nonRRJob.print(infor,nonRRJobInfor);
	}
	if (infor.hasSection(DERIV)) {
		derivJob.print(infor,derivJobInfor);
	}
}

void SQInts::assembleCPPFiles() const
{
	// open the top cpp file
	string cppFile = infor.getWorkFuncName(false,NULL_POS,-1,true);
	ofstream CPP;
	CPP.open(cppFile.c_str());

	// generate the head of file
	infor.headPrinting(CPP);

	// now append all of function prototype to the head
	// particularly, because the vrr contraction may also
	// in a seperate function, therefore we need to check it
	vector<int> modules(infor.getSectionInfor());
	for(int iFunc=modules.size()-1; iFunc>=0; iFunc--) {
		int module = modules[iFunc];
		appendFuncPrototype(module,CPP);
	}
	if (hasFileDefined(VRR_CONT)) {
		appendFuncPrototype(VRR_CONT,CPP);
	}

	// now it's the function name
	string func = infor.getFuncName();
	func = "void " + func;
	string arg  = infor.getArgList();
	string line = func + "(" + arg + ")";
	CPP << line << endl;
	CPP << "{" << endl;

	//
	// now we begin to generate code starting from VRR
	// section, until the final section is met
	//

	//////////////////////////////
	//       VRR section        //
	//////////////////////////////

	// generate the VRR head
	// append it to the main cpp file
	int moduleName = VRR_HEAD;
	appendFile(moduleName,CPP);

	// now it's the main body of VRR
	moduleName = VRR;
	if (hasFileDefined(moduleName)) {
		formWorkFile(moduleName);
		formFunctionCall(moduleName,CPP);
	}else{
		appendFile(moduleName,CPP);
	}

	// contraction part
	// for contraction if it's not in file split
	// mode, it will be with VRR code
	moduleName = VRR_CONT;
	if (hasFileDefined(moduleName)) {
		formWorkFile(moduleName);
		formFunctionCall(moduleName,CPP);
	}

	// now let's close the VRR part
	// determine the number space for printing etc.
	int oper = infor.getOper();
	int nSpace = getNSpaceByOper(oper);
	int nSpaceStop = 2;
	if (resultIntegralHasAdditionalOffset(oper)) nSpaceStop += 2;

	// finally, we need to add braket closure to the vrr body
	line = "}";
	for(int iSpace= nSpace-2; iSpace>=nSpaceStop; iSpace = iSpace - 2) {
		printLine(iSpace,line,CPP);
	}

	// if we have significance test, then we may
	// test the significance first
	// if VRR step all of integrals are omitted,
	// then we do not need to do HRR accordingly
	if(sigCheck(oper) && ! infor.isLastSection(VRR)) {
		CPP << endl;
		line = "/************************************************************";
		printLine(nSpaceStop,line,CPP);
		line = " * let's see the significance test result. if VRR result is";
		printLine(nSpaceStop,line,CPP);
		line = " * insignificant, there's no need to do following codes";
		printLine(nSpaceStop,line,CPP);
		line = " ************************************************************/";
		printLine(nSpaceStop,line,CPP);

		// the handling of significance at the bottom of VRR
		// this is to see whether we do HRR part
		// for the case that VRR/HRR are in a loop
		// we can not return, just use continue
		line = "if (! isSignificant) return;";
		if (resultIntegralHasAdditionalOffset(oper)) {
			line = "if (! isSignificant) continue;";
		}
		printLine(nSpaceStop,line,CPP);
	}

	//////////////////////////////
	//       HRR1 section etc.  //
	//////////////////////////////
	for(int iFunc=modules.size()-1; iFunc>=0; iFunc--) {
		int module = modules[iFunc];
		if (module == VRR || module == VRR_CONT) continue;
		if (hasFileDefined(module)) {

			// form the work file
			formWorkFile(module);

			// append the main module file if we have
			// this contains the variables declare or
			// the results declare etc.
			appendFile(module,CPP,false);

			// form the function call to the result cpp file
			formFunctionCall(module,CPP);
		}else{
			appendFile(module,CPP);
		}
	}

	// now finalize the cpp file
	if (oper == ESP) {
		CPP << "  }" << endl;
		CPP << "}" << endl;
	}else{
		CPP << "}" << endl;
	}
	CPP.close();
}

bool SQInts::isFileExist() const
{
	string fileName = infor.getWorkFuncName(false,NULL_POS,-1,true);
	path cppFile(fileName.c_str());
	if (exists(cppFile)) {
		return true;
	}
	return false;
}

bool SQInts::hasFileDefined(int moduleName) const
{
	// we will check t he function statement
	// if it has, then we have the files for this section
	int module = -1;
	if (moduleName == VRR) {
		module = VRR_FUNC_STATEMENT;
	}else if (moduleName == VRR_CONT) {
		module = VRR_CONT_STATEMENT;
	}else if (moduleName == HRR1) {
		module = HRR1_FUNC_STATEMENT;
	}else if (moduleName == HRR2) {
		module = HRR2_FUNC_STATEMENT;
	}else if (moduleName == NON_RR) {
		module = NON_RR_FUNC_STATEMENT;
	}else if (moduleName == DERIV) {
		module = DERIV_FUNC_STATEMENT;
	}else {
		crash(true,"the input module name is invalid in SQInts::hasFileDefined");
	}
	bool onlyWithFuncName = false;
	bool inFinalDir = false;
	string pro = infor.getWorkFuncName(onlyWithFuncName,module,-1,inFinalDir);
	path p(pro.c_str());
	if (exists(p)) {
		return true;
	}
	return false;
}

void SQInts::appendFuncPrototype(int moduleName, ofstream& CPP) const
{
	// deriv the function prototype
	// get the name
	int module = -1;
	if (moduleName == VRR) {
		module = VRR_FUNC_STATEMENT;
	}else if (moduleName == VRR_CONT) {
		module = VRR_CONT_STATEMENT;
	}else if (moduleName == HRR1) {
		module = HRR1_FUNC_STATEMENT;
	}else if (moduleName == HRR2) {
		module = HRR2_FUNC_STATEMENT;
	}else if (moduleName == NON_RR) {
		module = NON_RR_FUNC_STATEMENT;
	}else if (moduleName == DERIV) {
		module = DERIV_FUNC_STATEMENT;
	}else {
		crash(true,"the input module name is invalid in SQInts::appendFuncPrototype");
	}

	// now append the prototype to the main cpp file
	// we note  that if the module is not in file split mode, we do not
	// have the prototype file
	// so we check it's existence
	bool onlyWithFuncName = false;
	bool inFinalDir = false;
	string pro = infor.getWorkFuncName(onlyWithFuncName,module,-1,inFinalDir);
	path p(pro.c_str());
	if (! exists(p)) {
		return;
	}

	// now do the work
	ifstream PRO;
	PRO.open(pro.c_str(),ios::in);
	string line;
	while(getline(PRO,line)) {
		CPP << line << endl;
	}
	PRO.close();
}

void SQInts::formFunctionCall(int moduleName, ofstream& CPP) const
{
	// deriv the function prototype
	// get the name
	int module = -1;
	if (moduleName == VRR) {
		module = VRR_FUNC_STATEMENT;
	}else if (moduleName == VRR_CONT) {
		module = VRR_CONT_STATEMENT;
	}else if (moduleName == HRR1) {
		module = HRR1_FUNC_STATEMENT;
	}else if (moduleName == HRR2) {
		module = HRR2_FUNC_STATEMENT;
	}else if (moduleName == NON_RR) {
		module = NON_RR_FUNC_STATEMENT;
	}else if (moduleName == DERIV) {
		module = DERIV_FUNC_STATEMENT;
	}else {
		crash(true,"the input module name is invalid in SQInts::appendFuncPrototype");
	}

	// now append the prototype to the main cpp file
	// we note  that if the module is not in file split mode, we do not
	// have the prototype file
	// so we check it's existence
	bool onlyWithFuncName = false;
	bool inFinalDir = false;
	string pro = infor.getWorkFuncName(onlyWithFuncName,module,-1,inFinalDir);
	path p(pro.c_str());
	if (! exists(p)) {
		cout << "the module name " << moduleName << endl;
		crash(true, "the prototype file does not exist in formFunctionCall of sqints");
	}

	// now get the function statement
	vector<string> prototype;
	prototype.reserve(200);
	ifstream PRO;
	PRO.open(pro.c_str(),ios::in);
	string line;
	while(getline(PRO,line)) {

		// get the function statement
		if (line.find("void") == std::string::npos) continue;
		string statement = line;

		// now get rid of the type information etc.
		LineParse lp(statement);
		string result;
		result.reserve(1000);
		for(int i=0; i<lp.getNPieces(); i++) {

			// we begin to determine that whether this is variable name
			string val = lp.findValue(i);

			// check whether this is the function name
			if (val.find("(")!=std::string::npos) {
				int pos = val.find("(");
				string v = val.substr(0,pos+1);
				result = result + v;
				continue;
			}

			// omit these values
			if (val.find("const")!=std::string::npos) continue;
			if (val.find("vector")!=std::string::npos) continue;
			if (val.find("Double")!=std::string::npos) continue;
			if (val.find("LocalMemScr")!=std::string::npos) continue;
			if (val.find("void")!=std::string::npos) continue;

			// if the name is turned out to be a shell quartet name,
			// then this is must be VRR's output result or HRR input
			// result array
			// since we all use pointer, we need to pass in pointer inside
			if (val.find("SQ")!=std::string::npos) {

				// see that whether the name has ' additional to the name or not
				int len = val.length();
				if (val[len-1] == ',') {
					val[len-1] = '[';
					val.append("0],");
					val = "&" + val;
				}else{
					val = "&" + val + "[0]";
				}
			}

			// append it to the result
			result = result + val;
		}
		prototype.push_back(result);
	}
	PRO.close();

	// determine the nspace
	int nSpace = 2;
	int oper = infor.getOper();
	if (moduleName == VRR || moduleName == VRR_CONT) {
		nSpace = getNSpaceByOper(oper);
	}
	if (resultIntegralHasAdditionalOffset(oper)) {
		nSpace += 2;
	}

	// finally, append these function calls to the main file
	CPP << endl;
	for(int i=0; i<(int)prototype.size(); i++) {
		string func = prototype[i];
		printLine(nSpace,func,CPP);
		CPP << endl;
	}
	CPP << endl;
}

void SQInts::formWorkFile(int moduleName) const
{
	// let's see how many sub files we have for this given module
	// except VRR, VRR only have one module
	int iFile = 1;
	int nFiles = 0;
	bool onlyWithFuncName = false;
	bool inFinalDir = false;
	while(true) {
		string fileName = infor.getWorkFuncName(onlyWithFuncName,moduleName,iFile,inFinalDir);
		path cppFile(fileName.c_str());
		if (exists(cppFile)) {
			nFiles++;
		}else{
			break;
		}
		iFile++;
	}

	// we should not have 0 sub files
	// because it already in file split mode
	if (nFiles == 0) {
		crash(true,"why there are no subfiles in SQInts::formWorkFile");
	}

	// deriv the function prototype
	// get the name
	int module = -1;
	if (moduleName == VRR) {
		module = VRR_FUNC_STATEMENT;
	}else if (moduleName == VRR_CONT) {
		module = VRR_CONT_STATEMENT;
	}else if (moduleName == HRR1) {
		module = HRR1_FUNC_STATEMENT;
	}else if (moduleName == HRR2) {
		module = HRR2_FUNC_STATEMENT;
	}else if (moduleName == NON_RR) {
		module = NON_RR_FUNC_STATEMENT;
	}else if (moduleName == DERIV) {
		module = DERIV_FUNC_STATEMENT;
	}else {
		crash(true,"the input module name is invalid in SQInts::formWorkFile");
	}

	// now let's deriv the prototype
	vector<string> prototype;
	prototype.reserve(nFiles);
	string pro = infor.getWorkFuncName(onlyWithFuncName,module,-1,inFinalDir);
	path p(pro.c_str());
	if (! exists(p)) {
		cout << "Missing function prototype file " << pro << endl;
		crash(true, "Why the function prototype file does not exist??? we need it in formWorkFile");
	}
	ifstream PRO;
	PRO.open(pro.c_str(),ios::in);
	string line;
	while(getline(PRO,line)) {
		if (line.find("void") == std::string::npos) continue;
		if (line.find(";") != std::string::npos) {
			int pos = line.find(";");
			line[pos] = ' ';
		}
		prototype.push_back(line);
	}
	PRO.close();

	// also check the prototype number
	if (prototype.size() != (size_t)nFiles) {
		cout << "module name " << moduleName << endl;
		cout << "prototype size " << prototype.size() << endl;
		cout << "nFiles " << nFiles << endl;
		crash(true,"the prototype number does not match nFiles in SQInts::formWorkFile");
	}

	// now let's form every subfile
	inFinalDir = true;
	for(iFile = 1; iFile<=nFiles; iFile++) {

		// get the work file name
		int fileIndex = iFile;
		string fileName = infor.getWorkFuncName(onlyWithFuncName,moduleName,fileIndex,inFinalDir);

		// now let's open it
		ofstream CPP;
		CPP.open(fileName.c_str(),ios::out);

		// firstly create the include part
		infor.headPrinting(CPP);

		// now print the function prototyoe
		string pro = prototype[iFile-1];
		CPP << pro << endl;

		// add in the "{" so that to start the main body
		CPP << "{" << endl;

		// now let's open the work file
		string work = infor.getWorkFuncName(onlyWithFuncName,moduleName,fileIndex,false);

		// check existance
		path p(work.c_str());
		if (! exists(p)) {
			cout << "Missing work file " << work << endl;
			crash(true, "Why the main body code file does not exist??? we need it in formWorkFile");
		}

		// now let's open it
		ifstream workFile;
		string line;
		workFile.open(work.c_str(),ios::in);
		while(getline(workFile,line)) {
			CPP << line << endl;
		}
		workFile.close();

		// close the cpp file
		CPP << "}" << endl;
		CPP.close();
	}
}

void SQInts::appendFile(int moduleName, ofstream& CPP, bool checkExist) const
{
	// according to the module and status,
	// get the work file name
	string file = infor.getWorkFuncName(false,moduleName);

	// check existance
	// if we do not have this file, we do not do a crash
	// just return
	path p(file.c_str());
	if (! exists(p)) {
		if (checkExist) {
			cout << "Missing file " << file << endl;
			crash(true, "Why the file does not exist??? we need it in appendFile function of sqints");
		}else{
			return;
		}
	}

	// now let's open it
	ifstream workFile;
	workFile.open(file.c_str(),ios::in);
	string line;
	while(getline(workFile,line)) {
		CPP << line << endl;
	}
	workFile.close();
}

void SQInts::codeGeneration()
{
	// generate the tmp folder
	// the work dir name should be same with
	// the one used in function of getProjectFileDir
	int intOperator = infor.getOper();
	int jobOrder    = infor.getJobOrder();
	string workDir = infor.getProjectTmpFileDir(intOperator,jobOrder);
	path tmpWorkDir(workDir.c_str());
	create_directory(tmpWorkDir);

	// generate the codes
	intCodeGeneration();

	// now assemble cpp files
	assembleCPPFiles();

	// this is debugging codes
	//const vector<int> shellCodes = infor.getShellCodeArray();
	//if (intOperator == THREEBODYKI && shellCodes[0] == 3 && shellCodes[1] == 3 && shellCodes[2] == 3) {
	//	crash(true, "job complete");
	//}

	// finally, clean the tmp file
	remove_all(tmpWorkDir);
}


/* ****************************************************************** **
**    OpenSees - Open System for Earthquake Engineering Simulation    **
**          Pacific Earthquake Engineering Research Center            **
**                                                                    **
**                                                                    **
** (C) Copyright 1999, The Regents of the University of California    **
** All Rights Reserved.                                               **
**                                                                    **
** Commercial use of this program without express permission of the   **
** University of California, Berkeley, is strictly prohibited.  See   **
** file 'COPYRIGHT'  in main directory for information on usage and   **
** redistribution,  and for a DISCLAIMER OF ALL WARRANTIES.           **
**                                                                    **
** Developed by:                                                      **
**   Frank McKenna (fmckenna@ce.berkeley.edu)                         **
**   Gregory L. Fenves (fenves@ce.berkeley.edu)                       **
**   Filip C. Filippou (filippou@ce.berkeley.edu)                     **
**                                                                    **
** ****************************************************************** */

// $Revision: 1.3 $
// $Date: 2009-07-29 21:57:42 $
// $Source: /usr/local/cvs/OpenSees/SRC/analysis/algorithm/eigenAlgo/FrequencyAlgo.cpp,v $

// File: ~/analysis/algorithm/eigenAlgo/FrequencyAlgo.C
//
// Written: Jun Peng
// Created: Mon Feb. 8, 1999
// Revision: A
//
// Description: This file contains the class definition of FrequencyAlgo.
// FrequencyAlgo is a class which performs a eigen solution algorithm
// to solve the Generalized eigen equations. It is not expected that
// this class will have subclasses.
//
// This class is inheritanted from the base class of SolutionAlgorithm
// which was created by fmk (Frank).

#include <FrequencyAlgo.h>
#include <AnalysisModel.h>
#include <EigenAnalysis.h>
#include <EigenIntegrator.h>
#include <EigenSOE.h>
#include <Vector.h>
#include <Channel.h>
#include <FEM_ObjectBroker.h>
#include <Timer.h>

FrequencyAlgo::FrequencyAlgo()
  :EigenAlgorithm(EigenALGORITHM_TAGS_Frequency)
{
    // do nothing here.
}

FrequencyAlgo::~FrequencyAlgo()
{
    // do nothing here.
}

int
FrequencyAlgo::solveCurrentStep(int numModes)
{
    AnalysisModel *theModel = this->getAnalysisModelPtr();
    EigenSOE *theSOE = this->getEigenSOEptr();
    EigenIntegrator *theIntegrator = this->getEigenIntegratorPtr();

    if ((theModel == 0) || (theIntegrator == 0) || (theSOE == 0)) {
       opserr << "WARNING FrequencyAlgo::solverCurrentStep() - ";
       opserr << "setLinks() has not been called. \n";
       return -1;
    }

    if (theIntegrator->formK() < 0) {
       opserr << "WARNING FrequencyAlgo::solverCurrentStep() - ";
       opserr << "the Integrator failed in formK().\n";
       return -2;
    }

    if (theIntegrator->formM() < 0) {
       opserr << "WARNING FrequencyAlgo::solverCurrentStep() - ";
       opserr << "the Integrator failed in formK().\n";
       return -3;
    }

    if (theSOE->solve(numModes, true) < 0) {
       opserr << "Warning FrequencyAlgo::solveCurrentStep() - ";
       opserr << "the EigenSOE failed in solve().\n";
       return -4;
    }

    // now set the eigenvalues and eigenvectors in the model
    theModel->setNumEigenvectors(numModes);
    Vector theEigenvalues(numModes);
    for (int i=1; i<=numModes; i++) {
	theEigenvalues[i-1] = theSOE->getEigenvalue(i);
	theModel->setEigenvector(i, theSOE->getEigenvector(i));
    }
    theModel->setEigenvalues(theEigenvalues);

    return 0;
}

int
FrequencyAlgo::sendSelf(int cTag, Channel &theChannel)
{
    return 0;
}

int
FrequencyAlgo::recvSelf(int cTag, Channel &theChannel,
			FEM_ObjectBroker &theBroker)
{
    return 0;
}

void
FrequencyAlgo::Print(OPS_Stream &s, int flag)
{
    s << "\t Eigen Algorithm \n";
}


/*!
@file
Defines `boost::hana::demux`.

@copyright Louis Dionne 2013-2017
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
 */

#ifndef BOOST_HANA_FUNCTIONAL_DEMUX_HPP
#define BOOST_HANA_FUNCTIONAL_DEMUX_HPP

#include <boost/hana/basic_tuple.hpp>
#include <boost/hana/config.hpp>
#include <boost/hana/detail/decay.hpp>

#include <cstddef>
#include <utility>

BOOST_HANA_NAMESPACE_BEGIN
    //! @ingroup group-functional
    //! Invoke a function with the results of invoking other functions
    //! on its arguments.
    //!
    //! Specifically, `demux(f)(g...)` is a function such that
    //! @code
    //!     demux(f)(g...)(x...) == f(g(x...)...)
    //! @endcode
    //!
    //! Each `g` is called with all the arguments, and then `f` is called
    //! with the result of each `g`. Hence, the arity of `f` must match
    //! the number of `g`s.
    //!
    //! This is called `demux` because of a vague similarity between this
    //! device and a demultiplexer in signal processing. `demux` takes what
    //! can be seen as a continuation (`f`), a bunch of functions to split a
    //! signal (`g...`) and zero or more arguments representing the signal
    //! (`x...`). Then, it calls the continuation with the result of
    //! splitting the signal with whatever functions where given.
    //!
    //! @note
    //! When used with two functions only, `demux` is associative. In other
    //! words (and noting `demux(f, g) = demux(f)(g)` to ease the notation),
    //! it is true that `demux(demux(f, g), h) == demux(f, demux(g, h))`.
    //!
    //!
    //! Signature
    //! ---------
    //! The signature of `demux` is
    //! \f[
    //!     \mathtt{demux} :
    //!         (B_1 \times \dotsb \times B_n \to C)
    //!             \to ((A_1 \times \dotsb \times A_n \to B_1)
    //!                 \times \dotsb
    //!                 \times (A_1 \times \dotsb \times A_n \to B_n))
    //!             \to (A_1 \times \dotsb \times A_n \to C)
    //! \f]
    //!
    //! This can be rewritten more tersely as
    //! \f[
    //!     \mathtt{demux} :
    //!         \left(\prod_{i=1}^n B_i \to C \right)
    //!         \to \prod_{j=1}^n \left(\prod_{i=1}^n A_i \to B_j \right)
    //!         \to \left(\prod_{i=1}^n A_i \to C \right)
    //! \f]
    //!
    //!
    //! Link with normal composition
    //! ----------------------------
    //! The signature of `compose` is
    //! \f[
    //!     \mathtt{compose} : (B \to C) \times (A \to B) \to (A \to C)
    //! \f]
    //!
    //! A valid observation is that this coincides exactly with the type
    //! of `demux` when used with a single unary function. Actually, both
    //! functions are equivalent:
    //! @code
    //!     demux(f)(g)(x) == compose(f, g)(x)
    //! @endcode
    //!
    //! However, let's now consider the curried version of `compose`,
    //! `curry<2>(compose)`:
    //! \f[
    //!     \mathtt{curry_2(compose)} : (B \to C) \to ((A \to B) \to (A \to C))
    //! \f]
    //!
    //! For the rest of this explanation, we'll just consider the curried
    //! version of `compose` and so we'll use `compose` instead of
    //! `curry<2>(compose)` to lighten the notation. With currying, we can
    //! now consider `compose` applied to itself:
    //! \f[
    //!     \mathtt{compose(compose, compose)} :
    //!         (B \to C) \to (A_1 \to A_2 \to B) \to (A_1 \to A_2 \to C)
    //! \f]
    //!
    //! If we uncurry deeply the above expression, we obtain
    //! \f[
    //!     \mathtt{compose(compose, compose)} :
    //!         (B \to C) \times (A_1 \times A_2 \to B) \to (A_1 \times A_2 \to C)
    //! \f]
    //!
    //! This signature is exactly the same as that of `demux` when given a
    //! single binary function, and indeed they are equivalent definitions.
    //! We can also generalize this further by considering
    //! `compose(compose(compose, compose), compose)`:
    //! \f[
    //!     \mathtt{compose(compose(compose, compose), compose)} :
    //!         (B \to C) \to (A_1 \to A_2 \to A_3 \to B)
    //!             \to (A_1 \to A_2 \to A_3 \to C)
    //! \f]
    //!
    //! which uncurries to
    //! \f[
    //!     \mathtt{compose(compose(compose, compose), compose)} :
    //!         (B \to C) \times (A_1 \times A_2 \times A_3 \to B)
    //!             \to (A_1 \times A_2 \times A_3 \to C)
    //! \f]
    //!
    //! This signature is exactly the same as that of `demux` when given a
    //! single ternary function. Hence, for a single n-ary function `g`,
    //! `demux(f)(g)` is equivalent to the n-times composition of `compose`
    //! with itself, applied to `g` and `f`:
    //! @code
    //!     demux(f)(g) == fold_left([compose, ..., compose], id, compose)(g, f)
    //!                           //  ^^^^^^^^^^^^^^^^^^^^^ n times
    //! @endcode
    //!
    //! More information on this insight can be seen [here][1]. Also, I'm
    //! not sure how this insight could be generalized to more than one
    //! function `g`, or if that is even possible.
    //!
    //!
    //! Proof of associativity in the binary case
    //! -----------------------------------------
    //! As explained above, `demux` is associative when it is used with
    //! two functions only. Indeed, given functions `f`, `g` and `h` with
    //! suitable signatures, we have
    //! @code
    //!     demux(f)(demux(g)(h))(x...) == f(demux(g)(h)(x...))
    //!                                 == f(g(h(x...)))
    //! @endcode
    //!
    //! On the other hand, we have
    //! @code
    //!     demux(demux(f)(g))(h)(x...) == demux(f)(g)(h(x...))
    //!                                 == f(g(h(x...)))
    //! @endcode
    //!
    //! and hence `demux` is associative in the binary case.
    //!
    //!
    //! Example
    //! -------
    //! @include example/functional/demux.cpp
    //!
    //! [1]: http://stackoverflow.com/q/5821089/627587
#ifdef BOOST_HANA_DOXYGEN_INVOKED
    constexpr auto demux = [](auto&& f) {
        return [perfect-capture](auto&& ...g) {
            return [perfect-capture](auto&& ...x) -> decltype(auto) {
                // x... can't be forwarded unless there is a single g
                // function, or that could cause double-moves.
                return forwarded(f)(forwarded(g)(x...)...);
            };
        };
    };
#else
    template <typename F>
    struct pre_demux_t;

    struct make_pre_demux_t {
        struct secret { };
        template <typename F>
        constexpr pre_demux_t<typename detail::decay<F>::type> operator()(F&& f) const {
            return {static_cast<F&&>(f)};
        }
    };

    template <typename Indices, typename F, typename ...G>
    struct demux_t;

    template <typename F>
    struct pre_demux_t {
        F f;

        template <typename ...G>
        constexpr demux_t<std::make_index_sequence<sizeof...(G)>, F,
                          typename detail::decay<G>::type...>
        operator()(G&& ...g) const& {
            return {make_pre_demux_t::secret{}, this->f, static_cast<G&&>(g)...};
        }

        template <typename ...G>
        constexpr demux_t<std::make_index_sequence<sizeof...(G)>, F,
                          typename detail::decay<G>::type...>
        operator()(G&& ...g) && {
            return {make_pre_demux_t::secret{}, static_cast<F&&>(this->f), static_cast<G&&>(g)...};
        }
    };

    template <std::size_t ...n, typename F, typename ...G>
    struct demux_t<std::index_sequence<n...>, F, G...> {
        template <typename ...T>
        constexpr demux_t(make_pre_demux_t::secret, T&& ...t)
            : storage_{static_cast<T&&>(t)...}
        { }

        basic_tuple<F, G...> storage_;

        template <typename ...X>
        constexpr decltype(auto) operator()(X&& ...x) const& {
            return hana::at_c<0>(storage_)(
                hana::at_c<n+1>(storage_)(x...)...
            );
        }

        template <typename ...X>
        constexpr decltype(auto) operator()(X&& ...x) & {
            return hana::at_c<0>(storage_)(
                hana::at_c<n+1>(storage_)(x...)...
            );
        }

        template <typename ...X>
        constexpr decltype(auto) operator()(X&& ...x) && {
            return static_cast<F&&>(hana::at_c<0>(storage_))(
                static_cast<G&&>(hana::at_c<n+1>(storage_))(x...)...
            );
        }
    };

    template <typename F, typename G>
    struct demux_t<std::index_sequence<0>, F, G> {
        template <typename ...T>
        constexpr demux_t(make_pre_demux_t::secret, T&& ...t)
            : storage_{static_cast<T&&>(t)...}
        { }

        basic_tuple<F, G> storage_;

        template <typename ...X>
        constexpr decltype(auto) operator()(X&& ...x) const& {
            return hana::at_c<0>(storage_)(
                hana::at_c<1>(storage_)(static_cast<X&&>(x)...)
            );
        }

        template <typename ...X>
        constexpr decltype(auto) operator()(X&& ...x) & {
            return hana::at_c<0>(storage_)(
                hana::at_c<1>(storage_)(static_cast<X&&>(x)...)
            );
        }

        template <typename ...X>
        constexpr decltype(auto) operator()(X&& ...x) && {
            return static_cast<F&&>(hana::at_c<0>(storage_))(
                static_cast<G&&>(hana::at_c<1>(storage_))(static_cast<X&&>(x)...)
            );
        }
    };

    constexpr make_pre_demux_t demux{};
#endif
BOOST_HANA_NAMESPACE_END

#endif // !BOOST_HANA_FUNCTIONAL_DEMUX_HPP


// Copyright (c) 2008-2016 Emil Dotchevski and Reverge Studios, Inc.

// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#ifndef UUID_3EDF999CA1C011DEBA5C8DA956D89593
#define UUID_3EDF999CA1C011DEBA5C8DA956D89593

#include <boost/qvm/assert.hpp>
#include <boost/qvm/deduce_mat.hpp>
#include <boost/qvm/enable_if.hpp>
#include <boost/qvm/inline.hpp>
#include <boost/qvm/vec_traits.hpp>

namespace boost {
namespace qvm {
////////////////////////////////////////////////

namespace qvm_detail {
template <class OriginalVector> class col_mat_ {
  col_mat_(col_mat_ const &);
  col_mat_ &operator=(col_mat_ const &);
  ~col_mat_();

public:
  template <class T> BOOST_QVM_INLINE_TRIVIAL col_mat_ &operator=(T const &x) {
    assign(*this, x);
    return *this;
  }

  template <class R> BOOST_QVM_INLINE_TRIVIAL operator R() const {
    R r;
    assign(r, *this);
    return r;
  }
};
} // namespace qvm_detail

template <class OriginalVector>
struct mat_traits<qvm_detail::col_mat_<OriginalVector>> {
  typedef qvm_detail::col_mat_<OriginalVector> this_matrix;
  typedef typename vec_traits<OriginalVector>::scalar_type scalar_type;
  static int const rows = vec_traits<OriginalVector>::dim;
  static int const cols = 1;

  template <int Row, int Col>
  static BOOST_QVM_INLINE_CRITICAL scalar_type
  read_element(this_matrix const &x) {
    BOOST_QVM_STATIC_ASSERT(Col == 0);
    BOOST_QVM_STATIC_ASSERT(Row >= 0);
    BOOST_QVM_STATIC_ASSERT(Row < rows);
    return vec_traits<OriginalVector>::template read_element<Row>(
        reinterpret_cast<OriginalVector const &>(x));
  }

  template <int Row, int Col>
  static BOOST_QVM_INLINE_CRITICAL scalar_type &write_element(this_matrix &x) {
    BOOST_QVM_STATIC_ASSERT(Col == 0);
    BOOST_QVM_STATIC_ASSERT(Row >= 0);
    BOOST_QVM_STATIC_ASSERT(Row < rows);
    return vec_traits<OriginalVector>::template write_element<Row>(
        reinterpret_cast<OriginalVector &>(x));
  }

  static BOOST_QVM_INLINE_CRITICAL scalar_type
  read_element_idx(int row, int col, this_matrix const &x) {
    BOOST_QVM_ASSERT(col == 0);
    BOOST_QVM_ASSERT(row >= 0);
    BOOST_QVM_ASSERT(row < rows);
    return vec_traits<OriginalVector>::read_element_idx(
        row, reinterpret_cast<OriginalVector const &>(x));
  }

  static BOOST_QVM_INLINE_CRITICAL scalar_type &
  write_element_idx(int row, int col, this_matrix &x) {
    BOOST_QVM_ASSERT(col == 0);
    BOOST_QVM_ASSERT(row >= 0);
    BOOST_QVM_ASSERT(row < rows);
    return vec_traits<OriginalVector>::write_element_idx(
        row, reinterpret_cast<OriginalVector &>(x));
  }
};

template <class OriginalVector, int R, int C>
struct deduce_mat<qvm_detail::col_mat_<OriginalVector>, R, C> {
  typedef mat<typename vec_traits<OriginalVector>::scalar_type, R, C> type;
};

template <class OriginalVector, int R, int C>
struct deduce_mat2<qvm_detail::col_mat_<OriginalVector>,
                   qvm_detail::col_mat_<OriginalVector>, R, C> {
  typedef mat<typename vec_traits<OriginalVector>::scalar_type, R, C> type;
};

template <class A>
typename boost::enable_if_c<is_vec<A>::value,
                            qvm_detail::col_mat_<A> const &>::type
    BOOST_QVM_INLINE_TRIVIAL
    col_mat(A const &a) {
  return reinterpret_cast<typename qvm_detail::col_mat_<A> const &>(a);
}

template <class A>
typename boost::enable_if_c<is_vec<A>::value, qvm_detail::col_mat_<A> &>::type
    BOOST_QVM_INLINE_TRIVIAL
    col_mat(A &a) {
  return reinterpret_cast<typename qvm_detail::col_mat_<A> &>(a);
}

////////////////////////////////////////////////

namespace qvm_detail {
template <class OriginalVector> class row_mat_ {
  row_mat_(row_mat_ const &);
  row_mat_ &operator=(row_mat_ const &);
  ~row_mat_();

public:
  template <class T> BOOST_QVM_INLINE_TRIVIAL row_mat_ &operator=(T const &x) {
    assign(*this, x);
    return *this;
  }

  template <class R> BOOST_QVM_INLINE_TRIVIAL operator R() const {
    R r;
    assign(r, *this);
    return r;
  }
};
} // namespace qvm_detail

template <class OriginalVector>
struct mat_traits<qvm_detail::row_mat_<OriginalVector>> {
  typedef qvm_detail::row_mat_<OriginalVector> this_matrix;
  typedef typename vec_traits<OriginalVector>::scalar_type scalar_type;
  static int const rows = 1;
  static int const cols = vec_traits<OriginalVector>::dim;

  template <int Row, int Col>
  static BOOST_QVM_INLINE_CRITICAL scalar_type
  read_element(this_matrix const &x) {
    BOOST_QVM_STATIC_ASSERT(Row == 0);
    BOOST_QVM_STATIC_ASSERT(Col >= 0);
    BOOST_QVM_STATIC_ASSERT(Col < cols);
    return vec_traits<OriginalVector>::template read_element<Col>(
        reinterpret_cast<OriginalVector const &>(x));
  }

  template <int Row, int Col>
  static BOOST_QVM_INLINE_CRITICAL scalar_type &write_element(this_matrix &x) {
    BOOST_QVM_STATIC_ASSERT(Row == 0);
    BOOST_QVM_STATIC_ASSERT(Col >= 0);
    BOOST_QVM_STATIC_ASSERT(Col < cols);
    return vec_traits<OriginalVector>::template write_element<Col>(
        reinterpret_cast<OriginalVector &>(x));
  }

  static BOOST_QVM_INLINE_CRITICAL scalar_type
  read_element_idx(int row, int col, this_matrix const &x) {
    BOOST_QVM_ASSERT(row == 0);
    BOOST_QVM_ASSERT(col >= 0);
    BOOST_QVM_ASSERT(col < cols);
    return vec_traits<OriginalVector>::read_element_idx(
        col, reinterpret_cast<OriginalVector const &>(x));
  }

  static BOOST_QVM_INLINE_CRITICAL scalar_type &
  write_element_idx(int row, int col, this_matrix &x) {
    BOOST_QVM_ASSERT(row == 0);
    BOOST_QVM_ASSERT(col >= 0);
    BOOST_QVM_ASSERT(col < cols);
    return vec_traits<OriginalVector>::write_element_idx(
        col, reinterpret_cast<OriginalVector &>(x));
  }
};

template <class OriginalVector, int R, int C>
struct deduce_mat<qvm_detail::row_mat_<OriginalVector>, R, C> {
  typedef mat<typename vec_traits<OriginalVector>::scalar_type, R, C> type;
};

template <class OriginalVector, int R, int C>
struct deduce_mat2<qvm_detail::row_mat_<OriginalVector>,
                   qvm_detail::row_mat_<OriginalVector>, R, C> {
  typedef mat<typename vec_traits<OriginalVector>::scalar_type, R, C> type;
};

template <class A>
typename boost::enable_if_c<is_vec<A>::value,
                            qvm_detail::row_mat_<A> const &>::type
    BOOST_QVM_INLINE_TRIVIAL
    row_mat(A const &a) {
  return reinterpret_cast<typename qvm_detail::row_mat_<A> const &>(a);
}

template <class A>
typename boost::enable_if_c<is_vec<A>::value, qvm_detail::row_mat_<A> &>::type
    BOOST_QVM_INLINE_TRIVIAL
    row_mat(A &a) {
  return reinterpret_cast<typename qvm_detail::row_mat_<A> &>(a);
}

////////////////////////////////////////////////

namespace qvm_detail {
template <class OriginalVector> class translation_mat_ {
  translation_mat_(translation_mat_ const &);
  translation_mat_ &operator=(translation_mat_ const &);
  ~translation_mat_();

public:
  template <class T>
  BOOST_QVM_INLINE_TRIVIAL translation_mat_ &operator=(T const &x) {
    assign(*this, x);
    return *this;
  }

  template <class R> BOOST_QVM_INLINE_TRIVIAL operator R() const {
    R r;
    assign(r, *this);
    return r;
  }
};

template <class M, int Row, int Col,
          bool TransCol = (Col == mat_traits<M>::cols - 1)>
struct read_translation_matat;

template <class OriginalVector, int Row, int Col, bool TransCol>
struct read_translation_matat<translation_mat_<OriginalVector>, Row, Col,
                              TransCol> {
  static BOOST_QVM_INLINE_CRITICAL
      typename mat_traits<translation_mat_<OriginalVector>>::scalar_type
      f(translation_mat_<OriginalVector> const &) {
    return scalar_traits<typename mat_traits<
        translation_mat_<OriginalVector>>::scalar_type>::value(0);
  }
};

template <class OriginalVector, int D>
struct read_translation_matat<translation_mat_<OriginalVector>, D, D, false> {
  static BOOST_QVM_INLINE_CRITICAL
      typename mat_traits<translation_mat_<OriginalVector>>::scalar_type
      f(translation_mat_<OriginalVector> const &) {
    return scalar_traits<typename mat_traits<
        translation_mat_<OriginalVector>>::scalar_type>::value(1);
  }
};

template <class OriginalVector, int D>
struct read_translation_matat<translation_mat_<OriginalVector>, D, D, true> {
  static BOOST_QVM_INLINE_CRITICAL
      typename mat_traits<translation_mat_<OriginalVector>>::scalar_type
      f(translation_mat_<OriginalVector> const &) {
    return scalar_traits<typename mat_traits<
        translation_mat_<OriginalVector>>::scalar_type>::value(1);
  }
};

template <class OriginalVector, int Row, int Col>
struct read_translation_matat<translation_mat_<OriginalVector>, Row, Col,
                              true> {
  static BOOST_QVM_INLINE_CRITICAL
      typename mat_traits<translation_mat_<OriginalVector>>::scalar_type
      f(translation_mat_<OriginalVector> const &x) {
    return vec_traits<OriginalVector>::template read_element<Row>(
        reinterpret_cast<OriginalVector const &>(x));
  }
};
} // namespace qvm_detail

template <class OriginalVector>
struct mat_traits<qvm_detail::translation_mat_<OriginalVector>> {
  typedef qvm_detail::translation_mat_<OriginalVector> this_matrix;
  typedef typename vec_traits<OriginalVector>::scalar_type scalar_type;
  static int const rows = vec_traits<OriginalVector>::dim + 1;
  static int const cols = vec_traits<OriginalVector>::dim + 1;

  template <int Row, int Col>
  static BOOST_QVM_INLINE_CRITICAL scalar_type
  read_element(this_matrix const &x) {
    BOOST_QVM_STATIC_ASSERT(Row >= 0);
    BOOST_QVM_STATIC_ASSERT(Row < rows);
    BOOST_QVM_STATIC_ASSERT(Col >= 0);
    BOOST_QVM_STATIC_ASSERT(Col < cols);
    return qvm_detail::read_translation_matat<
        qvm_detail::translation_mat_<OriginalVector>, Row, Col>::f(x);
  }

  template <int Row, int Col>
  static BOOST_QVM_INLINE_CRITICAL scalar_type &write_element(this_matrix &x) {
    BOOST_QVM_STATIC_ASSERT(Row >= 0);
    BOOST_QVM_STATIC_ASSERT(Row < rows);
    BOOST_QVM_STATIC_ASSERT(Col == cols - 1);
    BOOST_QVM_STATIC_ASSERT(Col != Row);
    return vec_traits<OriginalVector>::template write_element<Row>(
        reinterpret_cast<OriginalVector &>(x));
  }

  static BOOST_QVM_INLINE_CRITICAL scalar_type
  read_element_idx(int row, int col, this_matrix const &x) {
    BOOST_QVM_ASSERT(row >= 0);
    BOOST_QVM_ASSERT(row < rows);
    BOOST_QVM_ASSERT(col >= 0);
    BOOST_QVM_ASSERT(col < cols);
    return row == col
               ? scalar_traits<scalar_type>::value(1)
               : (col == cols - 1
                      ? vec_traits<OriginalVector>::read_element_idx(
                            row, reinterpret_cast<OriginalVector const &>(x))
                      : scalar_traits<scalar_type>::value(0));
  }

  static BOOST_QVM_INLINE_CRITICAL scalar_type &
  write_element_idx(int row, int col, this_matrix const &x) {
    BOOST_QVM_ASSERT(row >= 0);
    BOOST_QVM_ASSERT(row < rows);
    BOOST_QVM_ASSERT(col == cols - 1);
    BOOST_QVM_ASSERT(col != row);
    return vec_traits<OriginalVector>::write_element_idx(
        row, reinterpret_cast<OriginalVector &>(x));
  }
};

template <class OriginalVector, int R, int C>
struct deduce_mat<qvm_detail::translation_mat_<OriginalVector>, R, C> {
  typedef mat<typename vec_traits<OriginalVector>::scalar_type, R, C> type;
};

template <class OriginalVector, int R, int C>
struct deduce_mat2<qvm_detail::translation_mat_<OriginalVector>,
                   qvm_detail::translation_mat_<OriginalVector>, R, C> {
  typedef mat<typename vec_traits<OriginalVector>::scalar_type, R, C> type;
};

template <class A>
typename boost::enable_if_c<is_vec<A>::value,
                            qvm_detail::translation_mat_<A> const &>::type
    BOOST_QVM_INLINE_TRIVIAL
    translation_mat(A const &a) {
  return reinterpret_cast<typename qvm_detail::translation_mat_<A> const &>(a);
}

template <class A>
typename boost::enable_if_c<is_vec<A>::value,
                            qvm_detail::translation_mat_<A> &>::type
    BOOST_QVM_INLINE_TRIVIAL
    translation_mat(A &a) {
  return reinterpret_cast<typename qvm_detail::translation_mat_<A> &>(a);
}

////////////////////////////////////////////////

namespace qvm_detail {
template <class OriginalVector> class diag_mat_ {
  diag_mat_(diag_mat_ const &);
  diag_mat_ &operator=(diag_mat_ const &);
  ~diag_mat_();

public:
  template <class T> BOOST_QVM_INLINE_TRIVIAL diag_mat_ &operator=(T const &x) {
    assign(*this, x);
    return *this;
  }

  template <class R> BOOST_QVM_INLINE_TRIVIAL operator R() const {
    R r;
    assign(r, *this);
    return r;
  }
};
} // namespace qvm_detail

template <class OriginalVector>
struct mat_traits<qvm_detail::diag_mat_<OriginalVector>> {
  typedef qvm_detail::diag_mat_<OriginalVector> this_matrix;
  typedef typename vec_traits<OriginalVector>::scalar_type scalar_type;
  static int const rows = vec_traits<OriginalVector>::dim;
  static int const cols = vec_traits<OriginalVector>::dim;

  template <int Row, int Col>
  static BOOST_QVM_INLINE_CRITICAL scalar_type
  read_element(this_matrix const &x) {
    BOOST_QVM_STATIC_ASSERT(Row >= 0);
    BOOST_QVM_STATIC_ASSERT(Row < rows);
    BOOST_QVM_STATIC_ASSERT(Col >= 0);
    BOOST_QVM_STATIC_ASSERT(Col < cols);
    return Row == Col ? vec_traits<OriginalVector>::template read_element<Row>(
                            reinterpret_cast<OriginalVector const &>(x))
                      : scalar_traits<scalar_type>::value(0);
  }

  template <int Row, int Col>
  static BOOST_QVM_INLINE_CRITICAL scalar_type &write_element(this_matrix &x) {
    BOOST_QVM_STATIC_ASSERT(Row >= 0);
    BOOST_QVM_STATIC_ASSERT(Row < rows);
    BOOST_QVM_STATIC_ASSERT(Row == Col);
    return vec_traits<OriginalVector>::template write_element<Row>(
        reinterpret_cast<OriginalVector &>(x));
  }

  static BOOST_QVM_INLINE_CRITICAL scalar_type
  read_element_idx(int row, int col, this_matrix const &x) {
    BOOST_QVM_ASSERT(row >= 0);
    BOOST_QVM_ASSERT(row < rows);
    BOOST_QVM_ASSERT(col >= 0);
    BOOST_QVM_ASSERT(col < cols);
    return row == col ? vec_traits<OriginalVector>::read_element_idx(
                            row, reinterpret_cast<OriginalVector const &>(x))
                      : scalar_traits<scalar_type>::value(0);
  }

  static BOOST_QVM_INLINE_CRITICAL scalar_type &
  write_element_idx(int row, int col, this_matrix &x) {
    BOOST_QVM_ASSERT(row >= 0);
    BOOST_QVM_ASSERT(row < rows);
    BOOST_QVM_ASSERT(row == col);
    return vec_traits<OriginalVector>::write_element_idx(
        row, reinterpret_cast<OriginalVector &>(x));
  }
};

template <class OriginalVector, int R, int C>
struct deduce_mat<qvm_detail::diag_mat_<OriginalVector>, R, C> {
  typedef mat<typename vec_traits<OriginalVector>::scalar_type, R, C> type;
};

template <class OriginalVector, int R, int C>
struct deduce_mat2<qvm_detail::diag_mat_<OriginalVector>,
                   qvm_detail::diag_mat_<OriginalVector>, R, C> {
  typedef mat<typename vec_traits<OriginalVector>::scalar_type, R, C> type;
};

template <class A>
typename boost::enable_if_c<is_vec<A>::value,
                            qvm_detail::diag_mat_<A> const &>::type
    BOOST_QVM_INLINE_TRIVIAL
    diag_mat(A const &a) {
  return reinterpret_cast<typename qvm_detail::diag_mat_<A> const &>(a);
}

template <class A>
typename boost::enable_if_c<is_vec<A>::value, qvm_detail::diag_mat_<A> &>::type
    BOOST_QVM_INLINE_TRIVIAL
    diag_mat(A &a) {
  return reinterpret_cast<typename qvm_detail::diag_mat_<A> &>(a);
}

////////////////////////////////////////////////
} // namespace qvm
} // namespace boost

#endif