/*=============================================================================

  NifTK: A software platform for medical image computing.

  Copyright (c) University College London (UCL). All rights reserved.

  This software is distributed WITHOUT ANY WARRANTY; without even
  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
  PURPOSE.

  See LICENSE.txt in the top level directory for details.

  =============================================================================*/

/*!
 * \file niftkConvertRawDICOMMammogramsToPresentation.cxx
 * \page niftkConvertRawDICOMMammogramsToPresentation
 * \section niftkConvertRawDICOMMammogramsToPresentationSummary niftkConvertRawDICOMMammogramsToPresentation
 *
 * Search for raw "For Processing" DICOM mammograms in a directory and convert them to "For Presentation" versions by calculating the logarithm of their intensities and then the inverse.
 *
 */

#include <niftkFileHelper.h>
#include <niftkConversionUtils.h>
#include <itkCommandLineHelper.h>

#include <itkCreatePositiveMammogram.h>

#include <itkImage.h>
#include <itkImageFileReader.h>
#include <itkImageFileWriter.h>
#include <itkNifTKImageIOFactory.h>
#include <itkMetaDataDictionary.h>
#include <itkMetaDataObject.h>
#include <itkGDCMImageIO.h>
#include <itkCastImageFilter.h>
#include <itkMammogramFatSubtractionImageFilter.h>
#include <itkMammogramMaskSegmentationImageFilter.h>

#include <boost/filesystem/path.hpp>

#include <vector>

#include <niftkConvertRawDICOMMammogramsToPresentationCLP.h>

namespace fs = boost::filesystem;

typedef itk::MetaDataDictionary DictionaryType;
typedef itk::MetaDataObject< std::string > MetaDataStringType;

struct arguments
{
  std::string dcmDirectoryIn;
  std::string dcmDirectoryOut;
  std::string strAdd2Suffix;

  bool flgOverwrite;
  bool flgRescaleIntensitiesToMaxRange;
  bool flgVerbose;
  bool flgFatSubtract;

  std::string iterFilename;
};

// -------------------------------------------------------------------------
// PrintDictionary()
// -------------------------------------------------------------------------

void PrintDictionary( DictionaryType &dictionary )
{
  DictionaryType::ConstIterator tagItr = dictionary.Begin();
  DictionaryType::ConstIterator end = dictionary.End();

  while ( tagItr != end )
  {
    MetaDataStringType::ConstPointer entryvalue =
      dynamic_cast<const MetaDataStringType *>( tagItr->second.GetPointer() );

    if ( entryvalue )
    {
      std::string tagkey = tagItr->first;
      std::string tagID;
      bool found =  itk::GDCMImageIO::GetLabelFromTag( tagkey, tagID );

      std::string tagValue = entryvalue->GetMetaDataObjectValue();

      std::cout << tagkey << " " << tagID <<  ": " << tagValue << std::endl;
    }

    ++tagItr;
  }
};

// -------------------------------------------------------------------------
// AddPresentationFileSuffix
// -------------------------------------------------------------------------

std::string AddPresentationFileSuffix( std::string fileName, std::string strAdd2Suffix )
{
  std::string suffix;
  std::string newSuffix;

  if ( ( fileName.length() >= 4 ) &&
       ( fileName.substr( fileName.length() - 4 ) == std::string( ".dcm" ) ) )
  {
    suffix = std::string( ".dcm" );
  }

  else if ( ( fileName.length() >= 4 ) &&
            ( fileName.substr( fileName.length() - 4 ) == std::string( ".DCM" ) ) )
  {
    suffix = std::string( ".DCM" );
  }

  else if ( ( fileName.length() >= 6 ) &&
            ( fileName.substr( fileName.length() - 6 ) == std::string( ".dicom" ) ) )
  {
    suffix = std::string( ".dicom" );
  }

  else if ( ( fileName.length() >= 6 ) &&
            ( fileName.substr( fileName.length() - 6 ) == std::string( ".DICOM" ) ) )
  {
    suffix = std::string( ".DICOM" );
  }

  else if ( ( fileName.length() >= 4 ) &&
            ( fileName.substr( fileName.length() - 4 ) == std::string( ".IMA" ) ) )
  {
    suffix = std::string( ".IMA" );
  }

  std::cout << "Suffix: '" << suffix << "'" << std::endl;

  newSuffix = strAdd2Suffix + suffix;

  if ( ( fileName.length() >= newSuffix.length() ) &&
       ( fileName.substr( fileName.length() - newSuffix.length() ) != newSuffix ) )
  {
    return fileName.substr( 0, fileName.length() - suffix.length() ) + newSuffix;
  }
  else
  {
    return fileName;
  }
};

// -------------------------------------------------------------------------
// main()
// -------------------------------------------------------------------------

template <class OutputPixelType>
int DoMain(arguments args, OutputPixelType min, OutputPixelType max)
{
  float progress = 0.;
  float iFile = 0.;
  float nFiles;

  // Get the list of files in the directory
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

  std::vector< std::string > fileNames;
  niftk::GetRecursiveFilesInDirectory( args.dcmDirectoryIn, fileNames );

  nFiles = fileNames.size();

  // Iterate through each file and convert it
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

  std::string fileInputFullPath;
  std::string fileInputRelativePath;
  std::string fileOutputRelativePath;
  std::string fileOutputFullPath;
  std::string dirOutputFullPath;

  std::vector< std::string >::iterator iterFileNames;

  typedef float InternalPixelType;

  const unsigned int   InputDimension = 2;

  typedef itk::Image< InternalPixelType, InputDimension > InternalImageType;
  typedef itk::Image< OutputPixelType, InputDimension > OutputImageType;

  typedef itk::RescaleIntensityImageFilter< InternalImageType, InternalImageType > RescalerType;
  typedef itk::CastImageFilter< InternalImageType, OutputImageType > CastingFilterType;

  typedef itk::ImageFileReader< InternalImageType > ReaderType;
  typedef itk::ImageFileWriter< OutputImageType > WriterType;

  ReaderType::Pointer reader = ReaderType::New();
  InternalImageType::Pointer image;

  typedef itk::GDCMImageIO           ImageIOType;
  ImageIOType::Pointer gdcmImageIO = ImageIOType::New();

  progress = iFile/nFiles;
  std::cout << "<filter-progress>" << std::endl
            << progress << std::endl
            << "</filter-progress>" << std::endl;

  // Read the image

  reader->SetImageIO( gdcmImageIO );
  reader->SetFileName( args.iterFilename );

  try
  {
    reader->UpdateLargestPossibleRegion();
  }

  catch (itk::ExceptionObject &ex)
  {
    std::cout << "Skipping file (not DICOM?): " << args.iterFilename << std::endl;
    return EXIT_FAILURE;
  }

  std::cout << "File: " << args.iterFilename << std::endl;

  image = reader->GetOutput();
  image->DisconnectPipeline();

  DictionaryType dictionary = image->GetMetaDataDictionary();

  // Convert a raw DICOM mammogram to a presentation version by log inverting it

  if ( ! itk::ConvertMammogramFromRawToPresentation< InternalImageType >( image,
                                                                          dictionary ) )
  {
    return EXIT_FAILURE;
  }

  // Fat subtract the image?

  if ( args.flgFatSubtract )
  {
    // First compute the mask

    typedef unsigned char MaskPixelType;
    typedef itk::Image< MaskPixelType, InputDimension > MaskImageType;

    typedef itk::MammogramMaskSegmentationImageFilter<InternalImageType, MaskImageType>
      MammogramMaskSegmentationImageFilterType;

    typename MammogramMaskSegmentationImageFilterType::Pointer
      maskFilter = MammogramMaskSegmentationImageFilterType::New();

    maskFilter->SetInput( image );

    maskFilter->SetVerbose( args.flgVerbose );

    maskFilter->SetIncludeBorderRegion( true );

    try {
      maskFilter->Update();
    }
    catch( itk::ExceptionObject & err )
    {
      std::cerr << "ERROR: Failed to segment image" << std::endl
                << err << std::endl;
      return EXIT_FAILURE;
    }

    typename MaskImageType::Pointer mask = maskFilter->GetOutput();

    mask->DisconnectPipeline();

    // Then calculate the fat subtracted image

    typedef itk::MammogramFatSubtractionImageFilter<InternalImageType>
      MammogramFatSubtractionImageFilterType;

    typename MammogramFatSubtractionImageFilterType::Pointer
      fatFilter = MammogramFatSubtractionImageFilterType::New();

    fatFilter->SetInput( image );

    fatFilter->SetVerbose( args.flgVerbose );

    fatFilter->SetComputeFatEstimationFit( true );

    fatFilter->SetMask( mask );

    try
    {
      fatFilter->Update();
    }
    catch( itk::ExceptionObject & err )
    {
      std::cerr << "ERROR: Failed to calculate the fat subtraction: " << err << std::endl;
      return EXIT_FAILURE;
    }

    image = fatFilter->GetOutput( 0 );
    image->DisconnectPipeline();
  }

  // Rescale the image to the maximum range

  if ( args.flgRescaleIntensitiesToMaxRange )
  {
    itksys_ios::ostringstream value;

    typename RescalerType::Pointer intensityRescaler = RescalerType::New();

    intensityRescaler->SetOutputMinimum( min );
    intensityRescaler->SetOutputMaximum( max );

    // Set the pixel intensity relationship sign to linear
    value.str("");
    value << "LIN";
    itk::EncapsulateMetaData<std::string>(dictionary,"0028|1040", value.str());

    // Set the pixel intensity relationship sign to one
    value.str("");
    value << 1;
    itk::EncapsulateMetaData<std::string>(dictionary,"0028|1041", value.str());

    // Set the new window centre tag value
    value.str("");
    value << max / 2;
    itk::EncapsulateMetaData<std::string>(dictionary,"0028|1050", value.str());

    // Set the new window width tag value
    value.str("");
    value << max;
    itk::EncapsulateMetaData<std::string>(dictionary,"0028|1051", value.str());

    // Set the rescale intercept and slope to zero and one
    value.str("");
    value << 0;
    itk::EncapsulateMetaData<std::string>(dictionary, "0028|1052", value.str());
    value.str("");
    value << 1;
    itk::EncapsulateMetaData<std::string>(dictionary, "0028|1053", value.str());

    intensityRescaler->UpdateLargestPossibleRegion();

    image = intensityRescaler->GetOutput();
    image->DisconnectPipeline();
  }

  // Cast to the output type

  typename CastingFilterType::Pointer caster = CastingFilterType::New();

  caster->SetInput( image );

  caster->UpdateLargestPossibleRegion();

  // Create the output image filename

  fileInputFullPath = args.iterFilename;

  fileInputRelativePath = fileInputFullPath.substr( args.dcmDirectoryIn.length() );

  fileOutputRelativePath = AddPresentationFileSuffix( fileInputRelativePath,
                                                      args.strAdd2Suffix );

  fileOutputFullPath = niftk::ConcatenatePath( args.dcmDirectoryOut,
                                               fileOutputRelativePath );

  dirOutputFullPath = fs::path( fileOutputFullPath ).branch_path().string();

  if ( ! niftk::DirectoryExists( dirOutputFullPath ) )
  {
    niftk::CreateDirAndParents( dirOutputFullPath );
  }

  std::cout << "Input relative filename: " << fileInputRelativePath << std::endl
            << "Output relative filename: " << fileOutputRelativePath << std::endl
            << "Output directory: " << dirOutputFullPath << std::endl;

  // Write the image to the output file

  if ( niftk::FileIsRegular( fileOutputFullPath ) && ( ! args.flgOverwrite ) )
  {
    std::cerr << std::endl << "ERROR: File " << fileOutputFullPath << " exists"
              << std::endl << "       and can't be overwritten. Consider option: 'overwrite'."
              << std::endl << std::endl;
    return EXIT_FAILURE;
  }
  else
  {

    if ( args.flgVerbose )
    {
      PrintDictionary( dictionary );
    }

    typename WriterType::Pointer writer = WriterType::New();

    typename OutputImageType::Pointer outImage = caster->GetOutput();

    writer->SetFileName( fileOutputFullPath );

    outImage->DisconnectPipeline();
    writer->SetInput( outImage );

    gdcmImageIO->SetMetaDataDictionary( dictionary );
    gdcmImageIO->KeepOriginalUIDOn( );
    writer->SetImageIO( gdcmImageIO );

    writer->UseInputMetaDataDictionaryOff();

    try
    {
      std::cout << "Writing image to file: " << fileOutputFullPath << std::endl;
      writer->Update();
    }
    catch (itk::ExceptionObject & e)
    {
      std::cerr << "ERROR: Failed to write image: " << std::endl << e << std::endl;
      return EXIT_FAILURE;
    }
  }

  std::cout << std::endl;

  return EXIT_SUCCESS;
}

// -------------------------------------------------------------------------
// main()
// -------------------------------------------------------------------------

int main( int argc, char *argv[] )
{
  itk::NifTKImageIOFactory::Initialize();

  float progress = 0.;
  float iFile = 0.;
  float nFiles;

  struct arguments args;

  // Validate command line args
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~

  PARSE_ARGS;

  if ( dcmDirectoryIn.length() == 0 )
  {
    commandLine.getOutput()->usage(commandLine);
    std::cerr << "ERROR: The input directory must be specified" << std::endl;
    return EXIT_FAILURE;
  }

  if ( dcmDirectoryOut.length() == 0 )
  {
    dcmDirectoryOut = dcmDirectoryIn;
  }

  args.dcmDirectoryIn  = dcmDirectoryIn;
  args.dcmDirectoryOut = dcmDirectoryOut;

  args.strAdd2Suffix = strAdd2Suffix;

  args.flgOverwrite   = flgOverwrite;
  args.flgVerbose     = flgVerbose;
  args.flgFatSubtract = flgFatSubtract;

  args.flgRescaleIntensitiesToMaxRange = flgRescaleIntensitiesToMaxRange;

  std::cout << std::endl << "Examining directory: "
	    << args.dcmDirectoryIn << std::endl << std::endl;

  // Get the list of files in the directory
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

  std::vector< std::string > fileNames;
  std::vector< std::string >::iterator iterFileNames;

  niftk::GetRecursiveFilesInDirectory( dcmDirectoryIn, fileNames );

  nFiles = fileNames.size();

  for ( iterFileNames = fileNames.begin();
	iterFileNames < fileNames.end();
	++iterFileNames, iFile += 1. )
  {
    args.iterFilename = *iterFileNames;

    std::cout << "File: " << args.iterFilename << std::endl;

    progress = iFile/nFiles;
    std::cout << "<filter-progress>" << std::endl
	      << progress << std::endl
	      << "</filter-progress>" << std::endl;

    itk::ImageIOBase::Pointer imageIO;
    imageIO = itk::ImageIOFactory::CreateImageIO(args.iterFilename.c_str(),
						 itk::ImageIOFactory::ReadMode);

    if ( ( ! imageIO ) || ( ! imageIO->CanReadFile( args.iterFilename.c_str() ) ) )
    {
      std::cerr << "WARNING: Unrecognised image type, skipping file: "
		<< args.iterFilename << std::endl;
      continue;
    }

    int result;

    switch (itk::PeekAtComponentType(args.iterFilename))
    {
    case itk::ImageIOBase::UCHAR:
      result = DoMain<unsigned char>( args,
                                      itk::NumericTraits<unsigned char>::ZeroValue(),
                                      itk::NumericTraits<unsigned char>::max() );
      break;

    case itk::ImageIOBase::CHAR:
      result = DoMain<char>( args,
                             itk::NumericTraits<char>::ZeroValue(),
                             itk::NumericTraits<char>::max() );
      break;

    case itk::ImageIOBase::USHORT:
      result = DoMain<unsigned short>( args,
                                       itk::NumericTraits<unsigned short>::ZeroValue(),
                                       static_cast<unsigned short>( 32767 ) );
      break;

    case itk::ImageIOBase::SHORT:
      result = DoMain<short>( args,
                              itk::NumericTraits<short>::ZeroValue(),
                              static_cast<short>( 32767 ) );
      break;

    case itk::ImageIOBase::UINT:
      result = DoMain<unsigned int>( args,
                                     itk::NumericTraits<unsigned int>::ZeroValue(),
                                     static_cast<unsigned int>( 32767 ) );
      break;

    case itk::ImageIOBase::INT:
      result = DoMain<int>( args,
                            itk::NumericTraits<int>::ZeroValue(),
                            static_cast<int>( 32767 ) );
      break;

    case itk::ImageIOBase::ULONG:
      result = DoMain<unsigned long>( args,
                                      itk::NumericTraits<unsigned long>::ZeroValue(),
                                      static_cast<unsigned long>( 32767 ) );
      break;

    case itk::ImageIOBase::LONG:
      result = DoMain<long>( args,
                             itk::NumericTraits<long>::ZeroValue(),
                             static_cast<long>( 32767 ) );
      break;

    case itk::ImageIOBase::FLOAT:
      result = DoMain<float>( args,
                              itk::NumericTraits<float>::ZeroValue(),
                              static_cast<float>( 32767 ) );
      break;

    case itk::ImageIOBase::DOUBLE:
      result = DoMain<double>( args,
                               itk::NumericTraits<double>::ZeroValue(),
                               static_cast<double>( 32767 ) );
      break;

    default:
      std::cerr << "WARNING: Unrecognised pixel type, skipping file: "
		<< args.iterFilename << std::endl;
    }

    std::cout << std::endl;
  }

  return EXIT_SUCCESS;
}


/*
 * Copyright 2009-2011 The VOTCA Development Team (http://www.votca.org)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

//
// calculates overlap integrals for molecular orbitals using libmoo
// moo_overlap  --pos1 <coordinate file for mol1>
//              --pos2 <coordinate file for mol2>
//		--conjseg <xml file defining conjugated segments>
//		--pdb geometry in which electronic coupling si calculated

#include <boost/program_options.hpp>
#include <string>
#include <votca/moo/jcalc.h>
#include <votca/moo/crgunit.h>
#include <votca/tools/application.h>
#include <votca/tools/globals.h>

using namespace std;
using namespace votca::moo;

class MOOApplication
	: public Application
{
public:
	    virtual string ProgramName() { return "moo_overlap"; }
	    virtual void HelpText(std::ostream &out) { };
	    void Initialize();
	    bool EvaluateOptions();
	    void Run(void);
protected:
	    string _conjseg, _pos1, _pos2, _pdbfile;
};

void MOOApplication::Initialize()
{
	// read in program options
	namespace po = boost::program_options;
	AddProgramOptions("MOO Options")
		("conjseg", po::value<string>(), " xml file describing two conjugated segments")
		("pos1", po::value<string>(), " position and orientation of molecule 1")
		("pos2", po::value<string>(), " position and orientation of molecule 2")
		("pdb", po::value<string>()->default_value("geometry.pdb"), " pdb file of two molecules")
		;
}

bool MOOApplication::EvaluateOptions()
{
	CheckRequired("conjseg");
	 _conjseg = OptionsMap()["conjseg"].as<string>();
	 _pos1 = OptionsMap()["pos1"].as<string>();
	 _pos2 = OptionsMap()["pos2"].as<string>();
	 _pdbfile = OptionsMap()["pdb"].as<string>();
	return true;
}

void MOOApplication::Run(void)
{
        //cout << "Reading Crg unit Types" << endl;
        JCalc jcalc(_conjseg);
        //cout << "Finished reading Crg unit Types" << endl;

        ifstream in1(_pos1.c_str());
        ifstream in2(_pos2.c_str());
        int written=0;
        while (in1 && in2) {
            string name1, name2;
            vec com1;
            vec com2;
            matrix or1;
            matrix or2;
            in1 >> name1 >> com1.x() >> com1.y() >> com1.z() >>
                    or1[0][0] >> or1[0][1] >> or1[0][2] >>
                    or1[1][0] >> or1[1][1] >> or1[1][2] >>
                    or1[2][0] >> or1[2][1] >> or1[2][2];
            in2 >> name2 >> com2.x() >> com2.y() >> com2.z() >>
                    or2[0][0] >> or2[0][1] >> or2[0][2] >>
                    or2[1][0] >> or2[1][1] >> or2[1][2] >>
                    or2[2][0] >> or2[2][1] >> or2[2][2];

            if (!in1 || !in2) break;

            if (globals::verbose) {
             	cout << "molecule A: " << name1 << endl
                     << " translated by: " << com1 << endl
                     << " rotated by: " << endl << or1 << endl;

            	cout << "molecule B: " << name2 << endl
                     << "translated by: " << com2 << endl
                     << "rotated by: " << endl << or2 << endl;
            }

            CrgUnit * A = jcalc.CreateCrgUnit(0, name1);
            CrgUnit * B = jcalc.CreateCrgUnit(1, name2);

            A->SetPos(0, com1);
            A->SetNorm(0, or1[2]);
            A->SetPlane(0, or1[1]);

            if (globals::verbose) {
                cout << "molecule A: " << A->getName()
                 << " position: " <<  A->GetPos(0)
                 << " norm: " << A->GetNorm(0)
                 << " plane: " << A->GetPlane(0) << endl;
            }

            B->SetPos(0, com2);
            B->SetNorm(0, or2[2]);
            B->SetPlane(0, or2[1]);

            if (globals::verbose) {
                cout << "molecule B: " << B->getName()
                 << " position: " <<  B->GetPos(0)
                 << " norm: " << B->GetNorm(0)
                 << " plane: " << B->GetPlane(0) << endl;
            }

           //write pdb file
           mol_and_orb *molecule = ( A -> rotate_translate_beads() );
           (*molecule).write_pdb(_pdbfile, "m1", written);
           written += (*molecule).getN();
           delete molecule;
           molecule = ( B -> rotate_translate_beads() );
           (*molecule).write_pdb(_pdbfile, "m2", written);
           written += (*molecule).getN();
           delete molecule;
           ofstream fl;
           fl.open(_pdbfile.c_str(), ios::app);
           fl.setf(ios::fixed);
           fl << "END" <<endl;

            //	cout << "Compute J" <<endl;
            vector <double> Js = jcalc.CalcJ(*A, *B);
            //	cout << "Finished computing J" <<endl;
            vector <double>::iterator itJ = Js.begin();
            for (; itJ != Js.end(); ++itJ) cout << '\t' << *itJ << endl;
	};
}

int main(int argc, char **argv)
{
	MOOApplication app;
	return app.Exec(argc, argv);
}


//
//=======================================================================
// Copyright 1997, 1998, 1999, 2000 University of Notre Dame.
// Authors: Andrew Lumsdaine, Lie-Quan Lee, Jeremy G. Siek
//
// 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 BOOST_GRAPH_STRONG_COMPONENTS_HPP
#define BOOST_GRAPH_STRONG_COMPONENTS_HPP

#include <stack>
#include <boost/config.hpp>
#include <boost/graph/depth_first_search.hpp>
#include <boost/type_traits/conversion_traits.hpp>
#include <boost/static_assert.hpp>
#include <boost/graph/overloading.hpp>
#include <boost/graph/detail/mpi_include.hpp>
#include <boost/concept/assert.hpp>

namespace boost
{

//==========================================================================
// This is Tarjan's algorithm for strongly connected components
// from his paper "Depth first search and linear graph algorithms".
// It calculates the components in a single application of DFS.
// We implement the algorithm as a dfs-visitor.

namespace detail
{

    template < typename ComponentMap, typename RootMap, typename DiscoverTime,
        typename Stack >
    class tarjan_scc_visitor : public dfs_visitor<>
    {
        typedef typename property_traits< ComponentMap >::value_type comp_type;
        typedef typename property_traits< DiscoverTime >::value_type time_type;

    public:
        tarjan_scc_visitor(ComponentMap comp_map, RootMap r, DiscoverTime d,
            comp_type& c_, Stack& s_)
        : c(c_)
        , comp(comp_map)
        , root(r)
        , discover_time(d)
        , dfs_time(time_type())
        , s(s_)
        {
        }

        template < typename Graph >
        void discover_vertex(
            typename graph_traits< Graph >::vertex_descriptor v, const Graph&)
        {
            put(root, v, v);
            put(comp, v, (std::numeric_limits< comp_type >::max)());
            put(discover_time, v, dfs_time++);
            s.push(v);
        }
        template < typename Graph >
        void finish_vertex(
            typename graph_traits< Graph >::vertex_descriptor v, const Graph& g)
        {
            typename graph_traits< Graph >::vertex_descriptor w;
            typename graph_traits< Graph >::out_edge_iterator ei, ei_end;
            for (boost::tie(ei, ei_end) = out_edges(v, g); ei != ei_end; ++ei)
            {
                w = target(*ei, g);
                if (get(comp, w) == (std::numeric_limits< comp_type >::max)())
                    put(root, v,
                        this->min_discover_time(get(root, v), get(root, w)));
            }
            if (get(root, v) == v)
            {
                do
                {
                    w = s.top();
                    s.pop();
                    put(comp, w, c);
                    put(root, w, v);
                } while (w != v);
                ++c;
            }
        }

    private:
        template < typename Vertex >
        Vertex min_discover_time(Vertex u, Vertex v)
        {
            return get(discover_time, u) < get(discover_time, v) ? u : v;
        }

        comp_type& c;
        ComponentMap comp;
        RootMap root;
        DiscoverTime discover_time;
        time_type dfs_time;
        Stack& s;
    };

    template < class Graph, class ComponentMap, class RootMap,
        class DiscoverTime, class P, class T, class R >
    typename property_traits< ComponentMap >::value_type strong_components_impl(
        const Graph& g, // Input
        ComponentMap comp, // Output
        // Internal record keeping
        RootMap root, DiscoverTime discover_time,
        const bgl_named_params< P, T, R >& params)
    {
        typedef typename graph_traits< Graph >::vertex_descriptor Vertex;
        BOOST_CONCEPT_ASSERT(
            (ReadWritePropertyMapConcept< ComponentMap, Vertex >));
        BOOST_CONCEPT_ASSERT((ReadWritePropertyMapConcept< RootMap, Vertex >));
        typedef typename property_traits< RootMap >::value_type RootV;
        BOOST_CONCEPT_ASSERT((ConvertibleConcept< RootV, Vertex >));
        BOOST_CONCEPT_ASSERT(
            (ReadWritePropertyMapConcept< DiscoverTime, Vertex >));

        typename property_traits< ComponentMap >::value_type total = 0;

        std::stack< Vertex > s;
        detail::tarjan_scc_visitor< ComponentMap, RootMap, DiscoverTime,
            std::stack< Vertex > >
            vis(comp, root, discover_time, total, s);
        depth_first_search(g, params.visitor(vis));
        return total;
    }

    //-------------------------------------------------------------------------
    // The dispatch functions handle the defaults for the rank and discover
    // time property maps.
    // dispatch with class specialization to avoid VC++ bug

    template < class DiscoverTimeMap > struct strong_comp_dispatch2
    {
        template < class Graph, class ComponentMap, class RootMap, class P,
            class T, class R >
        inline static typename property_traits< ComponentMap >::value_type
        apply(const Graph& g, ComponentMap comp, RootMap r_map,
            const bgl_named_params< P, T, R >& params, DiscoverTimeMap time_map)
        {
            return strong_components_impl(g, comp, r_map, time_map, params);
        }
    };

    template <> struct strong_comp_dispatch2< param_not_found >
    {
        template < class Graph, class ComponentMap, class RootMap, class P,
            class T, class R >
        inline static typename property_traits< ComponentMap >::value_type
        apply(const Graph& g, ComponentMap comp, RootMap r_map,
            const bgl_named_params< P, T, R >& params, param_not_found)
        {
            typedef
                typename graph_traits< Graph >::vertices_size_type size_type;
            size_type n = num_vertices(g) > 0 ? num_vertices(g) : 1;
            std::vector< size_type > time_vec(n);
            return strong_components_impl(g, comp, r_map,
                make_iterator_property_map(time_vec.begin(),
                    choose_const_pmap(
                        get_param(params, vertex_index), g, vertex_index),
                    time_vec[0]),
                params);
        }
    };

    template < class Graph, class ComponentMap, class RootMap, class P, class T,
        class R, class DiscoverTimeMap >
    inline typename property_traits< ComponentMap >::value_type scc_helper2(
        const Graph& g, ComponentMap comp, RootMap r_map,
        const bgl_named_params< P, T, R >& params, DiscoverTimeMap time_map)
    {
        return strong_comp_dispatch2< DiscoverTimeMap >::apply(
            g, comp, r_map, params, time_map);
    }

    template < class RootMap > struct strong_comp_dispatch1
    {

        template < class Graph, class ComponentMap, class P, class T, class R >
        inline static typename property_traits< ComponentMap >::value_type
        apply(const Graph& g, ComponentMap comp,
            const bgl_named_params< P, T, R >& params, RootMap r_map)
        {
            return scc_helper2(g, comp, r_map, params,
                get_param(params, vertex_discover_time));
        }
    };
    template <> struct strong_comp_dispatch1< param_not_found >
    {

        template < class Graph, class ComponentMap, class P, class T, class R >
        inline static typename property_traits< ComponentMap >::value_type
        apply(const Graph& g, ComponentMap comp,
            const bgl_named_params< P, T, R >& params, param_not_found)
        {
            typedef typename graph_traits< Graph >::vertex_descriptor Vertex;
            typename std::vector< Vertex >::size_type n
                = num_vertices(g) > 0 ? num_vertices(g) : 1;
            std::vector< Vertex > root_vec(n);
            return scc_helper2(g, comp,
                make_iterator_property_map(root_vec.begin(),
                    choose_const_pmap(
                        get_param(params, vertex_index), g, vertex_index),
                    root_vec[0]),
                params, get_param(params, vertex_discover_time));
        }
    };

    template < class Graph, class ComponentMap, class RootMap, class P, class T,
        class R >
    inline typename property_traits< ComponentMap >::value_type scc_helper1(
        const Graph& g, ComponentMap comp,
        const bgl_named_params< P, T, R >& params, RootMap r_map)
    {
        return detail::strong_comp_dispatch1< RootMap >::apply(
            g, comp, params, r_map);
    }

} // namespace detail

template < class Graph, class ComponentMap, class P, class T, class R >
inline typename property_traits< ComponentMap >::value_type strong_components(
    const Graph& g, ComponentMap comp,
    const bgl_named_params< P, T, R >& params BOOST_GRAPH_ENABLE_IF_MODELS_PARM(
        Graph, vertex_list_graph_tag))
{
    typedef typename graph_traits< Graph >::directed_category DirCat;
    BOOST_STATIC_ASSERT(
        (is_convertible< DirCat*, directed_tag* >::value == true));
    return detail::scc_helper1(
        g, comp, params, get_param(params, vertex_root_t()));
}

template < class Graph, class ComponentMap >
inline typename property_traits< ComponentMap >::value_type strong_components(
    const Graph& g,
    ComponentMap comp BOOST_GRAPH_ENABLE_IF_MODELS_PARM(
        Graph, vertex_list_graph_tag))
{
    typedef typename graph_traits< Graph >::directed_category DirCat;
    BOOST_STATIC_ASSERT(
        (is_convertible< DirCat*, directed_tag* >::value == true));
    bgl_named_params< int, int > params(0);
    return strong_components(g, comp, params);
}

template < typename Graph, typename ComponentMap, typename ComponentLists >
void build_component_lists(const Graph& g,
    typename graph_traits< Graph >::vertices_size_type num_scc,
    ComponentMap component_number, ComponentLists& components)
{
    components.resize(num_scc);
    typename graph_traits< Graph >::vertex_iterator vi, vi_end;
    for (boost::tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi)
        components[component_number[*vi]].push_back(*vi);
}

} // namespace boost

#include <queue>
#include <vector>
#include <boost/graph/transpose_graph.hpp>
#include <boost/pending/indirect_cmp.hpp>
#include <boost/graph/connected_components.hpp> // for components_recorder

namespace boost
{

//==========================================================================
// This is the version of strongly connected components from
// "Intro. to Algorithms" by Cormen, Leiserson, Rivest, which was
// adapted from "Data Structure and Algorithms" by Aho, Hopcroft,
// and Ullman, who credit the algorithm to S.R. Kosaraju and M. Sharir.
// The algorithm is based on computing DFS forests the graph
// and its transpose.

// This algorithm is slower than Tarjan's by a constant factor, uses
// more memory, and puts more requirements on the graph type.

template < class Graph, class DFSVisitor, class ComponentsMap,
    class DiscoverTime, class FinishTime, class ColorMap >
typename property_traits< ComponentsMap >::value_type
kosaraju_strong_components(
    Graph& G, ComponentsMap c, FinishTime finish_time, ColorMap color)
{
    BOOST_CONCEPT_ASSERT((MutableGraphConcept< Graph >));
    // ...

    typedef typename graph_traits< Graph >::vertex_descriptor Vertex;
    typedef typename property_traits< ColorMap >::value_type ColorValue;
    typedef color_traits< ColorValue > Color;
    typename property_traits< FinishTime >::value_type time = 0;
    depth_first_search(G,
        make_dfs_visitor(stamp_times(finish_time, time, on_finish_vertex())),
        color);

    Graph G_T(num_vertices(G));
    transpose_graph(G, G_T);

    typedef typename property_traits< ComponentsMap >::value_type count_type;

    count_type c_count(0);
    detail::components_recorder< ComponentsMap > vis(c, c_count);

    // initialize G_T
    typename graph_traits< Graph >::vertex_iterator ui, ui_end;
    for (boost::tie(ui, ui_end) = vertices(G_T); ui != ui_end; ++ui)
        put(color, *ui, Color::white());

    typedef typename property_traits< FinishTime >::value_type D;
    typedef indirect_cmp< FinishTime, std::less< D > > Compare;

    Compare fl(finish_time);
    std::priority_queue< Vertex, std::vector< Vertex >, Compare > Q(fl);

    typename graph_traits< Graph >::vertex_iterator i, j, iend, jend;
    boost::tie(i, iend) = vertices(G_T);
    boost::tie(j, jend) = vertices(G);
    for (; i != iend; ++i, ++j)
    {
        put(finish_time, *i, get(finish_time, *j));
        Q.push(*i);
    }

    while (!Q.empty())
    {
        Vertex u = Q.top();
        Q.pop();
        if (get(color, u) == Color::white())
        {
            depth_first_visit(G_T, u, vis, color);
            ++c_count;
        }
    }
    return c_count;
}

} // namespace boost

#include BOOST_GRAPH_MPI_INCLUDE(< boost / graph / distributed / strong_components.hpp >)

#endif // BOOST_GRAPH_STRONG_COMPONENTS_HPP


/* ============================================================================
* Copyright (c) 2009-2016 BlueQuartz Software, LLC
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* Neither the name of BlueQuartz Software, the US Air Force, nor the names of its
* contributors may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The code contained herein was partially funded by the followig contracts:
*    United States Air Force Prime Contract FA8650-07-D-5800
*    United States Air Force Prime Contract FA8650-10-D-5210
*    United States Prime Contract Navy N00173-07-C-2068
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */

#include "ApplyTransformationToGeometry.h"

#include <Eigen/Dense>

#include "SIMPLib/Common/Constants.h"
#include "SIMPLib/FilterParameters/AbstractFilterParametersReader.h"
#include "SIMPLib/FilterParameters/DataArraySelectionFilterParameter.h"
#include "SIMPLib/FilterParameters/DataContainerSelectionFilterParameter.h"
#include "SIMPLib/FilterParameters/DynamicTableFilterParameter.h"
#include "SIMPLib/FilterParameters/FloatFilterParameter.h"
#include "SIMPLib/FilterParameters/LinkedChoicesFilterParameter.h"
#include "SIMPLib/Geometry/EdgeGeom.h"
#include "SIMPLib/Geometry/IGeometry2D.h"
#include "SIMPLib/Geometry/IGeometry3D.h"
#include "SIMPLib/Geometry/VertexGeom.h"

#include "OrientationLib/OrientationMath/OrientationTransforms.hpp"

#include "DREAM3DReview/DREAM3DReviewConstants.h"
#include "DREAM3DReview/DREAM3DReviewVersion.h"

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
ApplyTransformationToGeometry::ApplyTransformationToGeometry()
: m_ComputedTransformationMatrix("", "", "TransformationMatrix")
, m_GeometryToTransform("")
, m_TransformationMatrixType(1)
{
  m_RotationAngle = 0.0f;
  m_RotationAxis[0] = 0.0f;
  m_RotationAxis[1] = 0.0f;
  m_RotationAxis[2] = 1.0f;

  m_Translation[0] = 0.0f;
  m_Translation[1] = 0.0f;
  m_Translation[2] = 0.0f;

  m_Scale[0] = 0.0f;
  m_Scale[1] = 0.0f;
  m_Scale[2] = 0.0f;
}

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
ApplyTransformationToGeometry::~ApplyTransformationToGeometry()
= default;

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
void ApplyTransformationToGeometry::setupFilterParameters()
{
  FilterParameterVectorType parameters;
  {
    LinkedChoicesFilterParameter::Pointer parameter = LinkedChoicesFilterParameter::New();
    parameter->setHumanLabel("Transformation Type");
    parameter->setPropertyName("TransformationMatrixType");
    parameter->setSetterCallback(SIMPL_BIND_SETTER(ApplyTransformationToGeometry, this, TransformationMatrixType));
    parameter->setGetterCallback(SIMPL_BIND_GETTER(ApplyTransformationToGeometry, this, TransformationMatrixType));
    QVector<QString> choices;
    choices.push_back("No Transformation");
    choices.push_back("Pre-Computed Transformation Matrix");
    choices.push_back("Manual Transformation Matrix");
    choices.push_back("Rotation");
    choices.push_back("Translation");
    choices.push_back("Scale");
    parameter->setChoices(choices);
    QStringList linkedProps = {"ComputedTransformationMatrix", "ManualTransformationMatrix", "RotationAngle", "RotationAxis", "Translation", "Scale"};
    parameter->setLinkedProperties(linkedProps);
    parameter->setEditable(false);
    parameter->setCategory(FilterParameter::Parameter);
    parameters.push_back(parameter);
  }
  {
    QStringList rHeaders, cHeaders;
    std::vector<std::vector<double>> defaultTable;
    for(size_t i = 0; i < 4; i++)
    {
      std::vector<double> row(4, 0);
      row[i] = 1.0;
      defaultTable.push_back(row);
    }
    m_ManualTransformationMatrix.setTableData(defaultTable);
    parameters.push_back(SIMPL_NEW_DYN_TABLE_FP("Transformation Matrix", ManualTransformationMatrix, FilterParameter::Parameter, ApplyTransformationToGeometry, 2));
  }
  parameters.push_back(SIMPL_NEW_FLOAT_FP("Rotation Angle (Degrees)", RotationAngle, FilterParameter::Parameter, ApplyTransformationToGeometry, 3));
  parameters.push_back(SIMPL_NEW_FLOAT_VEC3_FP("Rotation Axis (ijk)", RotationAxis, FilterParameter::Parameter, ApplyTransformationToGeometry, 3));
  parameters.push_back(SIMPL_NEW_FLOAT_VEC3_FP("Translation", Translation, FilterParameter::Parameter, ApplyTransformationToGeometry, 4));
  parameters.push_back(SIMPL_NEW_FLOAT_VEC3_FP("Scale", Scale, FilterParameter::Parameter, ApplyTransformationToGeometry, 5));
  DataContainerSelectionFilterParameter::RequirementType dcReq;
  IGeometry::Types geomTypes = {IGeometry::Type::Vertex, IGeometry::Type::Edge, IGeometry::Type::Triangle, IGeometry::Type::Quad, IGeometry::Type::Tetrahedral};
  dcReq.dcGeometryTypes = geomTypes;
  parameters.push_back(SIMPL_NEW_DC_SELECTION_FP("Geometry to Transform", GeometryToTransform, FilterParameter::RequiredArray, ApplyTransformationToGeometry, dcReq));
  {
    DataArraySelectionFilterParameter::RequirementType dasReq =
        DataArraySelectionFilterParameter::CreateRequirement(SIMPL::TypeNames::Float, SIMPL::Defaults::AnyComponentSize, AttributeMatrix::Type::Generic, IGeometry::Type::Any);
    parameters.push_back(SIMPL_NEW_DA_SELECTION_FP("Transformation Matrix", ComputedTransformationMatrix, FilterParameter::RequiredArray, ApplyTransformationToGeometry, dasReq, 1));
  }
  setFilterParameters(parameters);
}

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
void ApplyTransformationToGeometry::readFilterParameters(AbstractFilterParametersReader* reader, int index)
{
  reader->openFilterGroup(this, index);
  setManualTransformationMatrix(reader->readDynamicTableData("ManualTransformationMatrix", getManualTransformationMatrix()));
  setComputedTransformationMatrix(reader->readDataArrayPath("ComputedTransformationMatrix", getComputedTransformationMatrix()));
  setGeometryToTransform(reader->readDataArrayPath("GeometryToTransform", getGeometryToTransform()));
  setTransformationMatrixType(reader->readValue("TransformationMatrixType", getTransformationMatrixType()));
  setRotationAxis(reader->readFloatVec3("RotationAxis", getRotationAxis()));
  setRotationAngle(reader->readValue("RotationAngle", getRotationAngle()));
  setTranslation(reader->readFloatVec3("Translation", getTranslation()));
  setScale(reader->readFloatVec3("Scale", getScale()));
  reader->closeFilterGroup();
}

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
void ApplyTransformationToGeometry::dataCheck()
{
  clearErrorCode();
  clearWarningCode();

  IGeometry::Pointer igeom = getDataContainerArray()->getPrereqGeometryFromDataContainer<IGeometry, AbstractFilter>(this, getGeometryToTransform());

  if(getErrorCode() < 0)
  {
    return;
  }

  if(!std::dynamic_pointer_cast<IGeometry2D>(igeom) && !std::dynamic_pointer_cast<IGeometry3D>(igeom) && !std::dynamic_pointer_cast<VertexGeom>(igeom) && !std::dynamic_pointer_cast<EdgeGeom>(igeom))
  {
    QString ss =
        QObject::tr("Geometry to transform must be an unstructured geometry (Vertex, Edge, Triangle, Quadrilateral, or Tetrahedral), but the type is %1").arg(igeom->getGeometryTypeAsString());
    setErrorCondition(-702, ss);
  }

  std::vector<size_t> cDims = {4, 4};

  switch(getTransformationMatrixType())
  {
  case 0: // No-Op
  {
    QString ss = QObject::tr("No transformation has been selected, so this filter will perform no operations");
    setWarningCondition(-701, ss);
  }
  case 1: // Transformation matrix from array
  {
    m_TransformationMatrixPtr = getDataContainerArray()->getPrereqArrayFromPath<DataArray<float>, AbstractFilter>(this, getComputedTransformationMatrix(), cDims);
    if(m_TransformationMatrixPtr.lock())
    {
      m_TransformationMatrix = m_TransformationMatrixPtr.lock()->getPointer(0);
    }
    break;
  }
  case 2: // Manual transformation matrix
  {
    if(getManualTransformationMatrix().getNumRows() != 4)
    {
      QString ss = QObject::tr("Manually entered transformation matrix must have exactly 4 rows");
      setErrorCondition(-702, ss);
      return;
    }
    if(getManualTransformationMatrix().getNumCols() != 4)
    {
      QString ss = QObject::tr("Manually entered transformation matrix must have exactly 4 columns");
      setErrorCondition(-703, ss);
      return;
    }
    std::vector<std::vector<double>> tableData = getManualTransformationMatrix().getTableData();
    m_TransformationReference = FloatArrayType::CreateArray(1, cDims, "_INTERNAL_USE_ONLY_ManualTransformationMatrix", true);
    m_TransformationReference->initializeWithZeros();
    m_TransformationMatrixPtr = m_TransformationReference;
    if(m_TransformationMatrixPtr.lock())
    {
      m_TransformationMatrix = m_TransformationMatrixPtr.lock()->getPointer(0);
      for(size_t i = 0; i < tableData.size(); i++)
      {
        std::vector<double> row = tableData[i];
        for(size_t j = 0; j < row.size(); j++)
        {
          m_TransformationMatrix[4 * i + j] = static_cast<float>(row[j]);
        }
      }
    }
    break;
  }
  case 3: // Rotation via axis-angle
  {
    float rotAngle = m_RotationAngle * SIMPLib::Constants::k_Pi / 180.0;
    FOrientArrayType om(9);
    FOrientTransformsType::ax2om(FOrientArrayType(m_RotationAxis[0], m_RotationAxis[1], m_RotationAxis[2], rotAngle), om);

    m_TransformationReference = FloatArrayType::CreateArray(1, cDims, "_INTERNAL_USE_ONLY_ManualTransformationMatrix", true);
    m_TransformationReference->initializeWithZeros();
    m_TransformationMatrixPtr = m_TransformationReference;
    if(m_TransformationMatrixPtr.lock())
    {
      m_TransformationMatrix = m_TransformationMatrixPtr.lock()->getPointer(0);
      for(size_t i = 0; i < 3; i++)
      {
        m_TransformationMatrix[4 * i + 0] = om[3 * i + 0];
        m_TransformationMatrix[4 * i + 1] = om[3 * i + 1];
        m_TransformationMatrix[4 * i + 2] = om[3 * i + 2];
        m_TransformationMatrix[4 * i + 3] = 0.0f;
      }
      m_TransformationMatrix[4 * 3 + 3] = 1.0f;
    }
    break;
  }
  case 4: // Translation
  {
    m_TransformationReference = FloatArrayType::CreateArray(1, cDims, "_INTERNAL_USE_ONLY_ManualTransformationMatrix", true);
    m_TransformationReference->initializeWithZeros();
    m_TransformationMatrixPtr = m_TransformationReference;
    if(m_TransformationMatrixPtr.lock())
    {
      m_TransformationMatrix = m_TransformationMatrixPtr.lock()->getPointer(0);
      m_TransformationMatrix[4 * 0 + 0] = 1.0f;
      m_TransformationMatrix[4 * 1 + 1] = 1.0f;
      m_TransformationMatrix[4 * 2 + 2] = 1.0f;
      m_TransformationMatrix[4 * 0 + 3] = m_Translation[0];
      m_TransformationMatrix[4 * 1 + 3] = m_Translation[1];
      m_TransformationMatrix[4 * 2 + 3] = m_Translation[2];
      m_TransformationMatrix[4 * 3 + 3] = 1.0f;
    }
    break;
  }
  case 5: // Scale
  {
    m_TransformationReference = FloatArrayType::CreateArray(1, cDims, "_INTERNAL_USE_ONLY_ManualTransformationMatrix", true);
    m_TransformationReference->initializeWithZeros();
    m_TransformationMatrixPtr = m_TransformationReference;
    if(m_TransformationMatrixPtr.lock())
    {
      m_TransformationMatrix = m_TransformationMatrixPtr.lock()->getPointer(0);
      m_TransformationMatrix[4 * 0 + 0] = m_Scale[0];
      m_TransformationMatrix[4 * 1 + 1] = m_Scale[1];
      m_TransformationMatrix[4 * 2 + 2] = m_Scale[2];
      m_TransformationMatrix[4 * 3 + 3] = 1.0f;
    }
    break;
  }
  default:
  {
    QString ss = QObject::tr("Invalid selection for transformation type");
    setErrorCondition(-701, ss);
    break;
  }
  }
}

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
void ApplyTransformationToGeometry::preflight()
{
  // These are the REQUIRED lines of CODE to make sure the filter behaves correctly
  setInPreflight(true);              // Set the fact that we are preflighting.
  emit preflightAboutToExecute();    // Emit this signal so that other widgets can do one file update
  emit updateFilterParameters(this); // Emit this signal to have the widgets push their values down to the filter
  dataCheck();                       // Run our DataCheck to make sure everthing is setup correctly
  emit preflightExecuted();          // We are done preflighting this filter
  setInPreflight(false);             // Inform the system this filter is NOT in preflight mode anymore.
}

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
void ApplyTransformationToGeometry::applyTransformation()
{
  IGeometry::Pointer igeom = getDataContainerArray()->getDataContainer(m_GeometryToTransform)->getGeometry();

  int64_t numVertices = 0;
  float* vertices = nullptr;

  if(IGeometry2D::Pointer igeom2D = std::dynamic_pointer_cast<IGeometry2D>(igeom))
  {
    numVertices = igeom2D->getNumberOfVertices();
    vertices = igeom2D->getVertexPointer(0);
  }
  else if(IGeometry3D::Pointer igeom3D = std::dynamic_pointer_cast<IGeometry3D>(igeom))
  {
    numVertices = igeom3D->getNumberOfVertices();
    vertices = igeom3D->getVertexPointer(0);
  }
  else if(VertexGeom::Pointer vertex = std::dynamic_pointer_cast<VertexGeom>(igeom))
  {
    numVertices = vertex->getNumberOfVertices();
    vertices = vertex->getVertexPointer(0);
  }
  else if(EdgeGeom::Pointer edge = std::dynamic_pointer_cast<EdgeGeom>(igeom))
  {
    numVertices = edge->getNumberOfVertices();
    vertices = vertex->getVertexPointer(0);
  }
  else
  {
    return;
  }

  typedef Eigen::Matrix<float, 4, 4, Eigen::RowMajor> ProjectiveMatrix;
  Eigen::Map<ProjectiveMatrix> transformation(m_TransformationMatrix);

  int64_t progIncrement = numVertices / 100;
  int64_t prog = 1;
  int64_t progressInt = 0;
  int64_t counter = 0;

  for(int64_t i = 0; i < numVertices; i++)
  {
    if(getCancel())
    {
      return;
    }
    Eigen::Vector4f position(vertices[3 * i + 0], vertices[3 * i + 1], vertices[3 * i + 2], 1);
    Eigen::Vector4f transformedPosition = transformation * position;
    std::memcpy(vertices + (3 * i), transformedPosition.data(), sizeof(float) * 3);

    if(counter > prog)
    {
      progressInt = static_cast<int64_t>((static_cast<float>(counter) / numVertices) * 100.0f);
      QString ss = QObject::tr("Transforming Geometry || %1% Completed").arg(progressInt);
      notifyStatusMessage(ss);
      prog = prog + progIncrement;
    }
    counter++;
  }
}

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
void ApplyTransformationToGeometry::execute()
{
  clearErrorCode();
  clearWarningCode();
  dataCheck();
  if(getErrorCode() < 0)
  {
    return;
  }
  if(getWarningCode() < 0)
  {
    return;
  }

  if(m_TransformationMatrixType == 0)
  {
    return;
  }

    applyTransformation();

}

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
AbstractFilter::Pointer ApplyTransformationToGeometry::newFilterInstance(bool copyFilterParameters) const
{
  ApplyTransformationToGeometry::Pointer filter = ApplyTransformationToGeometry::New();
  if(copyFilterParameters)
  {
    copyFilterParameterInstanceVariables(filter.get());
  }
  return filter;
}

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
const QString ApplyTransformationToGeometry::getCompiledLibraryName() const
{
  return DREAM3DReviewConstants::DREAM3DReviewBaseName;
}

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
const QString ApplyTransformationToGeometry::getBrandingString() const
{
  return "DREAM3DReview";
}

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
const QString ApplyTransformationToGeometry::getFilterVersion() const
{
  QString version;
  QTextStream vStream(&version);
  vStream << DREAM3DReview::Version::Major() << "." << DREAM3DReview::Version::Minor() << "." << DREAM3DReview::Version::Patch();
  return version;
}

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
const QString ApplyTransformationToGeometry::getGroupName() const
{
  return DREAM3DReviewConstants::FilterGroups::DREAM3DReviewFilters;
}

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
const QUuid ApplyTransformationToGeometry::getUuid()
{
  return QUuid("{c681caf4-22f2-5885-bbc9-a0476abc72eb}");
}

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
const QString ApplyTransformationToGeometry::getSubGroupName() const
{
  return DREAM3DReviewConstants::FilterSubGroups::RotationTransformationFilters;
}

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
const QString ApplyTransformationToGeometry::getHumanLabel() const
{
  return "Apply Transformation to Geometry";
}


/*=============================================================================
  Copyright (c) 2010-2016 Bolero MURAKAMI
  https://github.com/bolero-MURAKAMI/Sprig

  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 SPRIG_NTL_SIGNIFICANT_HPP
#define SPRIG_NTL_SIGNIFICANT_HPP

#include <sprig/config/config.hpp>

#ifdef SPRIG_USING_PRAGMA_ONCE
#	pragma once
#endif	// #ifdef SPRIG_USING_PRAGMA_ONCE

#include <cstddef>
#include <cmath>
#include <string>
#include <algorithm>
#include <utility>
#include <boost/range.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/call_traits.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/type_traits/is_integral.hpp>
#include <boost/type_traits/is_float.hpp>
#include <boost/type_traits/remove_const.hpp>
#include <boost/mpl/or.hpp>
#include <boost/utility/enable_if.hpp>
#include <sprig/external/ntl/zz.hpp>
#include <sprig/external/ntl/rr.hpp>
#include <sprig/math.hpp>
#include <sprig/type_traits/is_call_copy_param.hpp>
#include <sprig/type_traits/is_char_type.hpp>
#include <sprig/type_traits/is_wchar_type.hpp>
#include <sprig/type_traits/is_c_str.hpp>
#include <sprig/type_traits/is_basic_string.hpp>
#include <sprig/type_traits/c_str_element.hpp>
#include <sprig/type_traits/remove_const_reference.hpp>
#include <sprig/str_cast.hpp>
#include <sprig/ntl/to_ZZ.hpp>
#include <sprig/ntl/to_RR.hpp>

namespace sprig {
	//
	// significant
	//
	template<typename T, typename Enable = void>
	class significant {};

	//
	// significant_base
	//
	template<typename T>
	class significant_base {
	public:
		typedef T type;
		typedef typename boost::call_traits<type>::param_type param_type;
		typedef std::size_t digits_type;
		typedef NTL::ZZ digits_ZZ_type;
		typedef NTL::RR digits_RR_type;
	protected:
		~significant_base();
	//public:
	//	static digits_type digits();
	//	static digits_ZZ_type const& digits_ZZ();
	//	static digits_RR_type const& digits_RR();
	//	static digits_type digits(param_type target);
	//	static digits_ZZ_type const& digits_ZZ(param_type target);
	//	static digits_RR_type const& digits_RR(param_type target);
	//	static digits_type digits10();
	//	static digits_ZZ_type const& digits10_ZZ();
	//	static digits_RR_type const& digits10_RR();
	};

	//
	// significant_float_base
	//
	template<typename T, std::size_t N>
	class significant_float_base
		: public significant_base<T>
	{
	private:
		typedef significant_base<T> base_type;
	public:
		typedef typename base_type::param_type param_type;
		typedef typename base_type::digits_type digits_type;
		typedef typename base_type::digits_ZZ_type digits_ZZ_type;
		typedef typename base_type::digits_RR_type digits_RR_type;
	public:
		static digits_type digits() {
			return N;
		}
		static digits_ZZ_type const& digits_ZZ() {
			static digits_ZZ_type const digits_(NTL::to_ZZ(N));
			return digits_;
		}
		static digits_RR_type const& digits_RR() {
			static digits_RR_type const digits_(NTL::to_RR(N));
			return digits_;
		}
		static digits_type digits(param_type) {
			return digits();
		}
		static digits_ZZ_type const& digits_ZZ(param_type) {
			return digits_RR();
		}
		static digits_RR_type const& digits_RR(param_type) {
			return digits_RR();
		}
		static digits_type digits10() {
			static digits_type const digits_(std::pow(10.0, static_cast<int>(N)));
			return digits_;
		}
		static digits_ZZ_type const& digits10_ZZ() {
			static digits_ZZ_type const digits_(NTL::power(NTL::to_ZZ(10), N));
			return digits_;
		}
		static digits_RR_type const& digits10_RR() {
			static digits_RR_type const digits_(NTL::power(NTL::to_RR(10), N));
			return digits_;
		}
	};

	//
	// significant_float_utils
	//
	template<typename Char, typename Enable = void>
	class significant_float_utils {};
	template<typename Char>
	class significant_float_utils<
		Char,
		typename boost::enable_if<
			is_char_type<Char>
		>::type
	> {
	public:
		typedef Char char_type;
	public:
		static char_type const e = 'e';
		static char_type const point = '.';
	public:
		template<typename IntType, typename Range>
		static IntType exponent(Range const& range) {
			typedef std::basic_string<char_type> string_type;
			typedef typename boost::range_iterator<Range>::type iterator;
			iterator i_e = std::find(boost::begin(range), boost::end(range), e);
			return i_e != boost::end(range)
				? boost::lexical_cast<IntType>(string_type(i_e + 1, boost::end(range)))
				: 0
				;
		}
		template<typename IntType, typename Range>
		static IntType integral(Range const& range) {
			typedef std::basic_string<char_type> string_type;
			typedef typename boost::range_iterator<Range>::type iterator;
			iterator i_point = std::find(boost::begin(range), boost::end(range), point);
			iterator i_e = std::find(boost::begin(range), boost::end(range), e);
			return i_point != boost::end(range)
				? boost::lexical_cast<IntType>(string_type(boost::begin(range), i_point))
				: i_e != boost::end(range)
					? boost::lexical_cast<IntType>(string_type(boost::begin(range), i_e))
					: boost::lexical_cast<IntType>(string_type(boost::begin(range), boost::end(range)))
				;
		}
		template<typename IntType, typename Range>
		static IntType fractional(Range const& range) {
			typedef std::basic_string<char_type> string_type;
			typedef typename boost::range_iterator<Range>::type iterator;
			iterator i_point = std::find(boost::begin(range), boost::end(range), point);
			iterator i_e = std::find(boost::begin(range), boost::end(range), e);
			return i_point != boost::end(range)
				? boost::lexical_cast<IntType>(string_type(i_point + 1, i_e))
				: 0
				;
		}
	};
	template<typename Char>
	class significant_float_utils<
		Char,
		typename boost::enable_if<
			is_wchar_type<Char>
		>::type
	> {
	public:
		typedef Char char_type;
	public:
		static char_type const e = L'e';
		static char_type const point = L'.';
	};

	//
	// significant_c_str_impl
	//
	template<typename Char>
	class significant_c_str_impl
		: public significant_base<Char const*>
	{
	private:
		typedef significant_base<Char const*> base_type;
	public:
		typedef typename base_type::param_type param_type;
		typedef typename base_type::digits_type digits_type;
		typedef typename base_type::digits_ZZ_type digits_ZZ_type;
		typedef typename base_type::digits_RR_type digits_RR_type;
	public:
		typedef Char char_type;
	public:
		static digits_type digits() {
			return 0;
		}
		static digits_ZZ_type const& digits_ZZ() {
			static digits_ZZ_type const digits_(NTL::to_ZZ(0));
			return digits_;
		}
		static digits_RR_type const& digits_RR() {
			static digits_RR_type const digits_(NTL::to_RR(0));
			return digits_;
		}
		static digits_type digits(param_type target) {
			return digits<std::basic_string<char_type> >(target);
		}
		template<typename String>
		static digits_type digits(String const& target) {
			typedef String string_type;
			typedef typename string_type::iterator iterator;
			digits_type result = 0;
			string_type value(target);
			iterator i_e = std::find(value.begin(), value.end(), significant_float_utils<char_type>::e);
			iterator i_point = std::find(value.begin(), value.end(), significant_float_utils<char_type>::point);
			if (i_point != value.end()) {
				result += std::distance(i_point + 1, i_e);
			}
			if (i_e != value.end()) {
				result -= boost::lexical_cast<digits_type>(string_type(i_e + 1, value.end()));
			}
			return result >= 0 ? result : 0;
		}
		static digits_ZZ_type const& digits_ZZ(param_type target) {
			return NTL::to_ZZ(digits(target));
		}
		static digits_RR_type const& digits_RR(param_type target) {
			return NTL::to_RR(digits(target));
		}
		static digits_type digits10() {
			return 1;
		}
		static digits_ZZ_type const& digits10_ZZ() {
			static digits_ZZ_type const digits_(NTL::to_ZZ(1));
			return digits_;
		}
		static digits_RR_type const& digits10_RR() {
			static digits_RR_type const digits_(NTL::to_RR(1));
			return digits_;
		}
	};

	//
	// specialization significant: is_integral(T)
	//
	template<typename T>
	class significant<
		T,
		typename boost::enable_if<
			boost::is_integral<T>
		>::type
	>
		: public significant_base<T>
	{
	private:
		typedef significant_base<T> base_type;
	public:
		typedef typename base_type::param_type param_type;
		typedef typename base_type::digits_type digits_type;
		typedef typename base_type::digits_ZZ_type digits_ZZ_type;
		typedef typename base_type::digits_RR_type digits_RR_type;
	public:
		static digits_type digits() {
			return 1;
		}
		static digits_ZZ_type const& digits_ZZ() {
			static digits_ZZ_type const digits_(NTL::to_ZZ(1));
			return digits_;
		}
		static digits_RR_type const& digits_RR() {
			static digits_RR_type const digits_(NTL::to_RR(1));
			return digits_;
		}
		static digits_type digits(param_type) {
			return 1;
		}
		static digits_ZZ_type const& digits_ZZ(param_type) {
			return digits_ZZ();
		}
		static digits_RR_type const& digits_RR(param_type) {
			return digits_RR();
		}
		static digits_type digits10() {
			return 10;
		}
		static digits_ZZ_type const& digits10_ZZ() {
			static digits_ZZ_type const digits_(NTL::to_ZZ(10));
			return digits_;
		}
		static digits_RR_type const& digits10_RR() {
			static digits_RR_type const digits_(NTL::to_RR(10));
			return digits_;
		}
	};
	//
	// specialization significant: is_same(T, float), is_same(T, double), is_same(T, long double)
	//
	template<typename T>
	class significant<
		T,
		typename boost::enable_if<
			boost::is_same<T, float>
		>::type
	>
		: public significant_float_base<T, 7>
	{};
	template<typename T>
	class significant<T, typename boost::enable_if<boost::is_same<T, double> >::type>
		: public significant_float_base<T, 15>
	{};
	template<typename T>
	class significant<T, typename boost::enable_if<boost::is_same<T, long double> >::type>
		: public significant_float_base<T, 15>
	{};
	//
	// specialization significant: is_c_str(T)
	//
	template<typename T>
	class significant<
		T,
		typename boost::enable_if<
			is_c_str<T>
		>::type
	>
		: public significant_base<T>
	{
	private:
		typedef significant_base<T> base_type;
	public:
		typedef typename base_type::param_type param_type;
		typedef typename base_type::digits_type digits_type;
		typedef typename base_type::digits_ZZ_type digits_ZZ_type;
		typedef typename base_type::digits_RR_type digits_RR_type;
	private:
		typedef significant_c_str_impl<
			typename c_str_element_value<T>::type
		> impl_type;
	public:
		static digits_type digits() {
			return impl_type::digits();
		}
		static digits_ZZ_type const& digits_ZZ() {
			return impl_type::digits_ZZ();
		}
		static digits_RR_type const& digits_RR() {
			return impl_type::digits_RR();
		}
		static digits_type digits(param_type target) {
			return impl_type::digits(target);
		}
		static digits_ZZ_type const& digits_ZZ(param_type target) {
			return impl_type::digits_ZZ(target);
		}
		static digits_RR_type const& digits_RR(param_type target) {
			return impl_type::digits_RR(target);
		}
		static digits_type digits10() {
			return impl_type::digits10();
		}
		static digits_ZZ_type const& digits10_ZZ() {
			return impl_type::digits10_ZZ();
		}
		static digits_RR_type const& digits10_RR() {
			return impl_type::digits10_RR();
		}
	};

	//
	// specialization significant: is_basic_string(T)
	//
	template<typename T>
	class significant<
		T,
		typename boost::enable_if<
			is_basic_string<T>
		>::type
	>
		: public significant_base<T>
	{
	private:
		typedef significant_base<T> base_type;
	public:
		typedef typename base_type::param_type param_type;
		typedef typename base_type::digits_type digits_type;
		typedef typename base_type::digits_ZZ_type digits_ZZ_type;
		typedef typename base_type::digits_RR_type digits_RR_type;
	private:
		typedef significant_c_str_impl<
			typename remove_const_reference<T>::type::value_type
		> impl_type;
	public:
		static digits_type digits() {
			return impl_type::digits();
		}
		static digits_ZZ_type const& digits_ZZ() {
			return impl_type::digits_ZZ();
		}
		static digits_RR_type const& digits_RR() {
			return impl_type::digits_RR();
		}
		static digits_type digits(param_type target) {
			return impl_type::template digits<typename remove_const_reference<T>::type>(target);
		}
		static digits_ZZ_type const& digits_ZZ(param_type target) {
			return impl_type::digits_ZZ(target);
		}
		static digits_RR_type const& digits_RR(param_type target) {
			return impl_type::digits_RR(target);
		}
		static digits_type digits10() {
			return impl_type::digits10();
		}
		static digits_ZZ_type const& digits10_ZZ() {
			return impl_type::digits10_ZZ();
		}
		static digits_RR_type const& digits10_RR() {
			return impl_type::digits10_RR();
		}
	};

	//
	// significant_digits
	//
	template<typename T>
	SPRIG_INLINE typename boost::enable_if<
		is_call_copy_param<typename significant<T>::param_type>,
		typename significant<T>::digits_type
	>::type
	significant_digits(T const target) {
		return significant<T>::digits(target);
	}
	template<typename T>
	SPRIG_INLINE typename boost::disable_if<
		is_call_copy_param<typename significant<T>::param_type>,
		typename significant<T>::digits_type
	>::type
	significant_digits(T const& target) {
		return significant<T>::digits(target);
	}

	//
	// to_ZZ_full_significant
	//
	// specialization to_ZZ_full_significant: is_integral(T)
	//
	template<typename T>
	SPRIG_INLINE typename boost::enable_if<
		boost::is_integral<T>,
		NTL::ZZ
	>::type
	to_ZZ_full_significant(T const target) {
		return to_ZZ(target);
	}
	//
	// specialization to_ZZ_full_significant: is_float(T)
	//
	template<typename T>
	SPRIG_INLINE typename boost::enable_if<
		boost::is_float<T>,
		NTL::ZZ
	>::type
	to_ZZ_full_significant(T const target) {
		typename significant<T>::digits_type digits = integer_digits(target);
		return to_ZZ(NTL::trunc(to_RR(target) * NTL::power(to_RR(10), static_cast<long>(significant<T>::digits() - digits))))
			* NTL::power(to_ZZ(10), static_cast<long>(digits));
	}
	//
	// specialization to_ZZ_full_significant: is_c_str(T)
	//
	template<typename T>
	SPRIG_INLINE typename boost::enable_if<
		is_c_str<T>,
		NTL::ZZ
	>::type
	to_ZZ_full_significant(T const target) {
		typename significant<T>::digits_type digits = significant<T>::digits(target);
		return to_ZZ(to_RR(target) * NTL::power(to_RR(10), digits));
	}
	//
	// specialization to_ZZ_full_significant: is_basic_string(T)
	//
	template<typename T>
	SPRIG_INLINE typename boost::enable_if<
		is_basic_string<T>,
		NTL::ZZ
	>::type
	to_ZZ_full_significant(T const& target) {
		typename significant<T>::digits_type digits = significant<T>::digits(target);
		return to_ZZ(to_RR(target) * NTL::power(to_RR(10), digits));
	}

	//
	// to_RR_full_significant
	//
	// specialization to_RR_full_significant: is_integral(T)
	//
	template<typename T>
	SPRIG_INLINE typename boost::enable_if<
		boost::is_integral<T>,
		NTL::RR
	>::type
	to_RR_full_significant(T const target) {
		return to_ZZ(target);
	}
	//
	// specialization to_RR_full_significant: is_float(T)
	//
	template<typename T>
	SPRIG_INLINE typename boost::enable_if<
		boost::is_float<T>,
		NTL::RR
	>::type
	to_RR_full_significant(T const target) {
		typename significant<T>::digits_type digits10 = significant<T>::digits10();
		return NTL::trunc(to_RR(target) * digits10);
	}
	//
	// specialization to_RR_full_significant: is_c_str(T)
	//
	template<typename T>
	SPRIG_INLINE typename boost::enable_if<
		is_c_str<T>,
		NTL::RR
	>::type
	to_RR_full_significant(T const target) {
		typename significant<T>::digits_type digits10 = significant<T>::digits10(target);
		return NTL::trunc(to_RR(target) * digits10);
	}
	//
	// specialization to_RR_full_significant: is_basic_string(T)
	//
	template<typename T>
	SPRIG_INLINE typename boost::enable_if<
		is_basic_string<T>,
		NTL::RR
	>::type
	to_RR_full_significant(T const& target) {
		typename significant<T>::digits_type digits = significant<T>::digits(target);
		return NTL::trunc(to_RR(target) * NTL::power(to_RR(10), digits));
	}

	//
	// to_ZZ_significant
	//
	// specialization to_ZZ_significant: is_integral(T)
	//
	template<typename T>
	SPRIG_INLINE typename boost::enable_if<
		boost::is_integral<T>,
		NTL::ZZ
	>::type
	to_ZZ_significant(T const target) {
		return to_ZZ(target);
	}
	//
	// specialization to_ZZ_significant: is_float(T)
	//
	template<typename T>
	SPRIG_INLINE typename boost::enable_if<
		boost::is_float<T>,
		NTL::ZZ
	>::type
	to_ZZ_significant(T const target) {
		return to_ZZ(target);
	}
	//
	// specialization to_ZZ_significant: is_c_str(T)
	//
	template<typename T>
	SPRIG_INLINE typename boost::enable_if<
		is_c_str<T>,
		NTL::ZZ
	>::type
	to_ZZ_significant(T const target) {
		return to_ZZ(target);
	}
	//
	// specialization to_ZZ_significant: is_basic_string(T)
	//
	template<typename T>
	SPRIG_INLINE typename boost::enable_if<
		is_basic_string<T>,
		NTL::ZZ
	>::type
	to_ZZ_significant(T const& target) {
		return to_ZZ(target);
	}

	//
	// to_RR_significant
	//
	// specialization to_RR_significant: is_integral(T)
	//
	template<typename T>
	SPRIG_INLINE typename boost::enable_if<
		boost::is_integral<T>,
		NTL::RR
	>::type
	to_RR_significant(T const target) {
		return to_RR(target);
	}
	//
	// specialization to_RR_significant: is_float(T)
	//
	template<typename T>
	SPRIG_INLINE typename boost::enable_if<
		boost::is_float<T>,
		NTL::RR
	>::type
	to_RR_significant(T const target) {
		typename significant<T>::digits_type digits10 = significant<T>::digits10();
		return to_RR_full_significant(target) / digits10;
	}
	//
	// specialization to_RR_significant: is_c_str(T)
	//
	template<typename T>
	SPRIG_INLINE typename boost::enable_if<
		is_c_str<T>,
		NTL::RR
	>::type
	to_RR_significant(T const target) {
		return to_RR(target);
	}
	//
	// specialization to_RR_significant: is_basic_string(T)
	//
	template<typename T>
	SPRIG_INLINE typename boost::enable_if<
		is_basic_string<T>,
		NTL::RR
	>::type
	to_RR_significant(T const& target) {
		return to_RR(target);
	}
} // namespace sprig

#endif	// #ifndef SPRIG_NTL_SIGNIFICANT_HPP


/*
 * This file is part of a C++ interface to the Radiative Transfer for Energetics (RTE)
 * and Rapid Radiative Transfer Model for GCM applications Parallel (RRTMGP).
 *
 * The original code is found at https://github.com/RobertPincus/rte-rrtmgp.
 *
 * Contacts: Robert Pincus and Eli Mlawer
 * email: rrtmgp@aer.com
 *
 * Copyright 2015-2020,  Atmospheric and Environmental Research and
 * Regents of the University of Colorado.  All right reserved.
 *
 * This C++ interface can be downloaded from https://github.com/Chiil/rrtmgp_cpp
 *
 * Contact: Chiel van Heerwaarden
 * email: chiel.vanheerwaarden@wur.nl
 *
 * Copyright 2020, Wageningen University & Research.
 *
 * Use and duplication is permitted under the terms of the
 * BSD 3-clause license, see http://opensource.org/licenses/BSD-3-Clause
 *
 */

#include <boost/algorithm/string.hpp>
#include <cmath>

#include "Netcdf_interface.h"
#include "Array.h"
#include "Gas_concs.h"
#include "Gas_optics.h"
#include "Optical_props.h"
#include "Source_functions.h"
#include "Fluxes.h"
#include "Rte_lw.h"
#include "Rte_sw.h"

#ifdef FLOAT_SINGLE_RRTMGP
#define FLOAT_TYPE float
#else
#define FLOAT_TYPE double
#endif

namespace
{
    std::vector<std::string> get_variable_string(
            const std::string& var_name,
            std::vector<int> i_count,
            Netcdf_handle& input_nc,
            const int string_len,
            bool trim=true)
    {
        // Multiply all elements in i_count.
        int total_count = std::accumulate(i_count.begin(), i_count.end(), 1, std::multiplies<>());

        // Add the string length as the rightmost dimension.
        i_count.push_back(string_len);

        // Multiply all elements in i_count.
        // int total_count_char = std::accumulate(i_count.begin(), i_count.end(), 1, std::multiplies<>());

        // Read the entire char array;
        std::vector<char> var_char;
        var_char = input_nc.get_variable<char>(var_name, i_count);

        std::vector<std::string> var;

        for (int n=0; n<total_count; ++n)
        {
            std::string s(var_char.begin()+n*string_len, var_char.begin()+(n+1)*string_len);
            if (trim)
                boost::trim(s);
            var.push_back(s);
        }

        return var;
    }

    template<typename TF>
    Gas_optics<TF> load_and_init_gas_optics(
            Master& master,
            const Gas_concs<TF>& gas_concs,
            const std::string& coef_file)
    {
        // READ THE COEFFICIENTS FOR THE OPTICAL SOLVER.
        Netcdf_file coef_nc(master, coef_file, Netcdf_mode::Read);

        // Read k-distribution information.
        int n_temps = coef_nc.get_dimension_size("temperature");
        int n_press = coef_nc.get_dimension_size("pressure");
        int n_absorbers = coef_nc.get_dimension_size("absorber");

        // CvH: I hardcode the value to 32 now, because coef files
        // CvH: changed dimension name inconsistently.
        // int n_char = coef_nc.get_dimension_size("string_len");
        constexpr int n_char = 32;

        int n_minorabsorbers = coef_nc.get_dimension_size("minor_absorber");
        int n_extabsorbers = coef_nc.get_dimension_size("absorber_ext");
        int n_mixingfracs = coef_nc.get_dimension_size("mixing_fraction");
        int n_layers = coef_nc.get_dimension_size("atmos_layer");
        int n_bnds = coef_nc.get_dimension_size("bnd");
        int n_gpts = coef_nc.get_dimension_size("gpt");
        int n_pairs = coef_nc.get_dimension_size("pair");
        int n_minor_absorber_intervals_lower = coef_nc.get_dimension_size("minor_absorber_intervals_lower");
        int n_minor_absorber_intervals_upper = coef_nc.get_dimension_size("minor_absorber_intervals_upper");
        int n_contributors_lower = coef_nc.get_dimension_size("contributors_lower");
        int n_contributors_upper = coef_nc.get_dimension_size("contributors_upper");

        // Read gas names.
        Array<std::string,1> gas_names(
                get_variable_string("gas_names", {n_absorbers}, coef_nc, n_char, true), {n_absorbers});

        Array<int,3> key_species(
                coef_nc.get_variable<int>("key_species", {n_bnds, n_layers, 2}),
                {2, n_layers, n_bnds});
        Array<TF,2> band_lims(coef_nc.get_variable<TF>("bnd_limits_wavenumber", {n_bnds, 2}), {2, n_bnds});
        Array<int,2> band2gpt(coef_nc.get_variable<int>("bnd_limits_gpt", {n_bnds, 2}), {2, n_bnds});
        Array<TF,1> press_ref(coef_nc.get_variable<TF>("press_ref", {n_press}), {n_press});
        Array<TF,1> temp_ref(coef_nc.get_variable<TF>("temp_ref", {n_temps}), {n_temps});

        TF temp_ref_p = coef_nc.get_variable<TF>("absorption_coefficient_ref_P");
        TF temp_ref_t = coef_nc.get_variable<TF>("absorption_coefficient_ref_T");
        TF press_ref_trop = coef_nc.get_variable<TF>("press_ref_trop");

        Array<TF,3> kminor_lower(
                coef_nc.get_variable<TF>("kminor_lower", {n_temps, n_mixingfracs, n_contributors_lower}),
                {n_contributors_lower, n_mixingfracs, n_temps});
        Array<TF,3> kminor_upper(
                coef_nc.get_variable<TF>("kminor_upper", {n_temps, n_mixingfracs, n_contributors_upper}),
                {n_contributors_upper, n_mixingfracs, n_temps});

        Array<std::string,1> gas_minor(get_variable_string("gas_minor", {n_minorabsorbers}, coef_nc, n_char),
                {n_minorabsorbers});

        Array<std::string,1> identifier_minor(
                get_variable_string("identifier_minor", {n_minorabsorbers}, coef_nc, n_char), {n_minorabsorbers});

        Array<std::string,1> minor_gases_lower(
                get_variable_string("minor_gases_lower", {n_minor_absorber_intervals_lower}, coef_nc, n_char),
                {n_minor_absorber_intervals_lower});
        Array<std::string,1> minor_gases_upper(
                get_variable_string("minor_gases_upper", {n_minor_absorber_intervals_upper}, coef_nc, n_char),
                {n_minor_absorber_intervals_upper});

        Array<int,2> minor_limits_gpt_lower(
                coef_nc.get_variable<int>("minor_limits_gpt_lower", {n_minor_absorber_intervals_lower, n_pairs}),
                {n_pairs, n_minor_absorber_intervals_lower});
        Array<int,2> minor_limits_gpt_upper(
                coef_nc.get_variable<int>("minor_limits_gpt_upper", {n_minor_absorber_intervals_upper, n_pairs}),
                {n_pairs, n_minor_absorber_intervals_upper});

        Array<int,1> minor_scales_with_density_lower(
                coef_nc.get_variable<int>("minor_scales_with_density_lower", {n_minor_absorber_intervals_lower}),
                {n_minor_absorber_intervals_lower});
        Array<int,1> minor_scales_with_density_upper(
                coef_nc.get_variable<int>("minor_scales_with_density_upper", {n_minor_absorber_intervals_upper}),
                {n_minor_absorber_intervals_upper});

        Array<int,1> scale_by_complement_lower(
                coef_nc.get_variable<int>("scale_by_complement_lower", {n_minor_absorber_intervals_lower}),
                {n_minor_absorber_intervals_lower});
        Array<int,1> scale_by_complement_upper(
                coef_nc.get_variable<int>("scale_by_complement_upper", {n_minor_absorber_intervals_upper}),
                {n_minor_absorber_intervals_upper});

        Array<std::string,1> scaling_gas_lower(
                get_variable_string("scaling_gas_lower", {n_minor_absorber_intervals_lower}, coef_nc, n_char),
                {n_minor_absorber_intervals_lower});
        Array<std::string,1> scaling_gas_upper(
                get_variable_string("scaling_gas_upper", {n_minor_absorber_intervals_upper}, coef_nc, n_char),
                {n_minor_absorber_intervals_upper});

        Array<int,1> kminor_start_lower(
                coef_nc.get_variable<int>("kminor_start_lower", {n_minor_absorber_intervals_lower}),
                {n_minor_absorber_intervals_lower});
        Array<int,1> kminor_start_upper(
                coef_nc.get_variable<int>("kminor_start_upper", {n_minor_absorber_intervals_upper}),
                {n_minor_absorber_intervals_upper});

        Array<TF,3> vmr_ref(
                coef_nc.get_variable<TF>("vmr_ref", {n_temps, n_extabsorbers, n_layers}),
                {n_layers, n_extabsorbers, n_temps});

        Array<TF,4> kmajor(
                coef_nc.get_variable<TF>("kmajor", {n_temps, n_press+1, n_mixingfracs, n_gpts}),
                {n_gpts, n_mixingfracs, n_press+1, n_temps});

        // Keep the size at zero, if it does not exist.
        Array<TF,3> rayl_lower;
        Array<TF,3> rayl_upper;

        if (coef_nc.variable_exists("rayl_lower"))
        {
            rayl_lower.set_dims({n_gpts, n_mixingfracs, n_temps});
            rayl_upper.set_dims({n_gpts, n_mixingfracs, n_temps});
            rayl_lower = coef_nc.get_variable<TF>("rayl_lower", {n_temps, n_mixingfracs, n_gpts});
            rayl_upper = coef_nc.get_variable<TF>("rayl_upper", {n_temps, n_mixingfracs, n_gpts});
        }

        // Is it really LW if so read these variables as well.
        if (coef_nc.variable_exists("totplnk"))
        {
            int n_internal_sourcetemps = coef_nc.get_dimension_size("temperature_Planck");

            Array<TF,2> totplnk(
                    coef_nc.get_variable<TF>( "totplnk", {n_bnds, n_internal_sourcetemps}),
                    {n_internal_sourcetemps, n_bnds});
            Array<TF,4> planck_frac(
                    coef_nc.get_variable<TF>("plank_fraction", {n_temps, n_press+1, n_mixingfracs, n_gpts}),
                    {n_gpts, n_mixingfracs, n_press+1, n_temps});

            // Construct the k-distribution.
            return Gas_optics<TF>(
                    gas_concs,
                    gas_names,
                    key_species,
                    band2gpt,
                    band_lims,
                    press_ref,
                    press_ref_trop,
                    temp_ref,
                    temp_ref_p,
                    temp_ref_t,
                    vmr_ref,
                    kmajor,
                    kminor_lower,
                    kminor_upper,
                    gas_minor,
                    identifier_minor,
                    minor_gases_lower,
                    minor_gases_upper,
                    minor_limits_gpt_lower,
                    minor_limits_gpt_upper,
                    minor_scales_with_density_lower,
                    minor_scales_with_density_upper,
                    scaling_gas_lower,
                    scaling_gas_upper,
                    scale_by_complement_lower,
                    scale_by_complement_upper,
                    kminor_start_lower,
                    kminor_start_upper,
                    totplnk,
                    planck_frac,
                    rayl_lower,
                    rayl_upper);
        }
        else
        {
            Array<TF,1> solar_src_quiet(
                    coef_nc.get_variable<TF>("solar_source_quiet", {n_gpts}), {n_gpts});
            Array<TF,1> solar_src_facular(
                    coef_nc.get_variable<TF>("solar_source_facular", {n_gpts}), {n_gpts});
            Array<TF,1> solar_src_sunspot(
                    coef_nc.get_variable<TF>("solar_source_sunspot", {n_gpts}), {n_gpts});

            TF tsi = coef_nc.get_variable<TF>("tsi_default");
            TF mg_index = coef_nc.get_variable<TF>("mg_default");
            TF sb_index = coef_nc.get_variable<TF>("sb_default");

            return Gas_optics<TF>(
                    gas_concs,
                    gas_names,
                    key_species,
                    band2gpt,
                    band_lims,
                    press_ref,
                    press_ref_trop,
                    temp_ref,
                    temp_ref_p,
                    temp_ref_t,
                    vmr_ref,
                    kmajor,
                    kminor_lower,
                    kminor_upper,
                    gas_minor,
                    identifier_minor,
                    minor_gases_lower,
                    minor_gases_upper,
                    minor_limits_gpt_lower,
                    minor_limits_gpt_upper,
                    minor_scales_with_density_lower,
                    minor_scales_with_density_upper,
                    scaling_gas_lower,
                    scaling_gas_upper,
                    scale_by_complement_lower,
                    scale_by_complement_upper,
                    kminor_start_lower,
                    kminor_start_upper,
                    solar_src_quiet,
                    solar_src_facular,
                    solar_src_sunspot,
                    tsi,
                    mg_index,
                    sb_index,
                    rayl_lower,
                    rayl_upper);
        }
        // End reading of k-distribution.
    }
}

template<typename TF>
void load_gas_concs(Gas_concs<TF>& gas_concs, Netcdf_file& input_nc)
{
    // This part is contained in the create
    Netcdf_group rad_nc = input_nc.get_group("radiation");

    const int n_lay = rad_nc.get_dimension_size("lay");

    gas_concs.set_vmr("h2o",
            Array<TF,1>(rad_nc.get_variable<TF>("h2o", {n_lay}), {n_lay}));
    gas_concs.set_vmr("co2",
            rad_nc.get_variable<TF>("co2"));
    gas_concs.set_vmr("o3",
            Array<TF,1>(rad_nc.get_variable<TF>("o3", {n_lay}), {n_lay}));
    gas_concs.set_vmr("n2o",
            rad_nc.get_variable<TF>("n2o"));
    gas_concs.set_vmr("ch4",
            rad_nc.get_variable<TF>("ch4"));
    gas_concs.set_vmr("o2",
            rad_nc.get_variable<TF>("o2"));
    gas_concs.set_vmr("n2",
            rad_nc.get_variable<TF>("n2"));
}

template<typename TF>
void solve_radiation(Master& master)
{
    // We are doing a single column run.
    const int n_col = 1;

    // These are the global variables that need to be contained in a class.
    Gas_concs<TF> gas_concs;

    std::unique_ptr<Gas_optics<TF>> kdist_lw;
    std::unique_ptr<Gas_optics<TF>> kdist_sw;

    // This is the part that is done in the initialization.
    Netcdf_file file_nc(master, "test_rcemip_input.nc", Netcdf_mode::Read);

    load_gas_concs<TF>(gas_concs, file_nc);
    kdist_lw = std::make_unique<Gas_optics<TF>>(
            load_and_init_gas_optics(master, gas_concs, "coefficients_lw.nc"));
    kdist_sw = std::make_unique<Gas_optics<TF>>(
            load_and_init_gas_optics(master, gas_concs, "coefficients_sw.nc"));

    // LOAD THE LONGWAVE SPECIFIC BOUNDARY CONDITIONS.
    // Set the surface temperature and emissivity.
    Array<TF,1> t_sfc({1});
    t_sfc({1}) = TF(300.);

    const int n_bnd = kdist_lw->get_nband();
    Array<TF,2> emis_sfc({n_bnd, 1});
    for (int ibnd=1; ibnd<=n_bnd; ++ibnd)
        emis_sfc({ibnd, 1}) = 1.;

    const int n_ang = 1;

    // LOAD THE SHORTWAVE SPECIFIC BOUNDARY CONDITIONS.
    Array<TF,1> sza({n_col});
    Array<TF,2> sfc_alb_dir({n_bnd, n_col});
    Array<TF,2> sfc_alb_dif({n_bnd, n_col});

    sza({1}) = TF(0.7339109504636155);

    for (int ibnd=1; ibnd<=n_bnd; ++ibnd)
    {
        sfc_alb_dir({ibnd, 1}) = 0.07;
        sfc_alb_dif({ibnd, 1}) = 0.07;
    }

    Array<TF,1> mu0({n_col});
    mu0({1}) = std::cos(sza({1}));

    // Solve the full column once.
    Netcdf_group input_nc = file_nc.get_group("radiation");

    const int n_lay = input_nc.get_dimension_size("lay");
    const int n_lev = input_nc.get_dimension_size("lev");

    Array<TF,2> p_lay(input_nc.get_variable<TF>("p_lay", {n_lay, n_col}), {n_col, n_lay});
    Array<TF,2> t_lay(input_nc.get_variable<TF>("t_lay", {n_lay, n_col}), {n_col, n_lay});
    Array<TF,2> p_lev(input_nc.get_variable<TF>("p_lev", {n_lev, n_col}), {n_col, n_lev});
    Array<TF,2> t_lev(input_nc.get_variable<TF>("t_lev", {n_lev, n_col}), {n_col, n_lev});

    Array<TF,2> col_dry({n_col, n_lay});
    if (input_nc.variable_exists("col_dry"))
        col_dry = input_nc.get_variable<TF>("col_dry", {n_lay, n_col});
    else
    {
        kdist_lw->get_col_dry(col_dry, gas_concs.get_vmr("h2o"), p_lev);
        kdist_sw->get_col_dry(col_dry, gas_concs.get_vmr("h2o"), p_lev);
    }

    // Solve the longwave first.
    std::unique_ptr<Optical_props_arry<TF>> optical_props_lw =
            std::make_unique<Optical_props_1scl<TF>>(n_col, n_lay, *kdist_lw);

    Source_func_lw<TF> sources(n_col, n_lay, *kdist_lw);

    kdist_lw->gas_optics(
            p_lay,
            p_lev,
            t_lay,
            t_sfc,
            gas_concs,
            optical_props_lw,
            sources,
            col_dry,
            t_lev);

    std::unique_ptr<Fluxes_broadband<TF>> fluxes =
            std::make_unique<Fluxes_broadband<TF>>(n_col, n_lev);

    const int top_at_1 = p_lay({1, 1}) < p_lay({1, n_lay});

    const int n_gpt_lw = optical_props_lw->get_ngpt();
    Array<TF,3> lw_gpt_flux_up({n_col, n_lev, n_gpt_lw});
    Array<TF,3> lw_gpt_flux_dn({n_col, n_lev, n_gpt_lw});

    Rte_lw<TF>::rte_lw(
            optical_props_lw,
            top_at_1,
            sources,
            emis_sfc,
            Array<TF,2>(), // Add an empty array, no inc_flux.
            lw_gpt_flux_up,
            lw_gpt_flux_dn,
            n_ang);

    fluxes->reduce(
            lw_gpt_flux_up, lw_gpt_flux_dn,
            optical_props_lw, top_at_1);

    Array<TF,2> lw_flux_up ({n_col, n_lev});
    Array<TF,2> lw_flux_dn ({n_col, n_lev});
    Array<TF,2> lw_flux_net({n_col, n_lev});
    Array<TF,2> lw_heating ({n_col, n_lay});

    // Copy the data to the output.
    for (int ilev=1; ilev<=n_lev; ++ilev)
    {
        lw_flux_up ({1, ilev}) = fluxes->get_flux_up ()({1, ilev});
        lw_flux_dn ({1, ilev}) = fluxes->get_flux_dn ()({1, ilev});
        lw_flux_net({1, ilev}) = fluxes->get_flux_net()({1, ilev});
    }

    const int n_gpt_sw = kdist_sw->get_ngpt();
    Array<TF,2> toa_src({n_col, n_gpt_sw});

    std::unique_ptr<Optical_props_arry<TF>> optical_props_sw =
            std::make_unique<Optical_props_2str<TF>>(n_col, n_lay, *kdist_sw);

    kdist_sw->gas_optics(
            p_lay,
            p_lev,
            t_lay,
            gas_concs,
            optical_props_sw,
            toa_src,
            col_dry);

    const TF tsi_scaling = 0.4053176301654965;
    for (int igpt=1; igpt<=n_gpt_sw; ++igpt)
        toa_src({1, igpt}) *= tsi_scaling;

    Array<TF,3> sw_gpt_flux_up    ({n_col, n_lev, n_gpt_sw});
    Array<TF,3> sw_gpt_flux_dn    ({n_col, n_lev, n_gpt_sw});
    Array<TF,3> sw_gpt_flux_dn_dir({n_col, n_lev, n_gpt_sw});

    Rte_sw<TF>::rte_sw(
            optical_props_sw,
            top_at_1,
            mu0,
            toa_src,
            sfc_alb_dir,
            sfc_alb_dif,
            Array<TF,2>(), // Add an empty array, no inc_flux.
            sw_gpt_flux_up,
            sw_gpt_flux_dn,
            sw_gpt_flux_dn_dir);

    fluxes->reduce(
            sw_gpt_flux_up, sw_gpt_flux_dn, sw_gpt_flux_dn_dir,
            optical_props_sw, top_at_1);

    Array<TF,2> sw_flux_up ({n_col, n_lev});
    Array<TF,2> sw_flux_dn ({n_col, n_lev});
    Array<TF,2> sw_flux_net({n_col, n_lev});
    Array<TF,2> sw_heating ({n_col, n_lay});

    // Copy the data to the output.
    for (int ilev=1; ilev<=n_lev; ++ilev)
    {
        sw_flux_up ({1, ilev}) = fluxes->get_flux_up ()({1, ilev});
        sw_flux_dn ({1, ilev}) = fluxes->get_flux_dn ()({1, ilev});
        sw_flux_net({1, ilev}) = fluxes->get_flux_net()({1, ilev});
    }

    // Compute the heating rates.
    constexpr TF g = 9.80655;
    constexpr TF cp = 1005.;

    Array<TF,2> heating ({n_col, n_lay});

    for (int ilay=1; ilay<=n_lay; ++ilay)
    {
        lw_heating({1, ilay}) =
                ( lw_flux_up({1, ilay+1}) - lw_flux_up({1, ilay})
                - lw_flux_dn({1, ilay+1}) + lw_flux_dn({1, ilay}) )
                * g / ( cp * (p_lev({1, ilay+1}) - p_lev({1, ilay})) ) * 86400.;

        sw_heating({1, ilay}) =
                ( sw_flux_up({1, ilay+1}) - sw_flux_up({1, ilay})
                - sw_flux_dn({1, ilay+1}) + sw_flux_dn({1, ilay}) )
                * g / ( cp * (p_lev({1, ilay+1}) - p_lev({1, ilay})) ) * 86400.;

        heating({1, ilay}) = lw_heating({1, ilay}) + sw_heating({1, ilay});
    }

    // Store the radiation fluxes to a file
    Netcdf_file output_nc(master, "test_rcemip_output.nc", Netcdf_mode::Create);
    output_nc.add_dimension("col", n_col);
    output_nc.add_dimension("lev", n_lev);
    output_nc.add_dimension("lay", n_lay);

    auto nc_p_lev = output_nc.add_variable<TF>("lev", {"lev"});
    auto nc_p_lay = output_nc.add_variable<TF>("lay", {"lay"});
    nc_p_lev.insert(p_lev.v(), {0});
    nc_p_lay.insert(p_lay.v(), {0});

    auto nc_lw_flux_up  = output_nc.add_variable<TF>("lw_flux_up" , {"lev", "col"});
    auto nc_lw_flux_dn  = output_nc.add_variable<TF>("lw_flux_dn" , {"lev", "col"});
    auto nc_lw_flux_net = output_nc.add_variable<TF>("lw_flux_net", {"lev", "col"});
    auto nc_lw_heating  = output_nc.add_variable<TF>("lw_heating" , {"lay", "col"});

    nc_lw_flux_up .insert(lw_flux_up .v(), {0, 0});
    nc_lw_flux_dn .insert(lw_flux_dn .v(), {0, 0});
    nc_lw_flux_net.insert(lw_flux_net.v(), {0, 0});
    nc_lw_heating .insert(lw_heating .v(), {0, 0});

    auto nc_sw_flux_up  = output_nc.add_variable<TF>("sw_flux_up" , {"lev", "col"});
    auto nc_sw_flux_dn  = output_nc.add_variable<TF>("sw_flux_dn" , {"lev", "col"});
    auto nc_sw_flux_net = output_nc.add_variable<TF>("sw_flux_net", {"lev", "col"});
    auto nc_sw_heating  = output_nc.add_variable<TF>("sw_heating" , {"lay", "col"});

    nc_sw_flux_up .insert(sw_flux_up .v(), {0, 0});
    nc_sw_flux_dn .insert(sw_flux_dn .v(), {0, 0});
    nc_sw_flux_net.insert(sw_flux_net.v(), {0, 0});
    nc_sw_heating .insert(sw_heating .v(), {0, 0});

    auto nc_heating = output_nc.add_variable<TF>("heating", {"lay", "col"});
    nc_heating.insert(heating.v(), {0, 0});
}

int main()
{
    Master master;
    try
    {
        master.start();
        master.init();

        solve_radiation<FLOAT_TYPE>(master);
    }

    // Catch any exceptions and return 1.
    catch (const std::exception& e)
    {
        master.print_message("EXCEPTION: %s\n", e.what());
        return 1;
    }
    catch (...)
    {
        master.print_message("UNHANDLED EXCEPTION!\n");
        return 1;
    }

    // Return 0 in case of normal exit.
    return 0;
}