/* CirKit: A circuit toolkit
 * Copyright (C) 2009-2015  University of Bremen
 * Copyright (C) 2015-2017  EPFL
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

#include "bdd.hpp"

#include <vector>

#include <boost/assign/std/vector.hpp>
#include <boost/format.hpp>
#include <boost/range/algorithm.hpp>
#include <boost/range/counting_range.hpp>

#include <core/utils/bitset_utils.hpp>
#include <core/utils/range_utils.hpp>
#include <classical/dd/count_solutions.hpp>

using namespace boost::assign;

namespace cirkit
{

/******************************************************************************
 * Types                                                                      *
 ******************************************************************************/

enum class bdd_operation {
  _and, _or, _xor, _not, cof0, cof1, exists,
  constrain, restrict, round_down, round_up, round
};

/******************************************************************************
 * Private functions                                                          *
 ******************************************************************************/

/******************************************************************************
 * Public functions                                                           *
 ******************************************************************************/

bdd_manager::bdd_manager( unsigned nvars, unsigned log_max_objs, bool verbose )
  : dd_manager( nvars, log_max_objs, verbose ) {}

bdd_manager::~bdd_manager() {}

unsigned bdd_manager::bdd_and( unsigned f, unsigned g )
{
  /* terminating cases */
  if ( f == 0u ) { return 0u; }
  if ( g == 0u ) { return 0u; }
  if ( f == 1u ) { return g; }
  if ( g == 1u ) { return f; }
  if ( f == g )  { return f; }

  /* commutativity */
  if ( f > g ) { return bdd_and( g, f ); }

  const auto r = cache.lookup( f, g, (unsigned)bdd_operation::_and );
  if ( r >= 0 ) { return r; }

  const auto& node1 = nodes.at( f );
  const auto& node2 = nodes.at( g );
  unsigned rlow, rhigh;
  if ( node1.var < node2.var )
  {
    rlow = bdd_and( node1.low, g );
    rhigh = bdd_and( node1.high, g );
  }
  else if ( node1.var > node2.var )
  {
    rlow = bdd_and( f, node2.low );
    rhigh = bdd_and( f, node2.high );
  }
  else
  {
    rlow = bdd_and( node1.low, node2.low );
    rhigh = bdd_and( node1.high, node2.high );
  }

  auto idx = unique_create( std::min( node1.var, node2.var ), rhigh, rlow );
  return cache.insert( f, g, (unsigned)bdd_operation::_and, idx );
}

unsigned bdd_manager::bdd_or( unsigned f, unsigned g )
{
  /* terminating cases */
  if ( f == 0u ) { return g; }
  if ( g == 0u ) { return f; }
  if ( f == 1u ) { return 1u; }
  if ( g == 1u ) { return 1u; }
  if ( f == g )  { return f; }

  /* commutativity */
  if ( f > g ) { return bdd_or( g, f ); }

  const auto r = cache.lookup( f, g, (unsigned)bdd_operation::_or );
  if ( r >= 0 ) { return r; }

  const auto& node1 = nodes.at( f );
  const auto& node2 = nodes.at( g );
  unsigned rlow, rhigh;
  if ( node1.var < node2.var )
  {
    rlow = bdd_or( node1.low, g );
    rhigh = bdd_or( node1.high, g );
  }
  else if ( node1.var > node2.var )
  {
    rlow = bdd_or( f, node2.low );
    rhigh = bdd_or( f, node2.high );
  }
  else
  {
    rlow = bdd_or( node1.low, node2.low );
    rhigh = bdd_or( node1.high, node2.high );
  }

  auto idx = unique_create( std::min( node1.var, node2.var ), rhigh, rlow );
  return cache.insert( f, g, (unsigned)bdd_operation::_or, idx );
}

unsigned bdd_manager::bdd_xor( unsigned f, unsigned g )
{
  /* terminating cases */
  if ( f == 0u ) { return g; }
  if ( g == 0u ) { return f; }
  if ( f == g )  { return 0u; }

  /* commutativity */
  if ( f > g ) { return bdd_xor( g, f ); }

  const auto r = cache.lookup( f, g, (unsigned)bdd_operation::_xor );
  if ( r >= 0 ) { return r; }

  const auto& node1 = nodes.at( f );
  const auto& node2 = nodes.at( g );
  unsigned rlow, rhigh;
  if ( node1.var < node2.var )
  {
    rlow = bdd_xor( node1.low, g );
    rhigh = bdd_xor( node1.high, g );
  }
  else if ( node1.var > node2.var )
  {
    rlow = bdd_xor( f, node2.low );
    rhigh = bdd_xor( f, node2.high );
  }
  else
  {
    rlow = bdd_xor( node1.low, node2.low );
    rhigh = bdd_xor( node1.high, node2.high );
  }

  auto idx = unique_create( std::min( node1.var, node2.var ), rhigh, rlow );
  return cache.insert( f, g, (unsigned)bdd_operation::_xor, idx );
}

unsigned bdd_manager::bdd_not( unsigned f )
{
  /* terminating cases */
  if ( f == 0u ) { return 1u; }
  if ( f == 1u ) { return 0u; }

  const auto r = cache.lookup( f, f, (unsigned)bdd_operation::_not );
  if ( r >= 0 ) { return r; }

  const auto& node = nodes.at( f );

  auto rlow = bdd_not( node.low );
  auto rhigh = bdd_not( node.high );

  auto idx = unique_create( node.var, rhigh, rlow );
  return cache.insert( f, f, (unsigned)bdd_operation::_not, idx );
}

unsigned bdd_manager::bdd_cof0( unsigned f, unsigned v )
{
  /* terminating cases */
  if ( f <= 1u ) { return f; }

  const auto& node = nodes.at( f );
  if ( node.var > v ) { return f; }

  const auto r = cache.lookup( f, v, (unsigned)bdd_operation::cof0 );
  if ( r >= 0 ) { return r; }

  unsigned idx;
  if ( node.var < v )
  {
    const auto rlow  = bdd_cof0( node.low, v );
    const auto rhigh = bdd_cof0( node.high, v );
    idx = unique_create( node.var, rhigh, rlow );
  }
  else
  {
    idx = node.low;
  }
  return cache.insert( f, v, (unsigned)bdd_operation::cof0, idx );
}

unsigned bdd_manager::bdd_cof1( unsigned f, unsigned v )
{
  /* terminating cases */
  if ( f <= 1u ) { return f; }

  const auto& node = nodes.at( f );
  if ( node.var > v ) { return f; }

  const auto r = cache.lookup( f, v, (unsigned)bdd_operation::cof1 );
  if ( r >= 0 ) { return r; }

  unsigned idx;
  if ( node.var < v )
  {
    const auto rlow  = bdd_cof1( node.low, v );
    const auto rhigh = bdd_cof1( node.high, v );
    idx = unique_create( node.var, rhigh, rlow );
  }
  else
  {
    idx = node.high;
  }
  return cache.insert( f, v, (unsigned)bdd_operation::cof1, idx );
}

unsigned bdd_manager::bdd_exists( unsigned f, unsigned g )
{
  /* terminating cases */
  if ( g == 1u || f <= 1u ) { return f; }

  const auto& node1 = nodes.at( f );
  const auto& node2 = nodes.at( g );

  if ( node1.var > node2.var )
  {
    return bdd_exists( f, node2.high );
  }

  const auto r = cache.lookup( f, g, (unsigned)bdd_operation::exists );
  if ( r >= 0 ) { return r; }

  unsigned idx;
  auto rlow  = bdd_exists( node1.low, node1.var == node2.var ? node2.high : g );

  if ( rlow == 1 && node1.var == node2.var )
  {
    idx = 1;
  }
  else
  {
    auto rhigh = bdd_exists( node1.high, node1.var == node2.var ? node2.high : g );

    if ( node1.var < node2.var )
    {
      idx = unique_create( node1.var, rhigh, rlow );
    }
    else
    {
      idx = bdd_or( rlow, rhigh );
    }
  }

  return cache.insert( f, g, (unsigned)bdd_operation::exists, idx );
}

unsigned bdd_manager::bdd_constrain( unsigned f, unsigned g )
{
  /* terminating cases */
  if ( g == 0u )            { return 0u; }
  if ( g == 1u || f <= 1u ) { return f;  }
  if ( f == g )             { return 1u; }

  const auto r = cache.lookup( f, g, (unsigned)bdd_operation::constrain );
  if ( r >= 0 ) { return r; }

  const auto& node1 = nodes.at( f );
  const auto& node2 = nodes.at( g );

  unsigned idx;

  do {
    unsigned v;

    if ( node1.var < node2.var )
    {
      v = node1.var;
    }
    else
    {
      v = node2.var;
      if ( node2.low == 0u )
      {
        idx = bdd_constrain( node1.var == v ? node1.high : f, node2.high );
        break;
      }
      else if ( node2.high == 0u )
      {
        idx = bdd_constrain( node1.var == v ? node1.low : f, node2.low );
        break;
      }
    }

    auto rlow = bdd_constrain( node1.var == v ? node1.low : f, node2.var == v ? node2.low : g );
    auto rhigh = bdd_constrain( node1.var == v ? node1.high : f, node2.var == v ? node2.high : g );
    idx = unique_create( v, rhigh, rlow );
  } while ( false );

  return cache.insert( f, g, (unsigned)bdd_operation::constrain, idx );
}

unsigned bdd_manager::bdd_restrict( unsigned f, unsigned g )
{
  /* terminating cases */
  if ( g == 0u )            { return 0u; }
  if ( g == 1u || f <= 1u ) { return f;  }
  if ( f == g )             { return 1u; }

  const auto r = cache.lookup( f, g, (unsigned)bdd_operation::restrict );
  if ( r >= 0 ) { return r; }

  const auto& node1 = nodes.at( f );
  const auto& node2 = nodes.at( g );

  unsigned idx;

  do {
    unsigned v;

    if ( node1.var < node2.var )
    {
      v = node1.var;
    }
    else
    {
      v = node2.var;
      if ( node2.low == 0u )
      {
        idx = bdd_restrict( node1.var == v ? node1.high : f, node2.high );
        break;
      }
      else if ( node2.high == 0u )
      {
        idx = bdd_restrict( node1.var == v ? node1.low : f, node2.low );
        break;
      }
    }

    /* special case in RESTRICT */
    if ( node1.low == node1.high )
    {
      idx = bdd_restrict( f, bdd_exists( g, v + 2u ) );
      break;
    }

    auto rlow = bdd_restrict( node1.var == v ? node1.low : f, node2.var == v ? node2.low : g );
    auto rhigh = bdd_restrict( node1.var == v ? node1.high : f, node2.var == v ? node2.high : g );
    idx = unique_create( v, rhigh, rlow );
  } while ( false );

  return cache.insert( f, g, (unsigned)bdd_operation::restrict, idx );
}

unsigned bdd_manager::bdd_round_to( unsigned f, unsigned level, unsigned cop, unsigned to )
{
  properties::ptr settings = std::make_shared<properties>();
  properties::ptr statistics = std::make_shared<properties>();

  count_solutions( bdd( this, f ), settings, statistics );

  return bdd_round_to( f, level, cop, to, statistics->get<std::map<unsigned, boost::multiprecision::uint256_t>>( "count_map" ) );
}

unsigned bdd_manager::bdd_round_to( unsigned f, unsigned level, unsigned cop, unsigned to, const std::map<unsigned, boost::multiprecision::uint256_t>& count_map )
{
  /* terminating cases */
  if ( f <= 1u ) { return f; }

  const auto r = cache.lookup( f, level, cop );
  if ( r >= 0 ) { return r; }

  const auto& node = nodes.at( f );

  auto idx = 0u;
  if ( node.var < level )
  {
    auto rlow = bdd_round_to( node.low, level, cop, to, count_map );
    auto rhigh = bdd_round_to( node.high, level, cop, to, count_map );

    idx = unique_create( node.var, rhigh, rlow );
  }
  else
  {
    auto cl = count_map.at( node.low );
    auto ch = count_map.at( node.high );

    if ( cl < ch )
    {
      auto rhigh = bdd_round_to( node.high, level, cop, to, count_map );
      idx = unique_create( node.var, rhigh, to );
    }
    else
    {
      auto rlow = bdd_round_to( node.low, level, cop, to, count_map );
      idx = unique_create( node.var, to, rlow );
    }
  }
  return cache.insert( f, level, cop, idx );
}

unsigned bdd_manager::bdd_round_down( unsigned f, unsigned level )
{
  return bdd_round_to( f, level, (unsigned)bdd_operation::round_down, 0u );
}

unsigned bdd_manager::bdd_round_up( unsigned f, unsigned level )
{
  return bdd_round_to( f, level, (unsigned)bdd_operation::round_up, 1u );
}

unsigned bdd_manager::bdd_round( unsigned f, unsigned level )
{
  /* terminating cases */
  if ( f <= 1u ) { return f; }

  const auto r = cache.lookup( f, level, (unsigned)bdd_operation::round );
  if ( r >= 0 ) { return r; }

  const auto& node = nodes.at( f );

  auto idx = 0u;
  if ( node.var < level )
  {
    auto rlow = bdd_round( node.low, level );
    auto rhigh = bdd_round( node.high, level );

    idx = unique_create( node.var, rhigh, rlow );
  }
  else
  {
    auto onset = count_solutions( bdd( this, f ) ) / ( 1ull << node.var );
    auto all   = 1ull << ( nvars - node.var );

    if ( ( onset << 1u ) > all ) /* if onset / all > .5 */
    {
      idx = 1u;
    }
  }
  return cache.insert( f, level, (unsigned)bdd_operation::round, idx );
}

bdd_manager_ptr bdd_manager::create( unsigned nvars, unsigned log_max_objs, bool verbose )
{
  return std::make_shared<bdd_manager>( nvars, log_max_objs, verbose );
}

unsigned bdd_manager::unique_create( unsigned var, unsigned high, unsigned low )
{
  if ( verbose )
  {
    //std::cout << boost::format( "[i] attempt to create (%d, %d, %d)" ) % var % high % low << std::endl;
  }
  assert( var < nvars );
  assert( var < nodes[high].var );
  assert( var < nodes[low].var );

  if ( high == low ) { return high; }

  return unique_lookup( var, high, low );
}

std::ostream& operator<<( std::ostream& os, const bdd_manager& mgr )
{
  for ( auto i : boost::counting_range( 0u, mgr.nvars + 2u ) )
  {
    os << i << ": " << mgr.nodes[i] << std::endl;
  }

  auto * q = mgr.unique;

  while ( q != mgr.unique + mgr.nodes.size() )
  {
    if ( *q )
    {
      os << *q << ": " << mgr.nodes[*q] << std::endl;
    }
    ++q;
  }

  return os;
}

bdd& bdd::operator=( const bdd& other )
{
  if ( this == &other ) { return *this; }
  assert( !manager || manager == other.manager );
  manager = other.manager;
  index   = other.index;
  return *this;
}

unsigned bdd::var() const
{
  return manager->get_var( index );
}

bdd bdd::high() const
{
  return bdd( manager, manager->get_high( index ) );
}

bdd bdd::low() const
{
  return bdd( manager, manager->get_low( index ) );
}

bdd bdd::operator&&( const bdd& other ) const
{
  assert( manager == other.manager );
  return bdd( manager, manager->bdd_and( index, other.index ) );
}

bdd bdd::operator||( const bdd& other ) const
{
  assert( manager == other.manager );
  return bdd( manager, manager->bdd_or( index, other.index ) );
}

bdd bdd::operator^( const bdd& other ) const
{
  assert( manager == other.manager );
  return bdd( manager, manager->bdd_xor( index, other.index ) );
}

bdd bdd::operator!() const
{
  return bdd( manager, manager->bdd_not( index ) );
}

bdd bdd::cof0( unsigned v ) const
{
  return bdd( manager, manager->bdd_cof0( index, v ) );
}

bdd bdd::cof1( unsigned v ) const
{
  return bdd( manager, manager->bdd_cof1( index, v ) );
}

bdd bdd::exists( const bdd& other ) const
{
  assert( manager == other.manager );
  return bdd( manager, manager->bdd_exists( index, other.index ) );
}

bdd bdd::constrain( const bdd& other ) const
{
  assert( manager == other.manager );
  return bdd( manager, manager->bdd_constrain( index, other.index ) );
}

bdd bdd::restrict( const bdd& other ) const
{
  assert( manager == other.manager );
  return bdd( manager, manager->bdd_restrict( index, other.index ) );
}

bdd bdd::round_down( unsigned level ) const
{
  return bdd( manager, manager->bdd_round_down( index, level ) );
}

bdd bdd::round_up( unsigned level ) const
{
  return bdd( manager, manager->bdd_round_up( index, level ) );
}

bdd bdd::round( unsigned level ) const
{
  return bdd( manager, manager->bdd_round( index, level ) );
}

bool bdd::equals( const bdd& other ) const
{
  assert( manager == other.manager );
  return index == other.index;
}

std::ostream &operator<<(std::ostream &stream, bdd::const_param_ref bdd)
{
  if ( bdd.is_bot() ) {
    return stream << "(zero)";
  }
  else if ( bdd.is_top() ) {
    return stream << "(one)";
  }
  else {
    return stream << boost::format ( "(var=%d, high=%d, low=%d)") % bdd.var() % bdd.high() % bdd.low();
  }
}

}

// Local Variables:
// c-basic-offset: 2
// eval: (c-set-offset 'substatement-open 0)
// eval: (c-set-offset 'innamespace 0)
// End:


/// @file DeconvolverFiesta.tcc
/// @brief Class for a deconvolver based on the Fista
/// @details This concrete class defines a deconvolver used to estimate an
/// image from a dirty image, psf optionally using a mask and a weights image.
/// @ingroup Deconvolver
///
/// @copyright (c) 2007 CSIRO
/// Australia Telescope National Facility (ATNF)
/// Commonwealth Scientific and Industrial Research Organisation (CSIRO)
/// PO Box 76, Epping NSW 1710, Australia
/// atnf-enquiries@csiro.au
///
/// This file is part of the ASKAP software distribution.
///
/// The ASKAP software distribution is free software: you can redistribute it
/// and/or modify it under the terms of the GNU General Public License as
/// published by the Free Software Foundation; either version 2 of the License,
/// or (at your option) any later version.
///
/// This program is distributed in the hope that it will be useful,
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
/// GNU General Public License for more details.
///
/// You should have received a copy of the GNU General Public License
/// along with this program; if not, write to the Free Software
/// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
///
/// @author Tim Cornwell <tim.cornwell@csiro.au>
///

#include <string>

#include <casacore/casa/aips.h>
#include <boost/shared_ptr.hpp>
#include <casacore/casa/Arrays/Array.h>
#include <casacore/casa/Arrays/ArrayMath.h>
#include <casacore/casa/BasicSL/STLIO.h>

#include <fft/FFTWrapper.h>
#include <askap/AskapLogging.h>
ASKAP_LOGGER(decfistalogger, ".deconvolution.fista");

#include <deconvolution/DeconvolverFista.h>
#include <deconvolution/MultiScaleBasisFunction.h>
#include <measurementequation/SynthesisParamsHelper.h>
#include <utils/ImageUtils.h>

namespace askap {

    namespace synthesis {

        /// @brief Class for a deconvolver based on the Fista Clean
        /// @details This base class defines a deconvolver used to estimate an
        /// image from a dirty image, psf optionally using a mask and a weights image.
        /// The template argument T is the type, and FT is the transform
        /// e.g. DeconvolverFista<Double, DComplex>
        /// @ingroup Deconvolver

        template<class T, class FT>
        DeconvolverFista<T, FT>::~DeconvolverFista()
        {
        };

        template<class T, class FT>
        DeconvolverFista<T, FT>::DeconvolverFista(Vector<Array<T> >& dirty, Vector<Array<T> >& psf)
                : DeconvolverBase<T, FT>::DeconvolverBase(dirty, psf)
        {
            if (this->itsNumberDirtyTerms > 1) {
                throw(AskapError("FISTA deconvolver cannot perform multi-term deconvolutions"));
            }
        };

        template<class T, class FT>
        DeconvolverFista<T, FT>::DeconvolverFista(Array<T>& dirty, Array<T>& psf)
                : DeconvolverBase<T, FT>::DeconvolverBase(dirty, psf)
        {
        };

        template<class T, class FT>
        void DeconvolverFista<T, FT>::configure(const LOFAR::ParameterSet& parset)
        {
            DeconvolverBase<T, FT>::configure(parset);

            // Make the basis function
            {
                std::vector<float> defaultScales(3);
                defaultScales[0] = 0.0;
                defaultScales[1] = 10.0;
                defaultScales[2] = 30.0;
                std::vector<float> scales = parset.getFloatVector("scales", defaultScales);

                ASKAPLOG_INFO_STR(decfistalogger, "Constructing Multiscale basis function with scales " << scales);
                const Bool orthogonal = parset.getBool("orthogonal", false);
                if (orthogonal) {
                    ASKAPLOG_DEBUG_STR(decfistalogger, "Multiscale basis functions will be orthogonalised");
                }

                itsBasisFunction = BasisFunction<Float>::ShPtr(new MultiScaleBasisFunction<Float>(scales,
                                   orthogonal));
            }
        }

        template<class T, class FT>
        void DeconvolverFista<T, FT>::initialise()
        {
            DeconvolverBase<T, FT>::initialise();

            if (itsBasisFunction) {
                this->itsBasisFunction->initialise(this->model().shape());
                itsBasisFunctionTransform.resize(itsBasisFunction->basisFunction().shape());
                casa::setReal(itsBasisFunctionTransform, itsBasisFunction->basisFunction().nonDegenerate());
                scimath::fft2d(itsBasisFunctionTransform, true);
            }

            ASKAPLOG_INFO_STR(decfistalogger, "Initialised FISTA solver");
        }

        template<class T, class FT>
        bool DeconvolverFista<T, FT>::deconvolve()
        {
            this->initialise();

            bool isMasked(this->itsWeight.nelements());
            if (!this->itsWeight(0).shape().conform(this->dirty().shape())) isMasked = false;

            Array<T> X, X_old, X_temp;

            X_temp.resize(this->model().shape());
            X_temp.set(T(0.0));

            X_old.resize(this->model().shape());
            X_old.set(T(0.0));

            X.resize(this->model().shape());
            X = this->model().copy();

            T absPeakVal;

            ASKAPLOG_INFO_STR(decfistalogger, "Performing Fista for " << this->control()->targetIter() << " iterations");

            this->updateResiduals(X);

            X_temp = X.copy();

            absPeakVal = max(abs(this->dirty()));

            //      T effectiveLambda(absPeakVal*this->control()->fractionalThreshold()+this->control()->lambda());
            T lambda(absPeakVal*(1.0 - this->control()->gain()));
            ASKAPLOG_INFO_STR(decfistalogger, "Effective lambda = " << lambda);

            T t_new = 1;

            T lipschitz(10.0);

            do {
                X_old = X_temp.copy();
                T t_old = t_new;

                this->updateResiduals(X);
                scimath::saveAsCasaImage("residuals.tab", this->dirty());

                X = X + this->dirty() / lipschitz;

                // Transform to other (e.g. multiscale) space
                Array<T> WX;
                scimath::saveAsCasaImage("X.tab", X);
                this->W(WX, X);
                scimath::saveAsCasaImage("W.tab", WX);

                // Now shrink the coefficients towards zero and clip those below
                // lambda/lipschitz.
                Array<T> shrink(WX.shape());

                Array<T> truncated(abs(WX) - lambda / lipschitz);
                shrink = truncated(truncated > T(0.0));
                shrink = sign(WX) * shrink;
                shrink(truncated < T(0.0)) = T(0.0);

                scimath::saveAsCasaImage("shrink.tab", WX);
                // Transform back from other (e.g. wavelet) space here

                this->WT(X_temp, shrink);
                scimath::saveAsCasaImage("WT.tab", X_temp);

                t_new = (T(1.0) + sqrt(T(1.0) + T(4.0) * square(t_old))) / T(2.0);
                X = X_temp + ((t_old - T(1.0)) / t_new) * (X_temp - X_old);
                {
                    casa::IPosition minPos;
                    casa::IPosition maxPos;
                    T minVal(0.0), maxVal(0.0);
                    if (isMasked) {
                        casa::minMaxMasked(minVal, maxVal, minPos, maxPos,
                                           this->dirty(),
                                           this->itsWeight(0));
                    } else {
                        casa::minMax(minVal, maxVal, minPos, maxPos, this->dirty());
                    }

                    ASKAPLOG_INFO_STR(decfistalogger, "   Maximum = " << maxVal << " at location " << maxPos);
                    ASKAPLOG_INFO_STR(decfistalogger, "   Minimum = " << minVal << " at location " << minPos);
                    if (abs(minVal) < abs(maxVal)) {
                        absPeakVal = abs(maxVal);
                    } else {
                        absPeakVal = abs(minVal);
                    }
                }

                T l1Norm = sum(abs(X_temp));
                T fit = casa::sum(this->dirty() * this->dirty());
                T objectiveFunction(fit + lambda*l1Norm);
                this->state()->setPeakResidual(absPeakVal);
                this->state()->setObjectiveFunction(objectiveFunction);
                this->state()->setTotalFlux(sum(X_temp));

                if (absPeakVal < lambda) {
                    lambda *= 1.0 - this->control()->gain();
                    ASKAPLOG_INFO_STR(decfistalogger, "Setting Lagrange multiplier lambda = " << lambda);
                }
                this->monitor()->monitor(*(this->state()));
                this->state()->incIter();
            } while (!this->control()->terminate(*(this->state())));
            this->model() = X_temp.copy();
            this->updateResiduals(this->model());

            ASKAPLOG_INFO_STR(decfistalogger, "Performed Fista for " << this->state()->currentIter() << " iterations");

            ASKAPLOG_INFO_STR(decfistalogger, this->control()->terminationString());

            this->finalise();

            absPeakVal = casa::max(casa::abs(this->dirty()));

            this->state()->setPeakResidual(absPeakVal);
            this->state()->setObjectiveFunction(absPeakVal);

            return True;
        }

        template<class T, class FT>
        void DeconvolverFista<T, FT>::setBasisFunction(boost::shared_ptr<BasisFunction<T> > bf)
        {
            itsBasisFunction = bf;
        };

        template<class T, class FT>
        boost::shared_ptr<BasisFunction<T> > DeconvolverFista<T, FT>::basisFunction()
        {
            return itsBasisFunction;
        };

        // Apply the convolve operation - this is undecimated and redundant. The 2D image
        // will be expanded along the third axis. Since the basis functions are normalised
        // to unit volume, the values here should be roughly maintained.
        template <class T, class FT>
        void DeconvolverFista<T, FT>::W(Array<T>& out, const Array<T>& in)
        {
            if (itsBasisFunction) {
                casa::Array<FT> inTransform(in.nonDegenerate().shape());
                casa::Array<FT> outPlaneTransform(in.nonDegenerate().shape());
                out.resize(itsBasisFunction->basisFunction().shape());
                casa::Cube<T> outCube(out);
                casa::setReal(inTransform, in.nonDegenerate());
                scimath::fft2d(inTransform, true);
                const uInt nPlanes(itsBasisFunction->basisFunction().shape()(2));
                for (uInt plane = 0; plane < nPlanes; plane++) {
                    outPlaneTransform = inTransform.nonDegenerate() * Cube<FT>(itsBasisFunctionTransform).xyPlane(plane);
                    scimath::fft2d(outPlaneTransform, false);
                    outCube.xyPlane(plane) = real(outPlaneTransform);
                }
            } else {
                out = in.copy();
            }
        }

        // Apply the transpose of the W operation - this is undecimated and redundant so we need
        // to sum over the planes
        template <class T, class FT>
        void DeconvolverFista<T, FT>::WT(Array<T>& out, const Array<T>& in)
        {
            if (itsBasisFunction) {
                const Cube<T> inCube(in);
                casa::Array<FT> inPlaneTransform(out.nonDegenerate().shape());
                casa::Array<FT> outTransform(out.nonDegenerate().shape());
                outTransform.set(FT(0.0));

                // To reconstruct, we filter out each basis from the cumulative sum
                // and then add the corresponding term from the in array.
                const uInt nPlanes(itsBasisFunction->basisFunction().shape()(2));

                casa::setReal(inPlaneTransform, inCube.xyPlane(nPlanes - 1));
                scimath::fft2d(inPlaneTransform, true);
                outTransform = Cube<FT>(itsBasisFunctionTransform).xyPlane(nPlanes - 1) * (inPlaneTransform);

                for (uInt plane = 1; plane < nPlanes; plane++) {
                    casa::setReal(inPlaneTransform, inCube.xyPlane(nPlanes - 1 - plane));
                    scimath::fft2d(inPlaneTransform, true);
                    outTransform = outTransform
                                   + Cube<FT>(itsBasisFunctionTransform).xyPlane(nPlanes - 1 - plane) * (inPlaneTransform - outTransform);
                }
                scimath::fft2d(outTransform, false);
                out.nonDegenerate() = real(outTransform);
            } else {
                out = in.copy();
            }
        }

    } // namespace synthesis

} // namespace askap


/**
 * Copyright (C) 2016 Turi
 * All rights reserved.
 *
 * This software may be modified and distributed under the terms
 * of the BSD license. See the LICENSE file for details.
 */
/**
 * Copyright (c) 2009 Carnegie Mellon University.
 *     All rights reserved.
 *
 *  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.
 *
 * For more about this software visit:
 *
 *      http://www.graphlab.ml.cmu.edu
 *
 */

/**
 * \file graph_ops.hpp
 *
 * This file supports basic graph io operations to simplify reading
 * and writing adjacency structures from files.
 *
 */

#ifndef GRAPHLAB_GRAPH_OPS_HPP
#define GRAPHLAB_GRAPH_OPS_HPP

#include <iostream>
#include <fstream>
#include <string>

#include <boost/algorithm/string/predicate.hpp>
#include <graph/distributed_graph.hpp>

#include <graphlab/macros_def.hpp>
namespace graphlab {

  namespace graph_ops {

    /**
     * builds a topological_sort of the graph returning it in topsort.
     *
     * \param[out] topsort Resultant topological sort of the graph vertices.
     *
     * function will return false if graph is not acyclic.
     */
    template <typename VertexType, typename EdgeType>
    bool topological_sort(const distributed_graph<VertexType, EdgeType>& graph,
                          std::vector<vertex_id_type>& topsort) {
      typedef distributed_graph<VertexType, EdgeType> graph_type;
      topsort.clear();
      topsort.reserve(graph.num_vertices());
      std::vector<size_t> indeg;
      indeg.resize(graph.num_vertices());
      std::queue<vertex_id_type> q;
      for (size_t i = 0;i < graph.num_vertices(); ++i) {
        indeg[i] = graph.get_in_edges(i).size();
        if (indeg[i] == 0) {
          q.push(i);
        }
      }

      while (!q.empty()) {
        vertex_id_type v = q.front();
        q.pop();
        topsort.push_back(v);
        foreach(typename graph_type::edge_type edge, graph.get_out_edges(v)) {
          vertex_id_type destv = edge.target();
          --indeg[destv];
          if (indeg[destv] == 0) {
            q.push(destv);
          }
        }
      }
      if (q.empty() && topsort.size() != graph.num_vertices()) {
        return false;
      }
      return true;
    } // end of topological sort

    template <typename VertexType, typename EdgeType>
    size_t num_neighbors(const distributed_graph<VertexType, EdgeType>& graph,
                         vertex_id_type& vid) {
      typedef distributed_graph<VertexType, EdgeType> graph_type;
      typename graph_type::edge_list_type in_edges =  graph.in_edges(vid);
      typename graph_type::edge_list_type out_edges = graph.out_edges(vid);
      typename graph_type::edge_list_type::const_iterator i = in_edges.begin();
      typename graph_type::edge_list_type::const_iterator j = out_edges.begin();
      size_t count = 0;
      for( ; i != in_edges.end() && j != out_edges.end(); ++count) {
        if(i->source() == j->target()) {
          ++i; ++j;
        } else if(i->source() < j->target()) {
          ++i;
        } else {
          ++j;
        }
      }
      for( ; i != in_edges.end(); ++i, ++count);
      for( ; j != out_edges.end(); ++j, ++count);
      return count;
    } // end of num_neighbors

    template <typename VertexType, typename EdgeType>
    void neighbors(const distributed_graph<VertexType, EdgeType>& graph,
                   const vertex_id_type vid,
                   std::vector<vertex_id_type>& neighbors ) {
      typedef distributed_graph<VertexType, EdgeType> graph_type;
      typename graph_type::edge_list_type in_edges =  graph.in_edges(vid);
      typename graph_type::edge_list_type out_edges = graph.out_edges(vid);
      typename graph_type::edge_list_type::const_iterator i = in_edges.begin();
      typename graph_type::edge_list_type::const_iterator j = out_edges.begin();
      while(i != in_edges.end() && j != out_edges.end()) {
        if(i->source() == j->target()) {
          neighbors.push_back(i->source());
          ++i; ++j;
        } else if(i->source() < j->target()) {
          neighbors.push_back(i->source());
          ++i;
        } else {
          neighbors.push_back(j->target());
          ++j;
        }
      }
      for( ; i != in_edges.end(); ++i) neighbors.push_back(i->source());
      for( ; j != out_edges.end(); ++j) neighbors.push_back(j->target());
    } // end of neighbors

    template <typename VertexType, typename EdgeType>
    bool save_metis_structure(const std::string& filename,
                              const distributed_graph<VertexType, EdgeType>& graph) {
      typedef distributed_graph<VertexType, EdgeType> graph_type;
      typedef typename graph_type::edge_type          edge_type;
      typedef typename graph_type::edge_list_type     edge_list_type;

      std::ofstream fout(filename.c_str());
      if(!fout.good()) return false;
      // Count the number of actual edges
      size_t nedges = 0;
      for(vertex_id_type i = 0; i < graph.num_vertices(); ++i)
        nedges += num_neighbors(graph, i);
      fout << graph.num_vertices() << ' ' << (nedges/2) << '\n';
      // Save the adjacency structure
      std::vector<vertex_id_type> neighbor_set;
      for(vertex_id_type i = 0; i < graph.num_vertices(); ++i) {
        neighbors(graph, i, neighbor_set);
        for(size_t j = 0; j < neighbor_set.size(); ++j) {
          fout << (neighbor_set[j] + 1);
          if(j + 1 < neighbor_set.size()) fout << ' ';
        }
        fout << '\n';
      }
      fout.close();
      return true;
    } // end of save metis

    template <typename VertexType, typename EdgeType>
    bool save_edge_list_structure(const std::string& filename,
                                  const distributed_graph<VertexType, EdgeType>& graph) {
      typedef distributed_graph<VertexType, EdgeType> graph_type;
      typedef typename graph_type::edge_type          edge_type;
      typedef typename graph_type::edge_list_type     edge_list_type;

      std::ofstream fout(filename.c_str());
      if(!fout.good()) return false;
      for(vertex_id_type i = 0; i < graph.num_vertices(); ++i)
        foreach(edge_type edge, graph.out_edges(i))
          fout << edge.source() << '\t' << edge.target() << '\n';
      fout.close();
      return true;
    } // end of save metis

    template <typename VertexType, typename EdgeType>
    bool save_zoltan_hypergraph_structure(const std::string& filename,
                                          const distributed_graph<VertexType, EdgeType>& graph) {
      typedef distributed_graph<VertexType, EdgeType> graph_type;
      typedef typename graph_type::edge_type          edge_type;
      typedef typename graph_type::edge_list_type     edge_list_type;

      std::ofstream fout(filename.c_str());
      if(!fout.good()) return false;

      // ok. I need to uniquely number each edge.
      // how?
      boost::unordered_map<std::pair<vertex_id_type,
        vertex_id_type>, size_t> edgetoid;
      size_t curid = 0;
      for(vertex_id_type i = 0; i < graph.num_vertices(); ++i) {
        foreach(const typename graph_type::edge_type& edge, graph.in_edges(i)) {
          std::pair<vertex_id_type, vertex_id_type> e =
            std::make_pair(edge.source(), edge.target());
          if (e.first > e.second) std::swap(e.first, e.second);
          if (edgetoid.find(e) == edgetoid.end()) {
            edgetoid[e] = curid;
            ++curid;
          }
        }
        foreach(const typename graph_type::edge_type& edge, graph.out_edges(i)) {
          std::pair<vertex_id_type, vertex_id_type> e =
            std::make_pair(edge.source(), edge.target());
          if (e.first > e.second) std::swap(e.first, e.second);
          if (edgetoid.find(e) == edgetoid.end()) {
            edgetoid[e] = curid;
            ++curid;
          }
        }
      }

      size_t numedges = curid;
      // each edge is a vertex, each vertex is an edge
      // a pin is total adjacency of a hyper edge
      fout << numedges << "\n\n";
      for (size_t i = 0;i < numedges; ++i) {
        fout << i+1 << "\n";
      }
      fout << "\n";
      fout << graph.num_vertices() << "\n\n";

      fout << numedges * 2 << "\n\n";
      // loop over the "hyperedge" and write out the edges it is adjacent to
      for(vertex_id_type i = 0; i < graph.num_vertices(); ++i) {
        boost::unordered_set<size_t> adjedges;
        foreach(const typename graph_type::edge_type& edge, graph.in_edges(i)) {
          std::pair<vertex_id_type, vertex_id_type> e =
            std::make_pair(edge.source(), edge.target());
          if (e.first > e.second) std::swap(e.first, e.second);
          adjedges.insert(edgetoid[e]);
        }
        foreach(const typename graph_type::edge_type& edge, graph.out_edges(i)) {
          std::pair<vertex_id_type, vertex_id_type> e =
            std::make_pair(edge.source(), edge.target());
          if (e.first > e.second) std::swap(e.first, e.second);
          adjedges.insert(edgetoid[e]);
        }
        // write
        std::vector<size_t> adjedgesvec;
        std::copy(adjedges.begin(), adjedges.end(),
                  std::inserter(adjedgesvec, adjedgesvec.end()));
        fout << i+1 << " " << adjedgesvec.size() << "\t";
        for (size_t j = 0;j < adjedgesvec.size(); ++j) {
          fout << adjedgesvec[j] + 1;
          if (j < adjedgesvec.size() - 1) fout << "\t";
        }
        fout << "\n";
      }
      fout.close();
      return true;
    }  // end of save_zoltan_hypergraph_structure

  }; // end of graph ops
}; // end of namespace graphlab
#include <graphlab/macros_undef.hpp>

#endif


//////////////////////////////////////////////////////////////////////////////////////
// This file is distributed under the University of Illinois/NCSA Open Source License.
// See LICENSE file in top directory for details.
//
// Copyright (c) 2016 Jeongnim Kim and QMCPACK developers.
//
// File developed by: Miguel Morales, moralessilva2@llnl.gov, Lawrence Livermore National Laboratory
//                    Jeremy McMinnis, jmcminis@gmail.com, University of Illinois at Urbana-Champaign
//                    Cynthia Gu, zg1@ornl.gov, Oak Ridge National Laboratory
//                    Jeongnim Kim, jeongnim.kim@gmail.com, University of Illinois at Urbana-Champaign
//                    Mark A. Berrill, berrillma@ornl.gov, Oak Ridge National Laboratory
//
// File created by: Jeongnim Kim, jeongnim.kim@gmail.com, University of Illinois at Urbana-Champaign
//////////////////////////////////////////////////////////////////////////////////////

#include <Configuration.h>
#include "Message/OpenMP.h"
#include "OhmmsData/AttributeSet.h"
#include "RandomNumberControl.h"
#include "Utilities/RandomGeneratorIO.h"
#include "Utilities/Timer.h"
#include "hdf/HDFVersion.h"
#include "hdf/hdf_archive.h"
#include "mpi/collectives.h"
#if defined(HAVE_LIBBOOST)
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <boost/foreach.hpp>
#include <string>
#include <set>
#include <exception>
#include <iostream>
#endif
#include "Utilities/SimpleParser.h"
#include "OhmmsData/Libxml2Doc.h"

namespace qmcplusplus
{
///initialize the static data members
PrimeNumberSet<RandomGenerator_t::uint_type> RandomNumberControl::PrimeNumbers;
std::vector<RandomGenerator_t*> RandomNumberControl::Children;
RandomGenerator_t::uint_type RandomNumberControl::Offset = 11u;

/// constructors and destructors
RandomNumberControl::RandomNumberControl(const char* aname)
    : OhmmsElementBase(aname), NeverBeenInitialized(true), myCur(NULL) //, Offset(5)
{}

/// generic output
bool RandomNumberControl::get(std::ostream& os) const
{
  if (omp_get_max_threads() > 1)
  {
    for (int ip = 0; ip < omp_get_max_threads(); ip++)
    {
      Children[ip]->write(os);
      os << std::endl;
    }
  }
  else
  {
    Random.write(os);
  }
  return true;
}

/// generic input
bool RandomNumberControl::put(std::istream& is) { return true; }

/// reset the generator
void RandomNumberControl::reset() { make_seeds(); }

/// reset the generator
void RandomNumberControl::make_seeds()
{
  int pid         = OHMMS::Controller->rank();
  int nprocs      = OHMMS::Controller->size();
  uint_type iseed = static_cast<uint_type>(std::time(0)) % 1024;
  mpi::bcast(*OHMMS::Controller, iseed);
  //OHMMS::Controller->bcast(iseed);//broadcast the seed
  Offset = iseed;
  std::vector<uint_type> mySeeds;
  RandomNumberControl::PrimeNumbers.get(Offset, nprocs * (omp_get_max_threads() + 2), mySeeds);
  Random.init(pid, nprocs, mySeeds[pid], Offset + pid);
  //change children as well
  make_children();
}

void RandomNumberControl::make_children()
{
  int nthreads = omp_get_max_threads();
  int n        = nthreads - Children.size();
  while (n)
  {
    Children.push_back(new RandomGenerator_t);
    n--;
  }
  int rank       = OHMMS::Controller->rank();
  int nprocs     = OHMMS::Controller->size();
  int baseoffset = Offset + nprocs + nthreads * rank;
  std::vector<uint_type> myprimes;
  PrimeNumbers.get(baseoffset, nthreads, myprimes);
  for (int ip = 0; ip < nthreads; ip++)
  {
    int offset = baseoffset + ip;
    Children[ip]->init(rank, nprocs, myprimes[ip], offset);
  }
}

xmlNodePtr RandomNumberControl::initialize(xmlXPathContextPtr acontext)
{
  OhmmsXPathObject rg_request("//random", acontext);
  put(rg_request[0]);
  return myCur;
}

void RandomNumberControl::test()
{
  /* Add random number generator tester
  */
  int nthreads = omp_get_max_threads();
  std::vector<double> avg(nthreads), avg2(nthreads);
#pragma omp parallel for
  for (int ip = 0; ip < nthreads; ++ip)
  {
    const int n = 1000000;
    double sum = 0.0, sum2 = 0.0;
    RandomGenerator_t& myrand(*Children[ip]);
    for (int i = 0; i < n; ++i)
    {
      double r = myrand.rand();
      sum += r;
      sum2 += r * r;
    }
    avg[ip]  = sum / static_cast<double>(n);
    avg2[ip] = sum2 / static_cast<double>(n);
  }
  std::vector<double> avg_tot(nthreads * OHMMS::Controller->size()), avg2_tot(nthreads * OHMMS::Controller->size());
  mpi::gather(*OHMMS::Controller, avg, avg_tot);
  mpi::gather(*OHMMS::Controller, avg2, avg2_tot);
  double avg_g  = 0.0;
  double avg2_g = 0.0;
  for (int i = 0, ii = 0; i < OHMMS::Controller->size(); ++i)
  {
    for (int ip = 0; ip < nthreads; ++ip, ++ii)
    {
      app_log() << "RNGTest " << std::setw(4) << i << std::setw(4) << ip << std::setw(20) << avg_tot[ii]
                << std::setw(20) << avg2_tot[ii] - avg_tot[ii] * avg_tot[ii] << std::endl;
      avg_g += avg_tot[ii];
      avg2_g += avg2_tot[ii];
    }
  }
  avg_g /= static_cast<double>(nthreads * OHMMS::Controller->size());
  avg2_g /= static_cast<double>(nthreads * OHMMS::Controller->size());
  app_log() << "RNGTest " << std::setw(4) << OHMMS::Controller->size() << std::setw(4) << nthreads << std::setw(20)
            << avg_g << std::setw(20) << avg2_g - avg_g * avg_g << std::endl;
  app_log().flush();
}

bool RandomNumberControl::put(xmlNodePtr cur)
{
  if (NeverBeenInitialized)
  {
    bool init_mpi = true;
    int offset_in = -1; // default is to generate by Wall-clock
    if (cur != NULL)
    {
      std::string pname("yes");
      OhmmsAttributeSet oAttrib;
      oAttrib.add(pname, "parallel");
      oAttrib.add(offset_in, "seed");
      oAttrib.put(cur);
      if (pname == "0" || pname == "false" || pname == "no")
        init_mpi = false;
    }
    int nprocs = 1;
    int pid    = 0;
    if (init_mpi)
    {
      pid    = OHMMS::Controller->rank();
      nprocs = OHMMS::Controller->size();
    }

    app_summary() << std::endl;
    app_summary() << " Random Number" << std::endl;
    app_summary() << " -------------" << std::endl;
    if (offset_in < 0)
    {
      offset_in = static_cast<int>(static_cast<uint_type>(std::time(0)) % 1024);
      app_summary() << "  Offset for the random number seeds based on time: " << offset_in << std::endl;
      mpi::bcast(*OHMMS::Controller, offset_in);
    }
    else
    {
      offset_in %= 1024;
      app_summary() << "  Offset for the random number seeds from input file (mod 1024): " << offset_in << std::endl;
    }
    app_summary() << std::endl;
    Offset = offset_in;
    std::vector<uint_type> mySeeds;
    //allocate twice of what is required
    PrimeNumbers.get(Offset, nprocs * (omp_get_max_threads() + 2), mySeeds);
    Random.init(pid, nprocs, mySeeds[pid], Offset + pid);
    app_log() << "  Range of prime numbers to use as seeds over processors and threads = " << mySeeds[0] << "-"
              << mySeeds[nprocs * omp_get_max_threads()] << std::endl;
    app_log() << std::endl;

    make_children();
    NeverBeenInitialized = false;
  }
  else
    reset();
  return true;
}

void RandomNumberControl::read_old(const std::string& fname, Communicate* comm)
{
  int nthreads = omp_get_max_threads();
  std::vector<uint_type> vt_tot, vt;
  std::vector<int> shape(2, 0), shape_now(2, 0);
  shape_now[0] = comm->size() * nthreads;
  shape_now[1] = Random.state_size();

  if (comm->rank() == 0)
  {
#if defined(HAVE_LIBBOOST)
    using boost::property_tree::ptree;
    ptree pt;
    std::string xname = fname + ".random.xml";
    read_xml(xname, pt);
    if (!pt.empty())
    {
      std::string engname = pt.get<std::string>("random.engine");
      if (engname == Random.EngineName)
      {
        std::istringstream dims(pt.get<std::string>("random.dims"));
        dims >> shape[0] >> shape[1];
        if (shape[0] == shape_now[0] && shape[1] == shape_now[1])
        {
          vt_tot.resize(shape[0] * shape[1]);
          std::istringstream v(pt.get<std::string>("random.states"));
          for (int i = 0; i < vt_tot.size(); ++i)
            v >> vt_tot[i];
        }
        else
          shape[0] = shape[1] = 0;
      }
    }
#else
    TinyVector<hsize_t, 2> shape_t(0);
    shape_t[1] = Random.state_size();
    hyperslab_proxy<std::vector<uint_type>, 2> slab(vt_tot, shape_t);
    std::string h5name = fname + ".random.h5";
    hdf_archive hout(comm);
    hout.open(h5name, H5F_ACC_RDONLY);
    hout.push(hdf::main_state);
    hout.push("random");
    std::string engname;
    hout.read(slab, Random.EngineName);
    shape[0] = static_cast<int>(slab.size(0));
    shape[1] = static_cast<int>(slab.size(1));
#endif
  }

  mpi::bcast(*comm, shape);

  if (shape[0] != shape_now[0] || shape[1] != shape_now[1])
  {
    app_log() << "Mismatched random number generators."
              << "\n  Number of streams     : old=" << shape[0] << " new= " << comm->size() * nthreads
              << "\n  State size per stream : old=" << shape[1] << " new= " << Random.state_size()
              << "\n  Using the random streams generated at the initialization." << std::endl;
    return;
  }

  app_log() << "  Restart from the random number streams from the previous configuration." << std::endl;
  vt.resize(nthreads * Random.state_size());

  if (comm->size() > 1)
    mpi::scatter(*comm, vt_tot, vt);
  else
    copy(vt_tot.begin(), vt_tot.end(), vt.begin());

  {
    if (nthreads > 1)
    {
      std::vector<uint_type>::iterator vt_it(vt.begin());
      for (int ip = 0; ip < nthreads; ip++, vt_it += shape[1])
      {
        std::vector<uint_type> c(vt_it, vt_it + shape[1]);
        Children[ip]->load(c);
      }
    }
    else
      Random.load(vt);
  }
}

void RandomNumberControl::write_old(const std::string& fname, Communicate* comm)
{
  int nthreads = omp_get_max_threads();
  std::vector<uint_type> vt, vt_tot;
  vt.reserve(nthreads * 1024);
  if (nthreads > 1)
    for (int ip = 0; ip < nthreads; ++ip)
    {
      std::vector<uint_type> c;
      Children[ip]->save(c);
      vt.insert(vt.end(), c.begin(), c.end());
    }
  else
    Random.save(vt);
  if (comm->size() > 1)
  {
    vt_tot.resize(vt.size() * comm->size());
    mpi::gather(*comm, vt, vt_tot);
  }
  else
    vt_tot = vt;

  if (comm->rank() == 0)
  {
#if defined(HAVE_LIBBOOST)
    using boost::property_tree::ptree;
    ptree pt;
    std::ostringstream dims, vt_o;
    dims << comm->size() * nthreads << " " << Random.state_size();
    std::vector<uint_type>::iterator v = vt_tot.begin();
    for (int i = 0; i < comm->size() * nthreads; ++i)
    {
      copy(v, v + Random.state_size(), std::ostream_iterator<uint_type>(vt_o, " "));
      vt_o << std::endl;
      v += Random.state_size();
    }
    pt.put("random.engine", Random.EngineName);
    pt.put("random.dims", dims.str());
    pt.put("random.states", vt_o.str());
    std::string xname = fname + ".random.xml";
    write_xml(xname, pt);
#else
    std::string h5name = fname + ".random.h5";
    hdf_archive hout(comm);
    hout.create(h5name);
    hout.push(hdf::main_state);
    hout.push("random");
    TinyVector<hsize_t, 2> shape(comm->size() * nthreads, Random.state_size());
    hyperslab_proxy<std::vector<uint_type>, 2> slab(vt_tot, shape);
    hout.write(slab, Random.EngineName);
    hout.close();
#endif
  }
}

/*New functions past this point*/
//switch between read functions
void RandomNumberControl::read(const std::string& fname, Communicate* comm)
{
  std::string h5name = fname + ".random.h5";
  hdf_archive hin(comm, true); //attempt to read in parallel
  hin.open(h5name, H5F_ACC_RDONLY);
  if (hin.is_parallel())
    read_parallel(hin, comm);
  else
    read_rank_0(hin, comm);
}

//switch between write functions
void RandomNumberControl::write(const std::string& fname, Communicate* comm)
{
  std::string h5name = fname + ".random.h5";
  hdf_archive hout(comm, true); //attempt to write in parallel
  hout.create(h5name);
  if (hout.is_parallel())
    write_parallel(hout, comm);
  else
    write_rank_0(hout, comm);
}

//Parallel read
void RandomNumberControl::read_parallel(hdf_archive& hin, Communicate* comm)
{
  // cast integer to size_t
  const size_t nthreads = static_cast<size_t>(omp_get_max_threads());
  const size_t comm_size = static_cast<size_t>(comm->size());
  const size_t comm_rank = static_cast<size_t>(comm->rank());

  std::vector<uint_type> vt, mt;
  TinyVector<int, 3> shape_now(comm->size(), nthreads, Random.state_size()); //cur configuration
  TinyVector<int, 3> shape_hdf5(3, 0);                                       //configuration when file was written

  //grab shape and Random.state_size() used to create hdf5 file
  hin.push(hdf::main_state);
  hin.read(shape_hdf5, "nprocs_nthreads_statesize");

  //if hdf5 file's shape and the current shape don't match, abort read
  if (shape_hdf5[0] != shape_now[0] || shape_hdf5[1] != shape_now[1] || shape_hdf5[2] != shape_now[2])
  {
    app_log() << "Mismatched random number generators."
              << "\n  Number of procs in streams : old=" << shape_hdf5[0] << " new= " << shape_now[0]
              << "\n  Number of threads in streams : old=" << shape_hdf5[1] << " new= " << shape_now[1]
              << "\n  State size per stream : old=" << shape_hdf5[2] << " new= " << shape_now[2]
              << "\n  Using the random streams generated at the initialization.\n";
    return;
  }
  app_log() << "  Restart from the random number streams from the previous configuration.\n";

  vt.resize(nthreads * Random.state_size()); //buffer for children[ip]
  mt.resize(Random.state_size());            //buffer for single thread Random object of random nums

  std::array<size_t, 2> shape{comm_size * nthreads, Random.state_size()}; //global dims of children dataset
  std::array<size_t, 2> counts{nthreads, Random.state_size()};               //local dimensions of dataset
  std::array<size_t, 2> offsets{comm_rank * nthreads, 0};                 //offsets for each process to read in

  hin.push("random"); //group that holds children[ip] random nums
  hyperslab_proxy<std::vector<uint_type>, 2> slab(vt, shape, counts, offsets);
  hin.read(slab, Random.EngineName);

  hin.pop();
  hin.push("random_master"); //group that holds Random_th random nums
  shape[0]   = comm->size(); //reset shape, counts and offset for non-multiple threads
  counts[0]  = 1;
  offsets[0] = comm->rank();
  hyperslab_proxy<std::vector<uint_type>, 2> slab2(mt, shape, counts, offsets);
  hin.read(slab2, Random.EngineName);
  hin.close();

  std::vector<uint_type>::iterator vt_it(vt.begin());
  for (int ip = 0; ip < nthreads; ip++, vt_it += shape[1])
  {
    std::vector<uint_type> c(vt_it, vt_it + shape[1]);
    Children[ip]->load(c); //load random nums back to program from buffer
  }
  Random.load(mt); //load random nums back to prog from buffer
}

//Parallel write
void RandomNumberControl::write_parallel(hdf_archive& hout, Communicate* comm)
{
  // cast integer to size_t
  const size_t nthreads = static_cast<size_t>(omp_get_max_threads());
  const size_t comm_size = static_cast<size_t>(comm->size());
  const size_t comm_rank = static_cast<size_t>(comm->rank());

  std::vector<uint_type> vt, mt;
  TinyVector<int, 3> shape_hdf5(comm->size(), nthreads, Random.state_size()); //configuration at write time
  vt.reserve(nthreads * Random.state_size()); //buffer for random numbers from children[ip] of each thread
  mt.reserve(Random.state_size());            //buffer for random numbers from single Random object

  for (int ip = 0; ip < nthreads; ++ip)
  {
    std::vector<uint_type> c;
    Children[ip]->save(c);
    vt.insert(vt.end(), c.begin(), c.end()); //get nums from each thread into buffer
  }
  Random.save(mt); //get nums for single random object (no threads)

  std::array<size_t, 2> shape{comm_size * nthreads, Random.state_size()}; //global dimensions
  std::array<size_t, 2> counts{nthreads, Random.state_size()};               //local dimensions
  std::array<size_t, 2> offsets{comm_rank * nthreads, 0};                 //offset for the file write

  hout.push(hdf::main_state);
  hout.write(shape_hdf5, "nprocs_nthreads_statesize"); //save the shape of the data at write

  hout.push("random"); //group for children[ip]
  hyperslab_proxy<std::vector<uint_type>, 2> slab(vt, shape, counts, offsets);
  hout.write(slab, Random.EngineName); //write to hdf5file
  hout.pop();

  shape[0]   = comm->size(); //adjust shape, counts, offset for just one thread
  counts[0]  = 1;
  offsets[0] = comm->rank();
  hout.push("random_master"); //group for random object without threads
  hyperslab_proxy<std::vector<uint_type>, 2> slab2(mt, shape, counts, offsets);
  hout.write(slab2, Random.EngineName); //write data to hdf5 file
  hout.close();
}

//Scatter read
void RandomNumberControl::read_rank_0(hdf_archive& hin, Communicate* comm)
{
  // cast integer to size_t
  const size_t nthreads = static_cast<size_t>(omp_get_max_threads());
  const size_t comm_size = static_cast<size_t>(comm->size());
  const size_t comm_rank = static_cast<size_t>(comm->rank());

  std::vector<uint_type> vt, vt_tot, mt, mt_tot;
  TinyVector<size_t, 3> shape_now(comm_size, nthreads, Random.state_size()); //current configuration
  TinyVector<size_t, 3> shape_hdf5;                                             //configuration when hdf5 file was written
  std::array<size_t, 2> shape{comm_size * nthreads, Random.state_size()};    //dimensions of children dataset

  //grab configuration of threads/procs and Random.state_size() in hdf5 file
  if (comm->rank() == 0)
  {
    hin.push(hdf::main_state);
    hin.read(shape_hdf5, "nprocs_nthreads_statesize");
  }

  mpi::bcast(*comm, shape_hdf5);

  //if hdf5 file's configuration and current configuration don't match, abort read
  if (shape_hdf5[0] != shape_now[0] || shape_hdf5[1] != shape_now[1] || shape_hdf5[2] != shape_now[2])
  {
    app_log() << "Mismatched random number generators."
              << "\n  Number of procs in streams : old=" << shape_hdf5[0] << " new= " << shape_now[0]
              << "\n  Number of threads in streams : old=" << shape_hdf5[1] << " new= " << shape_now[1]
              << "\n  State size per stream : old=" << shape_hdf5[2] << " new= " << shape_now[2]
              << "\n  Using the random streams generated at the initialization.\n";
    return;
  }
  app_log() << "  Restart from the random number streams from the previous configuration.\n";

  vt.resize(nthreads * Random.state_size()); //buffer for random nums in children of each thread
  mt.resize(Random.state_size());            //buffer for random numbers from single Random object

  if (comm->rank() == 0)
  {
    hin.push("random"); //group for children[ip] (Random.object for each thread)
    vt_tot.resize(nthreads * Random.state_size() * comm->size());
    hin.readSlabReshaped(vt_tot, shape, Random.EngineName);
    hin.pop();

    shape[0] = comm->size(); //reset shape to one thread per process
    mt_tot.resize(Random.state_size() * comm->size());
    hin.push("random_master"); //group for single Random object
    hin.readSlabReshaped(mt_tot, shape, Random.EngineName);
    hin.close();
  }

  if (comm->size() > 1)
  {
    mpi::scatter(*comm, vt_tot, vt); //divide big buffer into on for each proc
    mpi::scatter(*comm, mt_tot, mt);
  }
  else
  {
    copy(vt_tot.begin(), vt_tot.end(), vt.begin());
    copy(mt_tot.begin(), mt_tot.end(), mt.begin());
  }

  std::vector<uint_type>::iterator vt_it(vt.begin());
  for (int i = 0; i < nthreads; i++, vt_it += shape[1])
  {
    std::vector<uint_type> c(vt_it, vt_it + shape[1]);
    Children[i]->load(c); //read seeds for each thread from buffer back into object
  }
  Random.load(mt); //read seeds back into object
}

//scatter write
void RandomNumberControl::write_rank_0(hdf_archive& hout, Communicate* comm)
{
  // cast integer to size_t
  const size_t nthreads = static_cast<size_t>(omp_get_max_threads());
  const size_t comm_size = static_cast<size_t>(comm->size());
  const size_t comm_rank = static_cast<size_t>(comm->rank());

  std::vector<uint_type> vt, vt_tot, mt, mt_tot;
  std::array<size_t, 2> shape{comm_size * nthreads, Random.state_size()};     //dimensions of children dataset
  TinyVector<size_t, 3> shape_hdf5(comm_size, nthreads, Random.state_size()); //configuration at write time
  vt.reserve(nthreads * Random.state_size()); //buffer for children[ip] (Random object of seeds for each thread)
  mt.reserve(Random.state_size()); //buffer for single Random object of seeds, one per proc regardless of thread num

  for (int i = 0; i < nthreads; ++i)
  {
    std::vector<uint_type> c;
    Children[i]->save(c);
    vt.insert(vt.end(), c.begin(), c.end()); //copy children[nthreads] seeds to buffer
  }
  Random.save(mt); //copy random_th seeds to buffer

  if (comm->size() > 1)
  {
    vt_tot.resize(vt.size() * comm->size());
    mt_tot.resize(mt.size() * comm->size());
    mpi::gather(*comm, vt, vt_tot); //gather into one big buffer for master write
    mpi::gather(*comm, mt, mt_tot);
  }
  else
  {
    vt_tot = vt;
    mt_tot = mt;
  }

  if (comm->rank() == 0)
  {
    hout.push(hdf::main_state);
    hout.write(shape_hdf5, "nprocs_nthreads_statesize"); //configuration at write time to file

    hout.push("random"); //group for children[ip]
    hout.writeSlabReshaped(vt_tot, shape, Random.EngineName);
    hout.pop();

    shape[0] = comm->size();    //reset dims for single thread use
    hout.push("random_master"); //group for random_th object
    hout.writeSlabReshaped(mt_tot, shape, Random.EngineName);
    hout.close();
  }
}
} // namespace qmcplusplus


#ifndef CFG_BOOST_GRAPH_TRAITS_HPP
#define CFG_BOOST_GRAPH_TRAITS_HPP

/*
 * Convert a CFG into a BGL (Boost Graph Library) graph
 */
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/properties.hpp>
#include <boost/iterator/transform_iterator.hpp>

#include <crab/cfg/cfg.hpp>

namespace crab {
  namespace cfg {
   namespace graph  {
        template <typename G>
        struct mk_in_edge:
            public std::unary_function<typename boost::graph_traits<G>::vertex_descriptor,
                                       typename boost::graph_traits<G>::edge_descriptor> {
          typedef typename boost::graph_traits<G>::vertex_descriptor Node;
          typedef typename boost::graph_traits<G>::edge_descriptor   Edge;

          Node _dst;
          mk_in_edge () {}
          mk_in_edge (const Node &dst) : _dst (dst) {}
          Edge operator() (const Node &src) const { return Edge (src, _dst); }
        };

        template <typename G>
        struct mk_out_edge:
         public std::unary_function<typename boost::graph_traits<G>::vertex_descriptor,
                                    typename boost::graph_traits<G>::edge_descriptor> {
          typedef typename boost::graph_traits<G>::vertex_descriptor Node;
          typedef typename boost::graph_traits<G>::edge_descriptor   Edge;

          Node _src;
          mk_out_edge () {}
          mk_out_edge (const Node &src) : _src (src) { }
          Edge operator() (const Node &dst) const { return Edge (_src, dst); }
        };
    }  // end namespace graph
  } // end namespace cfg
} // end namespace crab

namespace boost {

     // cfg
     template<typename BasicBlockLabel, typename VariableName, typename Number>
     struct graph_traits<crab::cfg::Cfg <BasicBlockLabel, VariableName, Number> >  {
       typedef crab::cfg::Cfg<BasicBlockLabel, VariableName, Number> graph_t;
       typedef BasicBlockLabel vertex_descriptor;
       typedef std::pair<vertex_descriptor, vertex_descriptor> edge_descriptor;
       typedef std::pair<const vertex_descriptor,
                    const vertex_descriptor> const_edge_descriptor;

       typedef disallow_parallel_edge_tag edge_parallel_category;
       typedef bidirectional_tag directed_category;
       struct  this_graph_tag : virtual bidirectional_graph_tag,
                                virtual vertex_list_graph_tag {};
       typedef this_graph_tag traversal_category;

       typedef size_t vertices_size_type;
       typedef size_t edges_size_type;
       typedef size_t degree_size_type;

       static vertex_descriptor null_vertex() {
	 if (std::is_pointer<vertex_descriptor>::value)
	   return nullptr;
	 else {
	   // XXX: if vertex_descriptor is a basic type then
	   // null_vertex will return an undefined value, otherwise it
	   // will return the result of calling the default
	   // constructor.
	   vertex_descriptor n;
	   return n;
	 }
       }

       // iterator of basic_block_label_t's
       typedef typename graph_t::label_iterator vertex_iterator;
       // iterator of pairs of basic_block_label_t's
       typedef transform_iterator<crab::cfg::graph::mk_in_edge<graph_t>,
				  typename graph_t::pred_iterator> in_edge_iterator;
       // iterator of pairs of basic_block_label_t's
       typedef transform_iterator<crab::cfg::graph::mk_out_edge<graph_t>,
				  typename graph_t::succ_iterator> out_edge_iterator;
     }; // end class graph_traits

     // cfg_ref
     template<class CFG>
     struct graph_traits<crab::cfg::cfg_ref<CFG> >  {
       typedef crab::cfg::cfg_ref<CFG> graph_t;
       typedef typename graph_t::basic_block_label_t vertex_descriptor;
       typedef std::pair<vertex_descriptor, vertex_descriptor> edge_descriptor;
       typedef std::pair<const vertex_descriptor,
			 const vertex_descriptor> const_edge_descriptor;

       typedef disallow_parallel_edge_tag edge_parallel_category;
       typedef bidirectional_tag directed_category;
       struct  this_graph_tag : virtual bidirectional_graph_tag,
                                virtual vertex_list_graph_tag {};
       typedef this_graph_tag traversal_category;

       typedef size_t vertices_size_type;
       typedef size_t edges_size_type;
       typedef size_t degree_size_type;

       static vertex_descriptor null_vertex() {
	 if (std::is_pointer<vertex_descriptor>::value)
	   return nullptr;
	 else {
	   // XXX: if vertex_descriptor is a basic type then
	   // null_vertex will return an undefined value, otherwise it
	   // will return the result of calling the default
	   // constructor.
	   vertex_descriptor n;
	   return n;
	 }
       }

       typedef typename graph_t::label_iterator vertex_iterator;
       typedef boost::transform_iterator<crab::cfg::graph::mk_in_edge<graph_t>,
                                         typename graph_t::pred_iterator> in_edge_iterator;
       typedef boost::transform_iterator<crab::cfg::graph::mk_out_edge<graph_t>,
                                         typename graph_t::succ_iterator> out_edge_iterator;
     }; // end class graph_traits

     // cfg_rev
     template<class CFG>
     struct graph_traits<crab::cfg::cfg_rev<CFG> >  {
       typedef crab::cfg::cfg_rev<CFG> graph_t;
       typedef typename graph_t::basic_block_label_t vertex_descriptor;
       typedef std::pair<vertex_descriptor, vertex_descriptor> edge_descriptor;
       typedef std::pair<const vertex_descriptor,
			 const vertex_descriptor> const_edge_descriptor;

       typedef disallow_parallel_edge_tag edge_parallel_category;
       typedef bidirectional_tag directed_category;
       struct  this_graph_tag : virtual bidirectional_graph_tag,
                                virtual vertex_list_graph_tag {};
       typedef this_graph_tag traversal_category;

       typedef size_t vertices_size_type;
       typedef size_t edges_size_type;
       typedef size_t degree_size_type;

       static vertex_descriptor null_vertex() {
	 if (std::is_pointer<vertex_descriptor>::value)
	   return nullptr;
	 else {
	   // XXX: if vertex_descriptor is a basic type then
	   // null_vertex will return an undefined value, otherwise it
	   // will return the result of calling the default
	   // constructor.
	   vertex_descriptor n;
	   return n;
	 }
       }

       typedef typename graph_t::label_iterator vertex_iterator;
       typedef boost::transform_iterator<crab::cfg::graph::mk_in_edge<graph_t>,
                                         typename graph_t::pred_iterator> in_edge_iterator;
       typedef boost::transform_iterator<crab::cfg::graph::mk_out_edge<graph_t>,
                                         typename graph_t::succ_iterator> out_edge_iterator;
     }; // end class graph_traits

} // end namespace

// XXX: do not put it in the boost namespace because it won't compile
namespace crab {
   namespace cfg {

     // cfg
     template<typename BasicBlockLabel, typename VariableName, typename Number>
     inline std::pair<typename
		 boost::graph_traits<Cfg<BasicBlockLabel,VariableName,Number> >::
		 vertex_iterator,
                 typename
		 boost::graph_traits<Cfg<BasicBlockLabel,VariableName,Number> >::
		 vertex_iterator >
     vertices (Cfg<BasicBlockLabel,VariableName,Number> g) {
       return std::make_pair (g.label_begin (), g.label_end ());
     }

     template<typename BasicBlockLabel, typename VariableName, typename Number>
     inline std::pair< typename
		  boost::graph_traits<Cfg<BasicBlockLabel,VariableName,Number> >::
		  out_edge_iterator,
                  typename
		  boost::graph_traits<Cfg<BasicBlockLabel,VariableName,Number> >::
		  out_edge_iterator >
     out_edges (typename
		boost::graph_traits<Cfg<BasicBlockLabel,VariableName,Number> >::
		vertex_descriptor v,
                Cfg<BasicBlockLabel,VariableName,Number> g) {
       typedef Cfg<BasicBlockLabel,VariableName,Number> G;

       auto& node = g.get_node (v);
       auto p = node.next_blocks ();
       return std::make_pair (boost::make_transform_iterator
			      (p.first, graph::mk_out_edge<G> (v)),
                              boost::make_transform_iterator
			      (p.second, graph::mk_out_edge<G> (v)));

     }

     template<typename BasicBlockLabel, typename VariableName, typename Number>
     inline std::pair< typename
		  boost::graph_traits<Cfg<BasicBlockLabel,VariableName,Number> >::
		  in_edge_iterator,
                  typename
		  boost::graph_traits<Cfg<BasicBlockLabel,VariableName,Number> >::
		  in_edge_iterator >
     in_edges (typename
	       boost::graph_traits<Cfg<BasicBlockLabel,VariableName,Number> >::
	       vertex_descriptor v,
               Cfg<BasicBlockLabel,VariableName,Number> g) {
       typedef Cfg<BasicBlockLabel,VariableName,Number> G;

       auto& node = g.get_node (v);
       auto p = node.prev_blocks ();
       return std::make_pair (boost::make_transform_iterator
			      (p.first,
			       graph::mk_in_edge<G> (v)),
                              boost::make_transform_iterator
			      (p.second,
			       graph::mk_in_edge<G> (v)));
     }

     template<typename BasicBlockLabel, typename VariableName, typename Number>
     typename boost::graph_traits<Cfg<BasicBlockLabel,VariableName, Number> >::
     vertices_size_type
     num_vertices (Cfg<BasicBlockLabel,VariableName, Number> g) {
       return std::distance (g.label_begin (), g.label_end ());
     }

     template<typename BasicBlockLabel, typename VariableName, typename Number>
     typename boost::graph_traits<Cfg<BasicBlockLabel,VariableName,Number> >::
     degree_size_type
     in_degree (typename
		boost::graph_traits<Cfg<BasicBlockLabel,VariableName,Number> >::
		vertex_descriptor v,
                Cfg<BasicBlockLabel,VariableName,Number> g) {
       auto preds = g.prev_nodes (v);
       return std::distance (preds.begin (), preds.end ());
     }

     template<typename BasicBlockLabel, typename VariableName, typename Number>
     typename boost::graph_traits<Cfg<BasicBlockLabel,VariableName,Number> >::
     degree_size_type
     out_degree (typename
		 boost::graph_traits<Cfg<BasicBlockLabel,VariableName,Number> >::
		 vertex_descriptor v,
                 Cfg<BasicBlockLabel,VariableName,Number> g) {
       auto succs = g.next_nodes (v);
       return std::distance (succs.begin (), succs.end ());
     }

     template<typename BasicBlockLabel, typename VariableName, typename Number>
     typename boost::graph_traits<Cfg<BasicBlockLabel,VariableName,Number> >::
     degree_size_type
     degree (typename
	     boost::graph_traits<Cfg<BasicBlockLabel,VariableName,Number> >::
	     vertex_descriptor v,
             Cfg<BasicBlockLabel,VariableName,Number> g) {
       return out_degree (v, g) + in_degree (v, g);
     }

     // cfg_ref

     template<class CFG>
     inline std::pair<typename boost::graph_traits<cfg_ref<CFG> >::vertex_iterator,
		      typename boost::graph_traits<cfg_ref<CFG> >::vertex_iterator>
     vertices (cfg_ref<CFG> g) {
       return std::make_pair (g.label_begin (), g.label_end ());
     }

     template<class CFG>
     inline std::pair<typename boost::graph_traits<cfg_ref<CFG> >::out_edge_iterator,
		      typename boost::graph_traits<cfg_ref<CFG> >::out_edge_iterator>
     out_edges (typename boost::graph_traits<cfg_ref<CFG> >::vertex_descriptor v,
                cfg_ref<CFG> g) {
       typedef cfg_ref<CFG> G;
       auto &node = g.get_node (v);
       auto p = node.next_blocks ();
       return std::make_pair (boost::make_transform_iterator
			      (p.first, graph::mk_out_edge<G> (v)),
                              boost::make_transform_iterator
			      (p.second, graph::mk_out_edge<G> (v)));

     }

     template<class CFG>
     inline std::pair<typename boost::graph_traits<cfg_ref<CFG> >::in_edge_iterator,
		      typename boost::graph_traits<cfg_ref<CFG> >::in_edge_iterator>
     in_edges (typename boost::graph_traits<cfg_ref<CFG> >::vertex_descriptor v,
               cfg_ref<CFG> g) {
       typedef cfg_ref<CFG> G;
       auto &node = g.get_node (v);
       auto p = node.prev_blocks ();
       return std::make_pair (boost::make_transform_iterator
			      (p.first, graph::mk_in_edge<G> (v)),
                              boost::make_transform_iterator
			      (p.second, graph::mk_in_edge<G> (v)));
     }

     template<class CFG>
     typename boost::graph_traits<cfg_ref<CFG> >::vertices_size_type
     num_vertices (cfg_ref<CFG> g) {
       return std::distance (g.label_begin (), g.label_end ());
     }

     template<class CFG>
     typename boost::graph_traits<cfg_ref<CFG> >::degree_size_type
     in_degree (typename boost::graph_traits<cfg_ref<CFG> >::vertex_descriptor v,
                cfg_ref<CFG> g) {
       auto preds = g.prev_nodes (v);
       return std::distance (preds.begin (), preds.end ());
     }

     template<class CFG>
     typename boost::graph_traits<cfg_ref<CFG> >::degree_size_type
     out_degree (typename boost::graph_traits<cfg_ref<CFG> >::vertex_descriptor v,
                 cfg_ref<CFG> g) {
       auto succs = g.next_nodes (v);
       return std::distance (succs.begin (), succs.end ());
     }

     template<class CFG>
     typename boost::graph_traits<cfg_ref<CFG> >::degree_size_type
     degree (typename boost::graph_traits<cfg_ref<CFG> >::vertex_descriptor v,
             cfg_ref<CFG> g) {
       return out_degree (v, g) + in_degree (v, g);
     }

     // cfg_rev

     template<class CFG>
     inline std::pair<typename boost::graph_traits<cfg_rev<CFG> >::vertex_iterator,
		      typename boost::graph_traits<cfg_rev<CFG> >::vertex_iterator>
     vertices (cfg_rev<CFG> g) {
       return std::make_pair (g.label_begin (), g.label_end ());
     }

     template<class CFG>
     inline std::pair<typename boost::graph_traits<cfg_rev<CFG> >::out_edge_iterator,
		      typename boost::graph_traits<cfg_rev<CFG> >::out_edge_iterator>
     out_edges (typename boost::graph_traits<cfg_rev<CFG> >::vertex_descriptor v,
                cfg_rev<CFG> g) {
       typedef cfg_rev<CFG> G;
       auto &node = g.get_node (v);
       auto p = node.next_blocks ();
       return std::make_pair (boost::make_transform_iterator
			      (p.first, graph::mk_out_edge<G> (v)),
                              boost::make_transform_iterator
			      (p.second,
			       graph::mk_out_edge<G> (v)));
     }

     template<class CFG>
     inline std::pair<typename boost::graph_traits<cfg_rev<CFG> >::in_edge_iterator,
		      typename boost::graph_traits<cfg_rev<CFG> >::in_edge_iterator>
     in_edges (typename boost::graph_traits<cfg_rev<CFG> >::vertex_descriptor v,
               cfg_rev<CFG> g) {
       typedef cfg_rev<CFG> G;
       auto &node = g.get_node (v);
       auto p = node.prev_blocks ();
       return std::make_pair (boost::make_transform_iterator
			      (p.first, graph::mk_in_edge<G> (v)),
                              boost::make_transform_iterator
			      (p.second, graph::mk_in_edge<G> (v)));
     }

     template<class CFG>
     typename boost::graph_traits<cfg_rev<CFG> >::vertices_size_type
     num_vertices (cfg_rev<CFG> g) {
       return std::distance (g.label_begin (), g.label_end ());
     }

     template<class CFG>
     typename boost::graph_traits<cfg_rev<CFG> >::degree_size_type
     in_degree (typename boost::graph_traits<cfg_rev<CFG> >::vertex_descriptor v,
                cfg_rev<CFG> g) {
       auto preds = g.prev_nodes (v);
       return std::distance (preds.begin (), preds.end ());
     }

     template<class CFG>
     typename boost::graph_traits<cfg_rev<CFG> >::degree_size_type
     out_degree (typename boost::graph_traits<cfg_rev<CFG> >::vertex_descriptor v,
                 cfg_rev<CFG> g) {
       auto succs = g.next_nodes (v);
       return std::distance (succs.begin (), succs.end ());
     }

     template<class CFG>
     typename boost::graph_traits<cfg_rev<CFG> >::degree_size_type
     degree (typename boost::graph_traits<cfg_rev<CFG> >::vertex_descriptor v,
             cfg_rev<CFG> g) {
       return out_degree (v, g) + in_degree (v, g);
     }

 } // namespace cfg
} // namespace crab

#endif


/*  This file is part of libDAI - http://www.libdai.org/
 *
 *  libDAI is licensed under the terms of the GNU General Public License version
 *  2, or (at your option) any later version. libDAI is distributed without any
 *  warranty. See the file COPYING for more details.
 *
 *  Copyright (C) 2010  Joris Mooij  [joris dot mooij at libdai dot org]
 */

#include <dai/alldai.h>
#include <iostream>
#include <fstream>
#include <string>
#include "dai/emrun.h"
#include <dai/util.h>
#include <string>
#include <sys/stat.h>
#include <time.h>

#include <iomanip>      // std::setprecision
#include <sstream>
#include <string>
#include <cstdlib>
#include <boost/lexical_cast.hpp>
using namespace std;
using namespace dai;

// GA - initialization
size_t max_gen = 25; // no. of generations
size_t population_size = 20; // population size
Real pc;// = 0.8; // crossover probability - between 0.6 an 0.9
Real pm;// = 0.3; // mutation probability - between 1/pop.size and 1/chromosome_length
int population_no=0; // current population id

// 1. Creating initial population of EM runs

char* gibbsfile;// = "/home/priya/libDAI-0.3.1/alarm/500/5.tab";
//const char* gibbsfile = argv[1];
string alarmfgloc;// = "/home/priya/libDAI-0.3.1/alarm_learned_factor_graphs/";
string alarmlearnedfgloc;// = "/home/priya/libDAI-0.3.1/alarm_learned_factor_graphs/";
const char* networkname = "carstarts";
size_t maxIters =1000; // maximum number of iterations

ofstream out;
EMRun* runname;
vector <EMRun*> layer;
vector <EMRun*> layer_finalval;
//
// Generate a random number between 0 and 1
// return a uniform number in [0,1].
double unifRand()
{
    return rand() / double(RAND_MAX);
}
// Reset the random number generator with the system clock.
void seed()
{
	/*Declare variable to hold seconds on clock.*/
//	time_t seconds;
	/*Get value from system clock and place in seconds variable.*/
//	time(&seconds);
	/*Convert seconds to a unsigned integer.*/
     struct timeval time;
     gettimeofday(&time,NULL);

     // microsecond has 1 000 000
     srand((time.tv_sec * 1000) + (time.tv_usec / 10));
}

std::string& trim(std::string& s, const char* t = " \t\n\r\f\v")
{
	s.erase(0, s.find_first_not_of(t));
	s.erase(s.find_last_not_of(t) + 1);
	return s;
}

void startEM()
{
	size_t i =0,j=0;
	while(i<population_size)
	{
	 string number;
	 stringstream num;
	 num << i;
	 number = num.str();
	 string dirname;
	 stringstream p_id;
	 p_id << population_no;
	 dirname = p_id.str();
	 string fgfileloc =  alarmfgloc + dirname+"/"+"carstarts_"+number+".fg";
	 const char* fgfile = &fgfileloc[0];

	 runname = new EMRun(networkname, i, fgfile, gibbsfile); // args are -1.network name 2.factor-graph number 3. evidencefile

	 // 2. Run EM
	 boost::thread threadid(boost::bind(&EMRun::startEM, boost::ref(runname), maxIters)); // args - max iters
	 layer.push_back(runname);
	 sleep(1);
	 // 4. Next individual
	 i=i+1;
	}

}

void checkEMforTermination()
{
         string dirname;
	 stringstream p_id;
	 p_id << population_no;
	 dirname = p_id.str();
	string loc =  alarmlearnedfgloc + "learned/"+dirname+"/";
	const char* learnedloc = &loc[0];
	mkdir(learnedloc,0755);
	while (true){
	    //cout<<"in while loop "<<layer.size()<<endl;
	    vector<EMRun*>::iterator it;
	    for (it=layer.begin(); it != layer.end(); ++it){
	      //cout<<"layer size "<<layer.size()<<endl;
	      if((*it)->hasTerminated()) { //if it has terminated, set the run aside
		double ll = (*it)->getLL();
		double iters = (*it)->getIters();
		double runid = (*it)->getRun();
		string number;
		stringstream num;
		num << runid;
		number = num.str();
	 	string fglearnedfileloc =  alarmlearnedfgloc + "learned/"+dirname+"/carstarts_"+number+".fg";
	    	const char* fglearnedfile = &fglearnedfileloc[0];
		//cout<<"Run number "<<number<<" ll = "<<ll<<" iters = "<<iters<<endl;
		// 3. Store the learned factor graphs
		InfAlg* inf = (*it)->getInf();
		ofstream learnedstream;
		//cout<<"learned file = "<<fglearnedfile<<endl;
		learnedstream.open(fglearnedfile);
		learnedstream.precision(12);
		learnedstream << inf->fg();
		learnedstream.flush();
		learnedstream.close();
		delete inf;
		sleep(1);
		//cout<<"Run number "<<number<<" has terminated"<<endl;
		out<<pc<<" "<<pm<<" "<<gibbsfile<<" "<<fglearnedfile<<" "<<runid<<" "<<iters<<" "<<ll<<" "<<endl;
		layer_finalval.push_back(*it);
		layer.erase(it);
		if (layer.size() == 0){break;}
		else { it = layer.begin(); }
	      }
	      else if(((*it)->getIters()) == maxIters){ //we stop the run
		double ll = (*it)->getLL();
		double iters = (*it)->getIters();
		double runid = (*it)->getRun();
		string number;
		stringstream num;
		num << runid;
		number = num.str();
	 	string fglearnedfileloc =  alarmlearnedfgloc + "learned/"+dirname+"/carstarts_"+number+".fg";
	    	const char* fglearnedfile = &fglearnedfileloc[0];
		//cout<<"Run number "<<number<<" ll = "<<ll<<" iters = "<<iters<<endl;
		//cout<<"Run "<<(*it)->getRun()<<" has reached maximum iterations, so stopping the run"<<endl;
		boost::thread thread5(boost::bind(&EMRun::stopEM, boost::ref(*it)));
	 	// 3. Store the learned factor graphs
		InfAlg *inf = (*it)->getInf();
	    	ofstream learnedstream;
	    	//cout<<"learned file = "<<fglearnedfile<<endl;
	    	learnedstream.open(fglearnedfile);
	    	learnedstream.precision(12);
	    	learnedstream << inf->fg();
	    	learnedstream.close();
		delete inf;
		sleep(1);
		//cout<<"Run number "<<number<<" has reached maximum iterations so stopped"<<endl;
		out<<pc<<" "<<pm<<" "<<gibbsfile<<" "<<fglearnedfile<<" "<<runid<<" "<<iters<<" "<<ll<<" "<<endl;
		layer_finalval.push_back(*it);
		layer.erase(it);
	//	if (layer.size() == 0){break;}
	//	else { it = layer.begin(); }
	      }
	      //cout<<"Going .."<<endl;
	 }
	 if (layer.size() == 0){ //cout<<"breaking while"<<endl;
		break; }
	 else { //cout<<"iterating"<<endl;
		}
	  sleep(1);
	}
}
std::string doMutation(string line)
{
	seed();
	std::vector<std::string> fields;
	fields = tokenizeString( line, true,"  ");
	string state_line, param_line;
	stringstream v;
	double val = unifRand();
	//  cout << fixed << setprecision(12) << val <<endl;
	v << fixed << setprecision(15) << val; // a random number is generated
	for( size_t i = 0; i < fields.size(); ++i ) {
	   stringstream n;
	   n << fields[i];
	   string s = n.str();
	   if( s.find_first_not_of(" ")!= std::string::npos){
	 //	//cout << " val - " <<s<<endl;
		if(i == 0)
		state_line =s; // this is the index
	  }
	}
	line = state_line+"   "+v.str(); // combining the index and the random value
	return line; // return the new cpt value
}
void doCrossoverandMutation()
{
	// compute crossover probability
	 seed();
	 double pcross = unifRand(); // generates random a value between 0 and 1
	//cout<<"Crossover probability "<<pcross<<endl;
	// 4. Select 2 parents randomly
	const int LOW = 0;
	const int HIGH = 19;
	//int t =5;
	int t=20;
	// Generate the random variables to select as parents where 't' is the number of variables
	/*Declare variable to hold seconds on clock.*/
	time_t seconds;
	/*Get value from system clock and place in seconds variable.*/
	time(&seconds);
	/*Convert seconds to a unsigned integer.*/
	srand(time(0));
	set<int> myset;
	set<int>::iterator it;
	size_t k =0,p=0;
	int *arrval = new int[t];
	string dirname;
	stringstream p_id;
	p_id << population_no;
	dirname = p_id.str();
	string dirname2;
	stringstream p_id2;
	p_id2 << population_no+1;
	dirname2 = p_id2.str();

	string childloc =  alarmlearnedfgloc + dirname2+"/";
	const char* childfgloc = &childloc[0];
	mkdir(childfgloc,0755);
	for( size_t n = 0; n < t; n++ )
	{
	   int val = rand() % (HIGH - LOW + 1) + LOW;
	   it=myset.find(val);
	   arrval[n] = val;
	   if(it==myset.end()) // if the value is not in the set
	   {
		myset.insert(val);
		p++;
		if(p == 2)
		{
		 //cout <<"Random value is"<<arrval[n]<<" and "<< arrval[n-1]<<endl;
		 string number1;
		 stringstream num1;
		 num1 << arrval[n];
		 number1 = num1.str();
		 string parent1 =  alarmlearnedfgloc +"learned/"+dirname+"/"+ "carstarts_"+number1+".fg";
		 string child1 =  childloc+ "carstarts_"+number1+".fg";
		 const char* parentfgfile1 = &parent1[0];
		 const char* childfgfile1 = &child1[0];
		 string number2;
		 stringstream num2;
		 num2 << arrval[n-1];
		 number2 = num2.str();
		 string parent2 = alarmlearnedfgloc +"learned/"+dirname+"/"+ "carstarts_"+number2+".fg";
		 string child2 =  childloc+ "carstarts_"+number2+".fg";
		 const char* parentfgfile2 = &parent2[0];
		 const char* childfgfile2 = &child2[0];
		 //cout<<"parent files are "<< parentfgfile1<< " and "<< parentfgfile2<<endl;
		 //cout<<"child files are "<< childfgfile1<<" and "<<childfgfile2<<endl;

		 string line1,line2;
		 ifstream p1 (parentfgfile1);  ifstream p2 (parentfgfile2);
		 ofstream c1 (childfgfile1);  ofstream c2 (childfgfile2);
		// 5. Do crossover and create two children
		// randomly select a crossover point
		 int cr_pt = (rand() % 17) +1;
		 int c = 0; // count of parameters
	 	 int p_c1 = 0, p_c2 =0; // counter to skip the first four config lines in the cpt of the fg file
		 //cout<<"Crossover point is "<< cr_pt<<endl;
		 double pmut1=0, pmut2 =0;
		 if (p1.is_open() && p2.is_open())
		 {
			 getline(p1,line1); getline(p2,line2);
			 c1<< line1<<endl;
			 c2<< line2<<endl;

			 while(getline(p1,line1) && getline(p2,line2))
			 {
				////cout<<line1 << " "<< line2 <<endl;
				line1 = trim(line1); line2 = trim(line2);
				if(line1.empty())
				{
				 c++; p_c1=0; p_c2 = 0;
				 seed();
			 	 c1<< line1<<endl;
				 c2<< line2<<endl;
				 //cout<<"empty line"<<endl;
				}
				else
				{
				 p_c1++; p_c2++;
				 //cout<<"Line number: "<<p_c1<<" "<<p_c2<<endl;
				 //cout<<"Crossover point "<<cr_pt<<"our pt "<<c<<"pcross "<<pcross<<"pc "<<pc<<endl;
				 if(c <= cr_pt || pcross >= pc) // checking crossover probability
				 {
					if(p_c1 >= 5)
				        {
						seed();
						pmut1 = unifRand(); // generates random a value between 0 and 1 for child1
						//cout<<"Before: "<<line1<<"\t"<<"pmut1 "<<pmut1<<"\t";
						if(pmut1 <= pm) // checking mutation probability for child1
						{
						 //cout<<"Mutation is done at "<<c<<"\t";
						 line1 = doMutation(line1);
						}
						//cout<<"After: "<<line1<<endl;
					}
					if(p_c2 >= 5)
				        {
						seed();
				 		pmut2 = unifRand(); // generates random a value between 0 and 1 for child2
						//cout<<"Before: "<<line2<<"\t"<<"pmut2 "<<pmut2<<"\t";
						if(pmut2 <= pm) // checking mutation probability for child1
						{
						 //cout<<"Mutation is done at "<<c<<"\t";
						 line2 = doMutation(line2);
						}
						//cout<<"After: "<<line2<<endl;
					}
			 		c1<< line1<<endl;
					c2<< line2<<endl;
				}
				else
				{
					//cout<<"crossover happening at "<<c<<endl;
					if(p_c1 >= 5)
				        {
						seed();
						pmut1 = unifRand(); // generates random a value between 0 and 1 for child1
						//cout<<"Before: "<<line1<<"\t"<<"pmut1 "<<pmut1<<"\t";
						if(pmut1 <= pm) // checking mutation probability for child1
						{
						 //cout<<"Mutation is done at "<<c<<"\t";
						 line1 = doMutation(line1);
						}
						//cout<<"After: "<<line1<<endl;
					}
					if(p_c2 >= 5)
				        {
						seed();
				 		pmut2 = unifRand(); // generates random a value between 0 and 1 for child2
						//cout<<"Before: "<<line2<<"\t"<<"pmut2 "<<pmut2<<"\t";
						if(pmut2 <= pm) // checking mutation probability for child1
						{
						 //cout<<"Mutation is done at "<<c<<"\t";
						 line2 = doMutation(line2);
						}
						//cout<<"After: "<<line2<<endl;
					}
					//if(pcross <= pc)
					{
					c2<< line1<<endl;
					c1<< line2<<endl;
					}
				}
				}
			 }
		 }
		 p1.close(); p2.close();
	 	 c1.close(); c2.close();
		 p=0;
		//break;
		}

	   }
	   else
	   {
		n=n-1;
		continue;
	   }
	}
}
int main(int argc, char *argv[]) {
const char* fname =argv[2];
gibbsfile = argv[1];
istringstream ss( argv[3] );
ss >> pm;
stringstream ss1( argv[4] );
ss1 >> pc;
stringstream ss2(argv[5]);
alarmfgloc = ss2.str();
alarmlearnedfgloc = ss2.str();

out.open(fname);
 clock_t tStart = clock();
while(population_no<max_gen)
{
	cout<<"population no"<<population_no<<endl;
	startEM();
	checkEMforTermination();
	doCrossoverandMutation();
	population_no=population_no+1;
}
double t = (clock() - tStart)/CLOCKS_PER_SEC;
cout<<"Time taken: "<<t<<"s"<<endl;
out<<"Time taken: "<<t<<"s"<<endl;
out.close();

return 0;
}