/*
 Copyright (C) 2020 Quaternion Risk Management Ltd
 All rights reserved.

 This file is part of ORE, a free-software/open-source library
 for transparent pricing and risk analysis - http://opensourcerisk.org

 ORE is free software: you can redistribute it and/or modify it
 under the terms of the Modified BSD License.  You should have received a
 copy of the license along with this program.
 The license is also available online at <http://opensourcerisk.org>

 This program is distributed on the basis that it will form a useful
 contribution to risk analytics and model standardisation, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
*/

/*! \file qle/termstructures/blackvolsurfacewithatm.hpp
    \brief Wrapper class for a BlackVolTermStructure that easily exposes ATM vols.
    \ingroup termstructures
*/

#ifndef quantext_blackvolsurfacewithatm_hpp
#define quantext_blackvolsurfacewithatm_hpp

#include <boost/shared_ptr.hpp>
#include <ql/termstructures/volatility/equityfx/blackvoltermstructure.hpp>
#include <ql/termstructures/yieldtermstructure.hpp>
#include <qle/indexes/equityindex.hpp>

namespace QuantExt {
using namespace QuantLib;

//! Wrapper class for a BlackVolTermStructure that allows us to proxy one equity vol surface off another.
/*! This class implements BlackVolatilityTermStructure and takes a surface (well, any BlackVolTermStructure) as an
    input. It also takes Handles to two EquityIndices (index and proxyIndex), where index is the 'EquityIndex' of the
   underlying for the surface being constructed and proxyIndex is the 'EquityIndex' for the surface being proxied off.

    The vol returned from the new surface is proxied from the base, adjusting by the forward prices to match ATM:

    \f{eqnarray}{
    \sigma_2(K,T) = \sigma_1(\frac{K}{F_2}*F_1,T)
    \f}

    Where \n\n
    \f$ \sigma_1 = \text{Volatility of underlying being proxied against}\f$ \n
    \f$ \sigma_2 = \text{Volatility of underlying being proxied}\f$ \n
    \f$ F_1 = \text{Forward at time T of the underlying being proxied against}\f$ \n
    \f$ F_2 = \text{Forward at time T of the underlying being proxied}\f$ \n
    \f$ T = \text{Time}\f$
    \n

    Note: This surface only proxies equity volatilities, this is because we are forced to look up the equity fixings
    using time instead of date and use the forecastFixing method in an EquityIndex. A more general class could be
   developed if need, using Index instead of EquityIndex, if the time lookup could be overcome.

    */
//!\ingroup termstructures

class EquityBlackVolatilitySurfaceProxy : public BlackVolatilityTermStructure {
public:
    //! Constructor. This is a floating term structure (settlement days is zero)
    EquityBlackVolatilitySurfaceProxy(const boost::shared_ptr<BlackVolTermStructure>& proxySurface,
                                      const boost::shared_ptr<EquityIndex>& index,
                                      const boost::shared_ptr<EquityIndex>& proxyIndex);

    //! \name TermStructure interface
    //@{
    DayCounter dayCounter() const { return proxySurface_->dayCounter(); }
    Date maxDate() const { return proxySurface_->maxDate(); }
    Time maxTime() const { return proxySurface_->maxTime(); }
    const Date& referenceDate() const { return proxySurface_->referenceDate(); }
    Calendar calendar() const { return proxySurface_->calendar(); }
    Natural settlementDays() const { return proxySurface_->settlementDays(); }
    //@}

    //! \name VolatilityTermStructure interface
    //@{
    Rate minStrike() const;
    Rate maxStrike() const;
    //@}

    //! \name Inspectors
    //@{
    boost::shared_ptr<BlackVolTermStructure> proxySurface() const { return proxySurface_; }
    boost::shared_ptr<EquityIndex> index() const { return index_; }
    boost::shared_ptr<EquityIndex> proxyIndex() const { return proxyIndex_; }
    //@}

protected:
    // Here we adjust the returned vol.
    Volatility blackVolImpl(Time t, Real strike) const;

private:
    boost::shared_ptr<BlackVolTermStructure> proxySurface_;
    boost::shared_ptr<EquityIndex> index_, proxyIndex_;
};

} // namespace QuantExt

#endif


#ifndef DETECTNET_TRANSFORMATION_HPP
#define DETECTNET_TRANSFORMATION_HPP

#include <boost/array.hpp>

#include <opencv2/core/core.hpp>
#include <opencv2/opencv.hpp>

#include <vector>

#include "caffe/blob.hpp"
#include "caffe/common.hpp"
#include "caffe/layer.hpp"
#include "caffe/proto/caffe.pb.h"
#ifndef CPU_ONLY
#include "caffe/util/gpu_memory.hpp"
#endif

namespace caffe {

template<typename Dtype>
class CoverageGenerator;

template<typename Dtype>
struct BboxLabel_;

struct AugmentSelection {
  // Color augmentations
  float hue_rotation;
  float saturation;
  // Spatial augmentations
  bool flip;
  cv::Size scale;
  float rotation;
  cv::Point crop_offset;

  bool doHueRotation() const { return std::abs(hue_rotation) > FLT_EPSILON; }
  bool doDesaturation() const { return saturation < (1.0 - 1.0/UINT8_MAX); }
  bool doScale(const cv::Size& s) const {
    return s.height != scale.height || s.width != scale.width;
  }
  bool doRotation() const { return std::abs(rotation) > FLT_EPSILON; }
};

/**
 * @brief Applies common transformations to the input data, such as
 * scaling, mirroring, substracting the image mean...
 */
template <typename Dtype>
class DetectNetTransformationLayer : public Layer<Dtype, Dtype> {
 public:
  typedef cv::Size2i Size2i;
  typedef cv::Size_<Dtype> Size2v;
  typedef cv::Point2i Point2i;
  typedef cv::Point_<Dtype> Point2v;
  typedef cv::Rect Rect;
  typedef cv::Rect_<Dtype> Rectv;
  typedef cv::Vec<Dtype, 3> Vec3v;
  typedef cv::Mat_<cv::Vec<Dtype, 1> > Mat1v;
  typedef cv::Mat_<Vec3v> Mat3v;
  typedef BboxLabel_<Dtype> BboxLabel;

  explicit DetectNetTransformationLayer(const LayerParameter& param);

  virtual ~DetectNetTransformationLayer() {}

  virtual void LayerSetUp(const vector<Blob*>& bottom,
      const vector<Blob*>& top);
  virtual void Reshape(const vector<Blob*>& bottom,
      const vector<Blob*>& top);

  virtual inline const char* type() const { return "DetectNetTransformation"; }
  virtual inline int ExactNumBottomBlobs() const { return 2; }
  virtual inline int ExactNumTopBlobs() const { return 2; }

 protected:
  virtual void Forward_cpu(
      const vector<Blob*>& bottom,
      const vector<Blob*>& top);
  virtual void Forward_gpu(
      const vector<Blob*>& bottom,
      const vector<Blob*>& top);

  virtual void Backward_cpu(
      const vector<Blob*>& top,
      const vector<bool>& propagate_down,
      const vector<Blob*>& bottom) {}
  virtual void Backward_gpu(
      const vector<Blob*>& top,
      const vector<bool>& propagate_down,
      const vector<Blob*>& bottom) {}

  AugmentSelection get_augmentations(cv::Size);

  // Image transformations
  Mat3v transform_image_cpu(const Mat3v&, const AugmentSelection&);
  Mat3v transform_hsv_cpu(const Mat3v&, const AugmentSelection&);
  Mat3v rotate_image_cpu(const Mat3v&, const float);
  Mat3v crop_image_cpu(const Mat3v&, const cv::Size&, const cv::Point&);

  // Label transformations
  void transform_label_cpu(vector<BboxLabel>, Dtype*,
      const AugmentSelection&, const cv::Size&);
  vector<BboxLabel> flip_label_cpu(const vector<BboxLabel>&,
      const cv::Size&);
  vector<BboxLabel> scale_label_cpu(const vector<BboxLabel>&,
      const cv::Size&, const cv::Size&);
  vector<BboxLabel> rotate_label_cpu(const vector<BboxLabel>&,
      const cv::Size&, const float);
  vector<BboxLabel> crop_label_cpu(const vector<BboxLabel>&, const cv::Point&);

  /**
   * @return a Dtype from [0..1].
   */
  Dtype randDouble();

  Mat1v getTransformationMatrix(Rect region, Dtype rotation) const;
  cv::Size getRotatedSize(cv::Size, float rotation) const;
  void matToBlob(const Mat3v& source, Dtype* destination) const;
  void matsToBlob(const vector<Mat3v>& source, Blob* destination) const;
  vector<Mat3v> blobToMats(const Blob& image) const;
  vector<vector<BboxLabel> > blobToLabels(const Blob& labels) const;
  Mat3v dataToMat(
      const Dtype* _data,
      Size2i dimensions) const;
  void retrieveMeanImage(Size2i dimensions = Size2i());
  void retrieveMeanChannels();

  void meanSubtract(Mat3v* source) const;
  void pixelMeanSubtraction(Mat3v* source) const;
  void channelMeanSubtraction(Mat3v* source) const;

  DetectNetAugmentationParameter a_param_;
  DetectNetGroundTruthParameter g_param_;
  TransformationParameter t_param_;

  shared_ptr<CoverageGenerator<Dtype> > coverage_;

  Phase phase_;

  Mat3v data_mean_;
#ifndef CPU_ONLY
  TBlob<Dtype> mean_blob_;
  GPUMemory::Workspace gpu_workspace_augmentations_;
  GPUMemory::Workspace gpu_workspace_tmpdata_;
#endif
  boost::array<Dtype, 3> mean_values_;
  shared_ptr<Caffe::RNG> rng_;
};

}  // namespace caffe

#endif /* DETECTNET_TRANSFORMATION_HPP */


#include "Agent.h"
#include <exception>
#include <random>
#include <boost\dynamic_bitset.hpp>
#include "GeneticSequence.h"

namespace MDLG
{
	void Agent::BreedAgents(std::mt19937& rng, Agent & parent1, Agent & parent2, Agent & child1, Agent & child2, int num_crossovers, double mutation_rate)
	{
		//Ensure all are compatible
		if (!(parent1.IsCompatible(parent2) && parent2.IsCompatible(child1) && child1.IsCompatible(child2)))
		{
			throw std::invalid_argument("Agents are incompatible.");
		}

		child1.genome.clear();
		child2.genome.clear();

		for (int i = 0; i < parent1.genome.size(); i++)
		{
			std::vector<boost::dynamic_bitset<>> results = GeneticSequence::Crossover(rng, num_crossovers, parent1.genome[i], parent2.genome[i]);
			GeneticSequence::Mutate(rng, results[0], mutation_rate);
			GeneticSequence::Mutate(rng, results[0], mutation_rate);

			//Add to child genomes
			child1.genome.push_back(results[0]);
			child2.genome.push_back(results[1]);
		}
	}

	Agent::Agent()
	{
	}

	int Agent::AddSequence(int length)
	{
		this->genome_lengths.push_back(length);
		return this->genome_lengths.size() - 1;
	}

	void Agent::SetGenome(std::vector<boost::dynamic_bitset<>> genome, bool ensure_length_compliance)
	{
		if (ensure_length_compliance)
		{
			//Verify length
			if (genome.size() != genome_lengths.size())
			{
				throw std::invalid_argument("Genome does not comply with agent genome length.");
			}

			for (int i = 0; i < genome.size(); i++)
			{
				if (genome[i].size() != genome_lengths[i])
				{
					throw std::invalid_argument("Genome does not comply with agent genome length.");
				}
			}
		}

		this->genome = genome;
	}

	void Agent::SetRandomGenome(std::mt19937 & rng)
	{
		this->genome.clear();
		for (int i = 0; i < this->genome_lengths.size(); i++)
		{
			std::string genome_string = "";
			std::uniform_int_distribution<> dis(1, 2);
			for (int j = 0; j < this->genome_lengths[i]; j++)
			{
				genome_string += std::to_string(dis(rng) % 2);
			}
			this->genome.push_back(boost::dynamic_bitset<>(genome_string));
		}
	}

	const std::vector<boost::dynamic_bitset<>>* Agent::get_genome() const
	{
		return &this->genome;
	}

	const std::vector<int>* Agent::get_genome_lengths() const
	{
		return &this->genome_lengths;
	}

	bool Agent::IsCompatible(Agent & other) const
	{
		if (other.genome_lengths.size() != this->genome_lengths.size())
		{
			return false;
		}
		for (int i = 0; i < this->genome_lengths.size(); i++)
		{
			if (other.genome_lengths[i] != this->genome_lengths[i])
			{
				return false;
			}
		}

		return true;
	}

	Agent::~Agent()
	{
	}
}


#define BOOST_TEST_MODULE pcraster model_engine dimensionparser
#include <boost/test/unit_test.hpp>
#include "com_exception.h"

#define private public
#include "calc_dimensionparser.h"

// NOTE use string failureExpected in files expected to fail, see style guide

BOOST_AUTO_TEST_CASE(test)
{
  using namespace calc;

 {
  DimensionParser dp("a 4");
  BOOST_CHECK(dp.d_symbols.size()==1);
  BOOST_CHECK(dp.d_symbols[0].d_symbol == "a");
  BOOST_CHECK(dp.d_symbols[0].d_power  == 4);
 }
 {
  DimensionParser dp("m2s-1");
  BOOST_CHECK(dp.d_symbols.size()==2);
  BOOST_CHECK(dp.d_symbols[0].d_symbol == "m");
  BOOST_CHECK(dp.d_symbols[0].d_power  == 2);
  BOOST_CHECK(dp.d_symbols[1].d_symbol == "s");
  BOOST_CHECK(dp.d_symbols[1].d_power  == -1);
 }
 {
  DimensionParser dp("kg,m");
  BOOST_CHECK(dp.d_symbols.size()==2);
  BOOST_CHECK(dp.d_symbols[0].d_symbol == "kg");
  BOOST_CHECK(dp.d_symbols[0].d_power  == 1);
  BOOST_CHECK(dp.d_symbols[1].d_symbol == "m");
  BOOST_CHECK(dp.d_symbols[1].d_power  == 1);
 }
}

BOOST_AUTO_TEST_CASE(testError)
{
  using namespace calc;

 try {
  DimensionParser dp("kg;m");
 } catch (const com::Exception& e) {
BOOST_CHECK(e.messages().find("';' is not a recognized unit dimension")
   != std::string::npos);
 }
 try {
  DimensionParser dp("X kg");
 } catch (const com::Exception& e) {
BOOST_CHECK(e.messages().find("X") != std::string::npos);
 }

}


// Copyright 2014 Jonathan Graehl-http://graehl.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.
/** \file

    fast atoi for all integral types (atou for unsigned)
*/

#ifndef GRAEHL__ATOI_FAST_JG2012615_HPP
#define GRAEHL__ATOI_FAST_JG2012615_HPP
#pragma once

#ifndef GRAEHL_HAVE_STRTOUL
#ifdef _MSC_VER
#define GRAEHL_HAVE_STRTOUL 0
#else
#define GRAEHL_HAVE_STRTOUL 1
#endif
#endif

#include <boost/integer_traits.hpp>
#include <graehl/shared/verbose_exception.hpp>
#include <algorithm>
#include <cctype>
#include <cstdlib>
#include <cstring>
#include <iterator>
#include <limits>

#if GRAEHL_HAVE_STRTOUL
#include <limits.h>  //strtoul
#endif
#undef min  // damn you, windows
#undef max
#undef DELETE

#ifdef GRAEHL_TEST
#include <graehl/shared/test.hpp>
#endif

namespace graehl {

VERBOSE_EXCEPTION_DECLARE(string_to_exception)

// NOTE: stdlib atoi consumes dead whitespace; these don't
template <class U>
inline U atou_fast(char const* p) {  // faster than stdlib atoi. doesn't return how much of string was used.
  U x = 0;
  while (*p >= '0' && *p <= '9') {
    x *= 10;
    x += *p - '0';
    ++p;
  }
  return x;
}

template <class I>
inline I atoi_fast(char const* p) {  // faster than stdlib atoi. doesn't return how much of string was used.
  I x = 0;
  bool neg = false;
  if (*p == '-') {
    neg = true;
    ++p;
  }
  while (*p >= '0' && *p <= '9') {
    x *= 10;
    x += *p - '0';
    ++p;
  }
  return neg ? -x : x;
}

template <class CharIt>
struct PutChars {
  CharIt begin, end;
  PutChars(CharIt begin, CharIt end) : begin(begin), end(end) {}
  friend inline std::ostream& operator<<(std::ostream& out, PutChars const& self) {
    self.print(out);
    return out;
  }
  void print(std::ostream& out) const {
    for (CharIt i = begin; i != end; ++i) out.put(*i);
  }
};

template <class CharIt>
PutChars<CharIt> putChars(CharIt begin, CharIt end) {
  return PutChars<CharIt>(begin, end);
}

inline unsigned char hexdigit(char c) {
  if (c >= '0' && c <= '9') {
    return c - '0';
  } else if (c >= 'a' && c <= 'f') {
    return 10 + c - 'a';
  } else if (c >= 'A' && c <= 'F') {
    return 10 + c - 'A';
  } else {
    throw string_to_exception("expected a hexadecimal [01234567890abcdefABCDEF] digit");
  }
}

template <class U>
inline U hextou(char const* begin, char const* end, bool complete = true) {
  char const* p = begin;
  U x = 0;
  for (;;) {
    if (*p >= '0' && *p <= '9') {
      x *= 16;
      x += *p - '0';
    } else if (*p >= 'a' && *p <= 'f') {
      x *= 16;
      x += 10 + *p - 'a';
    } else if (*p >= 'A' && *p <= 'F') {
      x *= 16;
      x += 10 + *p - 'A';
    } else {
      if (complete)
        THROW_MSG(string_to_exception, "hexidecimal string '" << putChars(begin, end)
                                                              << "' had extra characters - value so far is "
                                                              << x);
      return x;
    }
    if (++p == end) return x;
  }
}

template <class U>
inline U octaltou(char const* begin, char const* end, bool complete = true) {
  char const* p = begin;
  U x = 0;
  for (;;) {
    if (*p >= '0' && *p <= '7') {
      x *= 8;
      x += *p - '0';
    } else {
      if (complete)
        THROW_MSG(string_to_exception, "hexidecimal string '" << putChars(begin, end)
                                                              << "' had extra characters - value so far is "
                                                              << x);
      return x;
    }
    if (++p == end) return x;
  }
}

template <class U, class I>
inline U atou_fast_advance(I& i, I end) {
  typedef typename std::iterator_traits<I>::value_type Char;
  U x = 0;
  if (i == end) return x;
  for (; i != end; ++i) {
    const Char c = *i;
    if (c < '0' || c > '9') return x;
    x *= 10;
    x += c - '0';
  }
  return x;
}

template <class U, class I>
inline U atou_fast(I i, I end) {
  return atou_fast_advance<U>(i, end);
}

template <class U, class I>
inline U atou_fast_advance_nooverflow(I& i, I end) {
  typedef typename std::iterator_traits<I>::value_type Char;
  I begin = i;
  const U maxTenth = boost::integer_traits<U>::const_max / 10;
  U x = 0;
  if (i == end) return x;
  for (; i != end; ++i) {
    const Char c = *i;
    if (c < '0' || c > '9') return x;
    if (x > maxTenth)
      THROW_MSG(string_to_exception, "ascii to unsigned overflow on char "
                                         << c << " in '" << putChars(begin, end) << "': " << x << "*10 > "
                                         << boost::integer_traits<U>::const_max);
    x *= 10;
    U prev = x;
    x += c - '0';
    if (x < prev)
      THROW_MSG(string_to_exception, "ascii to unsigned overflow on char "
                                         << c << " in '" << putChars(begin, end) << "': " << prev
                                         << " * 10 + " << c << " => (overflow) " << x);
  }
  return x;
}

template <class U, class Str, class I>
inline U atou_fast_advance_nooverflow(Str const& s, I& i, I end) {
  return atou_fast_advance_nooverflow<U>(i, end);
}

template <class U, class It>
inline U atou_fast_complete(It begin, It end) {
  It i = begin;
  U r = atou_fast_advance<U>(i, end);
  if (i != end)
    THROW_MSG(string_to_exception, "ascii to unsigned incomplete - only used first "
                                       << i - begin << " characters of '" << putChars(begin, end) << "' => "
                                       << r << " - trim whitespace too");
  return r;
}

template <class U, class Str, class I>
inline U atou_fast_complete(Str const& s, I begin, I end) {
  return atou_fast_complete<U>(begin, end);
}

template <class U, class Str>
inline U atou_fast_complete(Str const& s) {
  return atou_fast_complete<U>(s.begin(), s.end());
}

template <class U>
inline U atou_fast_complete(char const* s) {
  return atou_fast_complete<U>(s, s + std::strlen(
                                          s));  // TODO: could skip strlen call w/ a few more lines of code
}

template <class I>
inline I
atou_fast(std::string const& s) {  // faster than stdlib atoi. doesn't return how much of string was used.
  return atou_fast<I>(s.begin(), s.end());
}

template <class I, class It>
inline I atoi_fast_advance(It& i, It end) {
  typedef typename std::iterator_traits<It>::value_type Char;
  I x = 0;
  if (i == end) return x;
  bool neg = false;
  if (*i == '-') {
    neg = true;
    ++i;
  }
  for (; i != end; ++i) {
    const Char c = *i;
    if (c < '0' || c > '9') return x;
    x *= 10;
    x += c - '0';
  }
  return neg ? -x : x;
}

template <class I, class It>
inline I atoi_fast(It i, It end) {
  return atoi_fast_advance<I>(i, end);
}

template <class I, class It>
inline I atoi_fast_complete(It begin, It end) {
  It i = begin;
  I r = atoi_fast_advance<I>(i, end);
  if (i != end)
    THROW_MSG(string_to_exception, "ascii to int incomplete - only used first "
                                       << i - begin << " characters of '" << putChars(begin, end) << "' => "
                                       << r << " - trim whitespace too");
  return r;
}

template <class I>
inline I
atoi_fast(std::string const& s) {  // faster than stdlib atoi. doesn't return how much of string was used.
  return atoi_fast<I>(s.begin(), s.end());
}

inline int atoi_nows(std::string const& s) {
  return atoi_fast<int>(s);
}

inline int atoi_nows(char const* s) {
  return atoi_fast<int>(s);
}

inline unsigned atou_nows(std::string const& s) {
  return atou_fast<unsigned>(s);
}

inline unsigned atou_nows(char const* s) {
  return atou_fast<unsigned>(s);
}

inline long strtol_complete(char const* s, int base = 0) {
  char* e;
  if (*s) {
    long r = strtol(s, &e, base);
    char c = *e;
    if (!c || std::isspace(c))  // simplifying assumption: we're happy if there's other stuff in the string,
      // so long as the number ends in a space or eos. TODO: loop consuming spaces
      // until end?
      return r;
    THROW_MSG(string_to_exception, "integer from string '" << s << "' => " << r << " had extra chars: '" << e
                                                           << "'");
  }
  // empty string => 0
  return 0;
}

// returns -INT_MAX or INT_MAX if number is too large/small
inline int strtoi_complete_bounded(char const* s, int base = 0) {
  long l = strtol_complete(s, base);
  if (l < std::numeric_limits<int>::min()) return std::numeric_limits<int>::min();
  if (l > std::numeric_limits<int>::max()) return std::numeric_limits<int>::max();
  return l;
}
#define RANGE_STR(x) #x
#ifdef INT_MIN
#define INTRANGE_STR "[" RANGE_STR(INT_MIN) "," RANGE_STR(INT_MAX) "]"
#else
#define INTRANGE_STR "[-2137483648,2147483647]"
#endif

// throw if out of int range
inline int strtoi_complete_exact(char const* s, int base = 10) {
  long l = strtol_complete(s, base);
  if (l < std::numeric_limits<int>::min() || l > std::numeric_limits<int>::max())
    THROW_MSG(string_to_exception, "Out of range for int " INTRANGE_STR ": '" << s << "'");
  return l;
}

// FIXME: preprocessor separation for tokens int<->unsigned, long<->unsigned long, strtol<->strtoul ? massive
// code duplication
inline unsigned long strtoul_complete(char const* s, int base = 0) {
  unsigned long r;
  if (*s) {
#if GRAEHL_HAVE_STRTOUL
    char* e;
    r = strtoul(s, &e, base);
    char c = *e;
    if (!c || std::isspace(c))  // simplifying assumption: we're happy if there's other stuff in the string,
      // so long as the number ends in a space or eos. TODO: loop consuming spaces
      // until end?
      return r;
#else
    int nchars;
    // unsigned long r=strtol(s, &e, base); //FIXME: not usually safe
    if (sscanf(s, "%lu%n", &r, &nchars) && s[nchars] == '\0') return r;
#endif
  }
  THROW_MSG(string_to_exception, "can't get integer from '" << s << "'");
  return 0;  // quiet, warning!
}

inline unsigned strtou_complete_bounded(char const* s, int base = 10) {
  unsigned long l = strtoul_complete(s, base);
#include <graehl/shared/warning_compiler.h>
  CLANG_DIAG_OFF(tautological-compare)
  if (l < std::numeric_limits<unsigned>::min()) return std::numeric_limits<unsigned>::min();
  if (l > std::numeric_limits<unsigned>::max()) return std::numeric_limits<unsigned>::max();
  return l;
}

#ifdef UINT_MIN
#define UINTRANGE_STR "[" RANGE_STR(UINT_MIN) "-" RANGE_STR(UINT_MAX) "]"
#else
#define UINTRANGE_STR "[0-4,294,967,295]"
#endif

// throw if out of int range
inline unsigned strtou_complete_exact(char const* s, int base = 10) {
  unsigned long l = strtoul_complete(s, base);
  if (l < std::numeric_limits<unsigned>::min() || l > std::numeric_limits<unsigned>::max())
    THROW_MSG(string_to_exception, "Out of range for unsigned " UINTRANGE_STR ": '" << s << "'");
  CLANG_DIAG_ON(tautological-compare)
  return l;
}

struct StrCursor {
  char const* p;
  char const* end;
  bool operator!() const { return p == end; }
  operator bool() const { return p != end; }
  template <class Str>
  StrCursor(Str const& str) : p(&*str.begin()), end(&*str.end()) {}
  StrCursor(char const* cstr) : p(cstr), end(cstr + std::strlen(cstr)) {}
  StrCursor(char const* begin, char const* end) : p(begin), end(end) {}
};

struct CstrCursor {
  char const* p;
  bool operator!() const { return !*p; }
  operator bool() const { return *p; }
  CstrCursor(char const* cstr) : p(cstr) {}
};

inline bool space_or_tab(char c) {
  return c == ' ' || c == '\t';
}

inline bool digit_char(char c) {
  return c >= '0' && c <= '9';
}

/**
   \return a Float (e.g. float or double) from a string - assumes valid regular
   (not nan, inf, -inf, etc) nonnegative real numbers like 1.23E-20 (with no + sign)

   std::strtod has the deficiency that the string is assumed to be
   null-terminated. if we have a Field (a char * pair) rather than a std::string
   we need this

   the next-fastest alternative is the heavier-to-compile Boost spirit
*/
template <class Float, class StrCursor>
Float scan_real_no_sign(StrCursor& c) {
  // before decimal
  Float value = (Float)0;
  while (digit_char(*c.p)) {
    value = value * (Float)10 + (*c.p - '0');
    ++c.p;
    if (!c) return value;
  }

  // optional decimal, after decimal
  if (*c.p == '.') {
    ++c.p;
    if (!c) return value;
    Float value_of_digit = (Float)0.1;
    while (digit_char(*c.p)) {
      value += (*c.p - '0') * value_of_digit;
      ++c.p;
      if (!c) return value;
      value_of_digit *= (Float)0.1;
    }
  }

  // optional exponent
  if (c && ((*c.p == 'e') || (*c.p == 'E'))) {
    ++c.p;
    if (!c) return value;

    // optional exponent sign
    bool const negative_exponent = (*c.p == '-');
    if (negative_exponent) {
      ++c.p;
      if (!c) return value;
    } else if (*c.p == '+') {
      ++c.p;
      if (!c) return value;
    }

    unsigned exponent = 0;
    while (digit_char(*c.p)) {
      exponent = exponent * 10 + (*c.p - '0');
      ++c.p;
      if (!c) break;
    }

    if (exponent > std::numeric_limits<Float>::max_exponent)
      return negative_exponent ? (Float)0 : std::numeric_limits<Float>::infinity();

    // more numerically accurate than std::pow10 and about as fast (since |exponent| is never large)
    if (negative_exponent) {
      while (exponent >= 8) {
        value *= (Float)1e-8;
        exponent -= 8;
      }
      while (exponent > 0) {
        value *= (Float)1e-1;
        exponent -= 1;
      }
    } else {
      while (exponent >= 8) {
        value *= (Float)1e8;
        exponent -= 8;
      }
      while (exponent > 0) {
        value *= (Float)1e1;
        exponent -= 1;
      }
    }
  }

  return value;
}

template <class StrCursor>
bool cursor_at_inf(StrCursor& i) {
  if (i) {
    char const* p = i.p;
    if (*i.p == 'i' || *i.p == 'I')
      if (++i.p) {
        if (*i.p == 'n' || *i.p == 'N')
          if (++i.p)
            if (*i.p == 'f' || *i.p == 'F') {
              ++i.p;
              return true;
            }
        i.p = p;
      }
  }
  return false;
}

/**
   as scan_real_no_sign but handling - or + sign, optionally leading space, and
   optionally allowing INF or inf (but not NaN) input
*/
template <class Float, class StrCursor>
Float scan_real(StrCursor& c, bool skip_leading_space = false, bool parse_inf = true) {
  if (!c) return (Float)0;
  if (skip_leading_space)
    while (space_or_tab(*c.p)) {
      ++c.p;
      if (!c) return (Float)0;
    }
  // optional sign
  bool negative = false;
  if (*c.p == '-') {
    ++c.p;
    if (!c) return (Float)0;
    negative = true;
  } else if (*c.p == '+') {
    ++c.p;
    if (!c) return (Float)0;
  }
  if (parse_inf && cursor_at_inf(c))
    return negative ? -std::numeric_limits<Float>::infinity() : std::numeric_limits<Float>::infinity();
  else {
    Float const value = scan_real_no_sign<Float>(c);
    return negative ? -value : value;
  }
}

template <class Float>
Float parse_real(char const* p, char const* end, bool require_complete = true) {
  StrCursor str(p, end);
  if (require_complete) {
    Float r = scan_real<Float>(str);
    if (str)
      THROW_MSG(string_to_exception, "conversion to real number from '" << putChars(p, end)
                                                                        << "' leaves unused characters "
                                                                        << putChars(str.p, end));
    return r;
  } else
    return scan_real<Float>(str);
}

inline float parse_float(char const* p, char const* end, bool require_complete = true) {
  return parse_real<float>(p, end, require_complete);
}

inline double parse_double(char const* p, char const* end, bool require_complete = true) {
  return parse_real<double>(p, end, require_complete);
}

template <class Float>
Float parse_real(char const* cstr, bool require_complete = true) {
  CstrCursor str(cstr);
  if (require_complete) {
    Float r = scan_real<Float>(str);
    if (str)
      THROW_MSG(string_to_exception, "conversion to real number from '"
                                         << cstr << "' leaves unused characters " << str.p);
    return r;
  } else
    return scan_real<Float>(str);
}

inline float parse_float(char const* cstr, bool require_complete = true) {
  return parse_real<float>(cstr, require_complete);
}

inline double parse_double(char const* cstr, bool require_complete = true) {
  return parse_real<double>(cstr, require_complete);
}

#ifdef GRAEHL_TEST
BOOST_AUTO_TEST_CASE(test_scan_real) {
  BOOST_CHECK_EQUAL(parse_float("1.25"), 1.25f);
  BOOST_CHECK_THROW(parse_float("1.25a"), string_to_exception);
  BOOST_CHECK_THROW(parse_float("1.25 "), string_to_exception);
  BOOST_CHECK_EQUAL(parse_float("1.25 ", false), 1.25f);
  BOOST_CHECK_EQUAL(parse_float("1.25e10"), 1.25e10f);
  BOOST_CHECK_EQUAL(parse_float("1.25e+10"), 1.25e10f);
  BOOST_CHECK_EQUAL(parse_float("1.25e-10"), 1.25e-10f);
  BOOST_CHECK_EQUAL(parse_float("1e10"), 1e10f);
  BOOST_CHECK_EQUAL(parse_float("1e+10"), 1e10f);
  BOOST_CHECK_EQUAL(parse_float("1e-10"), 1e-10f);
  BOOST_CHECK_EQUAL(parse_float("123456"), 123456.f);
  BOOST_CHECK_EQUAL(parse_float("0.001953125"), 0.001953125f);
}
#endif

}

#endif


// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2014 Benoit Steiner <benoit.steiner.goog@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include "main.h"
#include "random_without_cast_overflow.h"

#include <Eigen/CXX11/Tensor>

using Eigen::Tensor;
using Eigen::array;

static void test_simple_cast()
{
  Tensor<float, 2> ftensor(20,30);
  ftensor = ftensor.random() * 100.f;
  Tensor<char, 2> chartensor(20,30);
  chartensor.setRandom();
  Tensor<std::complex<float>, 2> cplextensor(20,30);
  cplextensor.setRandom();

  chartensor = ftensor.cast<char>();
  cplextensor = ftensor.cast<std::complex<float> >();

  for (int i = 0; i < 20; ++i) {
    for (int j = 0; j < 30; ++j) {
      VERIFY_IS_EQUAL(chartensor(i,j), static_cast<char>(ftensor(i,j)));
      VERIFY_IS_EQUAL(cplextensor(i,j), static_cast<std::complex<float> >(ftensor(i,j)));
    }
  }
}

static void test_vectorized_cast()
{
  Tensor<int, 2> itensor(20,30);
  itensor = itensor.random() / 1000;
  Tensor<float, 2> ftensor(20,30);
  ftensor.setRandom();
  Tensor<double, 2> dtensor(20,30);
  dtensor.setRandom();

  ftensor = itensor.cast<float>();
  dtensor = itensor.cast<double>();

  for (int i = 0; i < 20; ++i) {
    for (int j = 0; j < 30; ++j) {
      VERIFY_IS_EQUAL(itensor(i,j), static_cast<int>(ftensor(i,j)));
      VERIFY_IS_EQUAL(dtensor(i,j), static_cast<double>(ftensor(i,j)));
    }
  }
}

static void test_float_to_int_cast()
{
  Tensor<float, 2> ftensor(20,30);
  ftensor = ftensor.random() * 1000.0f;
  Tensor<double, 2> dtensor(20,30);
  dtensor = dtensor.random() * 1000.0;

  Tensor<int, 2> i1tensor = ftensor.cast<int>();
  Tensor<int, 2> i2tensor = dtensor.cast<int>();

  for (int i = 0; i < 20; ++i) {
    for (int j = 0; j < 30; ++j) {
      VERIFY_IS_EQUAL(i1tensor(i,j), static_cast<int>(ftensor(i,j)));
      VERIFY_IS_EQUAL(i2tensor(i,j), static_cast<int>(dtensor(i,j)));
    }
  }
}

static void test_big_to_small_type_cast()
{
  Tensor<double, 2> dtensor(20, 30);
  dtensor.setRandom();
  Tensor<float, 2> ftensor(20, 30);
  ftensor = dtensor.cast<float>();

  for (int i = 0; i < 20; ++i) {
    for (int j = 0; j < 30; ++j) {
      VERIFY_IS_APPROX(dtensor(i,j), static_cast<double>(ftensor(i,j)));
    }
  }
}

static void test_small_to_big_type_cast()
{
  Tensor<float, 2> ftensor(20, 30);
  ftensor.setRandom();
  Tensor<double, 2> dtensor(20, 30);
  dtensor = ftensor.cast<double>();

  for (int i = 0; i < 20; ++i) {
    for (int j = 0; j < 30; ++j) {
      VERIFY_IS_APPROX(dtensor(i,j), static_cast<double>(ftensor(i,j)));
    }
  }
}

template <typename FromType, typename ToType>
static void test_type_cast() {
  Tensor<FromType, 2> ftensor(100, 200);
  // Generate random values for a valid cast.
  for (int i = 0; i < 100; ++i) {
    for (int j = 0; j < 200; ++j) {
      ftensor(i, j) = internal::random_without_cast_overflow<FromType,ToType>::value();
    }
  }

  Tensor<ToType, 2> ttensor(100, 200);
  ttensor = ftensor.template cast<ToType>();

  for (int i = 0; i < 100; ++i) {
    for (int j = 0; j < 200; ++j) {
      const ToType ref = internal::cast<FromType,ToType>(ftensor(i, j));
      VERIFY_IS_APPROX(ttensor(i, j), ref);
    }
  }
}

template<typename Scalar, typename EnableIf = void>
struct test_cast_runner {
  static void run() {
    test_type_cast<Scalar, bool>();
    test_type_cast<Scalar, int8_t>();
    test_type_cast<Scalar, int16_t>();
    test_type_cast<Scalar, int32_t>();
    test_type_cast<Scalar, int64_t>();
    test_type_cast<Scalar, uint8_t>();
    test_type_cast<Scalar, uint16_t>();
    test_type_cast<Scalar, uint32_t>();
    test_type_cast<Scalar, uint64_t>();
    test_type_cast<Scalar, half>();
    test_type_cast<Scalar, bfloat16>();
    test_type_cast<Scalar, float>();
    test_type_cast<Scalar, double>();
    test_type_cast<Scalar, std::complex<float>>();
    test_type_cast<Scalar, std::complex<double>>();
  }
};

// Only certain types allow cast from std::complex<>.
template<typename Scalar>
struct test_cast_runner<Scalar, typename internal::enable_if<NumTraits<Scalar>::IsComplex>::type> {
  static void run() {
    test_type_cast<Scalar, half>();
    test_type_cast<Scalar, bfloat16>();
    test_type_cast<Scalar, std::complex<float>>();
    test_type_cast<Scalar, std::complex<double>>();
  }
};

EIGEN_DECLARE_TEST(cxx11_tensor_casts)
{
  CALL_SUBTEST(test_simple_cast());
  CALL_SUBTEST(test_vectorized_cast());
  CALL_SUBTEST(test_float_to_int_cast());
  CALL_SUBTEST(test_big_to_small_type_cast());
  CALL_SUBTEST(test_small_to_big_type_cast());

  CALL_SUBTEST(test_cast_runner<bool>::run());
  CALL_SUBTEST(test_cast_runner<int8_t>::run());
  CALL_SUBTEST(test_cast_runner<int16_t>::run());
  CALL_SUBTEST(test_cast_runner<int32_t>::run());
  CALL_SUBTEST(test_cast_runner<int64_t>::run());
  CALL_SUBTEST(test_cast_runner<uint8_t>::run());
  CALL_SUBTEST(test_cast_runner<uint16_t>::run());
  CALL_SUBTEST(test_cast_runner<uint32_t>::run());
  CALL_SUBTEST(test_cast_runner<uint64_t>::run());
  CALL_SUBTEST(test_cast_runner<half>::run());
  CALL_SUBTEST(test_cast_runner<bfloat16>::run());
  CALL_SUBTEST(test_cast_runner<float>::run());
  CALL_SUBTEST(test_cast_runner<double>::run());
  CALL_SUBTEST(test_cast_runner<std::complex<float>>::run());
  CALL_SUBTEST(test_cast_runner<std::complex<double>>::run());

}


/* -*-coding: mule-utf-8-unix; fill-column: 58; -*-

  It will mine a new genesis block at the currency creation time.
  It was shared for everyone's profit by elbandi at https://bitcointalk.org/index.php?topic=391983.0
*/

#include <forward_list>
#include <memory>
#include <chrono>
#include <boost/thread.hpp>
#include "types/abstract_iterator.h"
#include "uint256.h"
#include "bignum.h"
#include "main.h"
#include "n_factor.h"
#include "hash/hash.h"
#include "log.h"
#include "pars.h"
#include "algos/retarget.h"
#include "btc_time.h"

//#define LOG_BEST_HASH
//#define DO_NOT_CALIBRATE_DIFFICULTY

namespace {

// types used for search desired block difficulty

using difficulty_list = std::forward_list<block::info_type>;

using difficulty_iterator =
  types::virtual_iterator::const_forward
    <difficulty_list::const_iterator>;

}

namespace genesis {

void block::mine()
{
  using namespace std::chrono;

  // Set n cores will be used for other block mining here
  const int n_cores = pars::testnet_switch(
    types::make_pair(6, 1)
  );

  CBlock& block = *this;

  retarget::difficulty& D =
    retarget::difficulty::instance();

  const auto block_period =
    D.block_period_by_design * n_cores;

  compact_bignum_t target_difficulty =
    D.min_difficulty_by_design;

  // FIXME one round is not enough because the process
  // is stochastic
  unsigned start_nonce = 0;
  // <-- add a loop here

  bool found = false;

  difficulty_list d_list;
  d_list.emplace_front(
    ::block::info_type{
      0,
      0,
      coin::times::block::clock::now()
    }
  );
  const auto d_list_bottom = d_list.cbegin();

  do {

    block.nNonce = start_nonce;

    const auto start = coin::times::block::clock::now();

    LOG() << "Searching for genesis block..." << std::endl;
    uint256 hashTarget =
      CBigNum(target_difficulty).getuint256();
    uint256 thash;

    auto H = hash::hasher::instance(block.GetTimePoint());

    LOG() << "(genesis mining) start with difficulty "
          << target_difficulty << std::endl;
    block.nBits = target_difficulty;

    auto best_hash = ~uint256();

    loop
    {
      thash = H->hash(block);

      if (thash <= hashTarget) {
        found = true;
        break;
      }

      if (thash < best_hash) {
        best_hash = thash;
#ifdef LOG_BEST_HASH
        LOG() << "(genesis mining) Best hash: "
              << thash << std::endl;
#endif
#ifndef DO_NOT_CALIBRATE_DIFFICULTY
        const auto now = coin::times::block::clock::now();
        const auto passed = now - start;
        if (passed > block_period + block_period / 2) {
          LOG() << "(genesis mining) "
            "target searching takes too long: "
                << duration_cast<seconds>(passed)
                << " secs" << std::endl;
          d_list.push_front({
            d_list.front().height + 1,
            target_difficulty,
            now
          });
          target_difficulty = D.next_block_difficulty(
            block_period,
            difficulty_iterator(d_list.cbegin()),
            difficulty_iterator(d_list_bottom)
          );
          break;
        }
#endif
      }
      ++block.nNonce;
      if (block.nNonce == 0)
      {
        printf("NONCE WRAPPED, incrementing time\n");
        ++block.nTime;
      }
    }
    if (found) {
#ifndef DO_NOT_CALIBRATE_DIFFICULTY
      const auto now = coin::times::block::clock::now();
      const auto passed = now - start;
      if (passed < block_period / 2) {
        LOG() << "(genesis mining) "
          "target searching takes too short: "
              << duration_cast<seconds>(passed)
              << " secs" << std::endl;
        d_list.push_front({
          d_list.front().height + 1,
          target_difficulty,
          now
        });
        target_difficulty = D.next_block_difficulty(
          block_period,
          difficulty_iterator(d_list.cbegin()),
          difficulty_iterator(d_list_bottom)
        );
        found = false;
      }
#if 0 // allow to disable higher difficulty in genesis
      else  if (passed > block_period + block_period / 2) {
        LOG() << "(genesis mining) "
                 "target searching takes too long: "
              << duration_cast<seconds>(passed)
              << " secs" << std::endl;
        d_list.push_front({
          d_list.front().height + 1,
          target_difficulty,
          now
        });
        target_difficulty = D.next_block_difficulty(
          block_period,
          difficulty_iterator(d_list.cbegin()),
          difficulty_iterator(d_list_bottom)
        );
        found = false;
      }
#endif
#endif
    }
  } while (!found);
  printf("block.nTime = %u \n", block.nTime);
  printf("block.nNonce = %u \n", block.nNonce);
  printf("block.GetHash = %s\n", block.GetHash().ToString().c_str());
}

} // genesis


// Copyright (c) 2019 Graphcore Ltd. All rights reserved.
#define BOOST_TEST_MODULE PipelineTopoConTest0

#include <boost/test/unit_test.hpp>
#include <filereader.hpp>
#include <popart/builder.hpp>
#include <popart/dataflow.hpp>
#include <popart/ir.hpp>
#include <popart/op/identity.hpp>
#include <popart/op/ipucopy.hpp>
#include <popart/op/l1.hpp>
#include <popart/op/nll.hpp>
#include <popart/op/restore.hpp>
#include <popart/op/stash.hpp>
#include <popart/sgd.hpp>
#include <popart/tensor.hpp>
#include <popart/tensordata.hpp>
#include <popart/testdevice.hpp>

void prepareIr1(popart::Ir &ir) {

  using namespace popart;

  auto builder     = Builder::create();
  auto aiOnnx      = builder->aiOnnxOpset9();
  auto aiGraphcore = builder->aiGraphcoreOpset1();

  TensorInfo info{"FLOAT", std::vector<int64_t>{2, 2}};

  TensorInfo info_l{"INT32", std::vector<int64_t>{2}};

  auto input1 = builder->addInputTensor(info);
  auto input2 = builder->addInputTensor(info);
  auto input3 = builder->addInputTensor(info);

  auto l0 = builder->addInputTensor(info_l);
  auto l1 = builder->addInputTensor(info_l);

  std::vector<float> wVals(2 * 2, 1.0f);
  ConstVoidData wData = {wVals.data(), info};

  auto w0   = builder->addInitializedInputTensor(wData);
  auto act0 = aiOnnx.add({w0, input1}, "act0");
  act0      = aiOnnx.relu({act0});

  auto w1   = builder->addInitializedInputTensor(wData);
  auto act1 = aiOnnx.add({w1, act0}, "act1");
  act1      = aiOnnx.relu({act1});

  act1 = aiOnnx.sin({act1});
  act1 = aiOnnx.cos({act1});
  act1 = aiOnnx.mul({act0, act1});
  act1 = aiOnnx.sigmoid({act1});
  act1 = aiOnnx.sin({act1});
  act1 = aiOnnx.sigmoid({act1});

  auto w2   = builder->addInitializedInputTensor(wData);
  auto act2 = aiOnnx.add({w2, act1}, "act2");
  act2      = aiOnnx.relu({act2});

  auto w3      = builder->addInitializedInputTensor(wData);
  auto act3    = aiOnnx.add({w3, act2}, "act3");
  act3         = aiOnnx.relu({act3});
  auto act3nll = aiGraphcore.nllloss({act3, l0}, ReductionType::Mean);

  auto act4 = aiOnnx.add({act2, act3});

  act4 = aiOnnx.sigmoid({act4});
  act4 = aiOnnx.sin({act4});
  act4 = aiOnnx.sigmoid({act4});

  auto w5   = builder->addInitializedInputTensor(wData);
  auto act5 = aiOnnx.mul({act4, w5});
  auto act6 = aiOnnx.mul({act5, act3});

  act6 = aiOnnx.mul({act6, input2});

  // TODO : IpuCopyOp copies Tensors from multiple sources (T10373) are required
  // to handle this extension:
  //  act6 = aiOnnx.mul({act6, input1});
  //  act6 = aiOnnx.mul({act6, input3});
  auto act6l1 = aiGraphcore.l1loss({act6}, 0.1, ReductionType::Mean);

  auto act7 = aiOnnx.sin({act6});
  act7      = aiOnnx.sigmoid({act6});
  act7      = aiOnnx.add({act6, act7});

  auto act8   = aiOnnx.mul({act7, act7});
  auto w6     = builder->addInitializedInputTensor(wData);
  act8        = aiOnnx.mul({act8, w6});
  act8        = aiOnnx.add({act8, input3});
  auto act8l1 = aiGraphcore.l1loss({act8}, 0.2, ReductionType::Sum);

  auto act9    = aiOnnx.relu({act8});
  act9         = aiOnnx.softmax({act9});
  auto act9nll = aiGraphcore.nllloss({act9, l1}, ReductionType::Sum);

  auto finalLoss = aiOnnx.sum({act6l1, act8l1, act3nll, act9nll});

  auto proto      = builder->getModelProto();
  auto modelProto = io::getModelFromString(proto);

  auto dataFlow = DataFlow(10, {{act4, AnchorReturnType("All")}});

  SessionOptions userOptions;
  userOptions.virtualGraphMode = VirtualGraphMode::Auto;
  userOptions.enablePipelining = true;

  auto optimizer = ConstSGD(0.01);

  auto device = createTestDevice(TEST_TARGET, 3);

  ir.prepare({modelProto,
              InputShapeInfo(),
              dataFlow,
              finalLoss,
              &optimizer,
              *device,
              userOptions,
              Patterns(PatternsLevel::Default)});
}

void prepareIr0(popart::Ir &ir) {

  using namespace popart;

  auto builder     = Builder::create();
  auto aiOnnx      = builder->aiOnnxOpset9();
  auto aiGraphcore = builder->aiGraphcoreOpset1();

  TensorInfo info{"FLOAT", std::vector<int64_t>{2, 2}};

  auto input1 = builder->addInputTensor(info);

  std::vector<float> w0Vals(2 * 2, 1.0f);
  ConstVoidData w0Data = {w0Vals.data(), info};
  auto w0              = builder->addInitializedInputTensor(w0Data);
  auto act0            = aiOnnx.add({w0, input1}, "act0");
  act0                 = aiOnnx.relu({act0});

  std::vector<float> w1Vals(2 * 2, 1.0f);
  ConstVoidData w1Data = {w1Vals.data(), info};
  auto w1              = builder->addInitializedInputTensor(w1Data);
  auto act1            = aiOnnx.add({w1, act0}, "act1");
  act1                 = aiOnnx.relu({act1});

  act1 = aiOnnx.sin({act1});
  act1 = aiOnnx.cos({act1});
  act1 = aiOnnx.mul({act0, act1});
  act1 = aiOnnx.sigmoid({act1});
  act1 = aiOnnx.sin({act1});
  act1 = aiOnnx.sigmoid({act1});

  std::vector<float> w2Vals(2 * 2, 1.0f);
  ConstVoidData w2Data = {w2Vals.data(), info};
  auto w2              = builder->addInitializedInputTensor(w2Data);
  auto act2            = aiOnnx.add({w2, act1}, "act2");
  act2                 = aiOnnx.relu({act2});

  std::vector<float> w3Vals(2 * 2, 1.0f);
  ConstVoidData w3Data = {w3Vals.data(), info};
  auto w3              = builder->addInitializedInputTensor(w3Data);
  auto act3            = aiOnnx.add({w3, act2}, "act3");
  act3                 = aiOnnx.relu({act3});

  std::vector<float> w4Vals(2 * 2, 1.0f);
  ConstVoidData w4Data = {w4Vals.data(), info};
  auto w4              = builder->addInitializedInputTensor(w4Data);
  auto act4            = aiOnnx.add({w4, act3}, "act4");
  act4                 = aiOnnx.relu({act4});

  std::vector<float> w5Vals(2 * 2, 1.0f);
  ConstVoidData w5Data = {w5Vals.data(), info};
  auto w5              = builder->addInitializedInputTensor(w5Data);
  auto act5            = aiOnnx.add({w5, act4}, "act5");
  act5                 = aiOnnx.relu({act5});

  auto l1 = builder->aiGraphcoreOpset1().l1loss({act5}, 0.1);

  auto proto      = builder->getModelProto();
  auto modelProto = io::getModelFromString(proto);

  auto dataFlow = DataFlow(10, {{act0, AnchorReturnType("All")}});

  SessionOptions userOptions;
  userOptions.virtualGraphMode = VirtualGraphMode::Auto;
  userOptions.enablePipelining = true;

  auto optimizer = ConstSGD(0.01);

  auto device = createTestDevice(TEST_TARGET, 3);

  ir.prepare({modelProto,
              InputShapeInfo(),
              dataFlow,
              l1,
              &optimizer,
              *device,
              userOptions,
              Patterns(PatternsLevel::Default)});
}

// We check that the topological constraints on Stash and Restore are satisfied
BOOST_AUTO_TEST_CASE(PipelineTopoConTest0) {

  using namespace popart;

  auto test = [](Ir &ir) {
    auto opSchedule = ir.getOpSchedule({}, RequireOptimalSchedule::Yes);
    for (auto op : opSchedule) {
      std::cout << op->str() << std::endl;
    }

    std::map<int64_t, std::vector<Op *>> schedsPerIPU;
    for (auto op : opSchedule) {
      int64_t id;
      if (op->hasVirtualGraphId()) {
        id = op->getVirtualGraphId();
      } else if (dynamic_cast<IpuCopyOp *>(op)) {
        id = dynamic_cast<IpuCopyOp *>(op)->getSourceIpu();
      } else {
        throw error("failed to determine IpuId for Op {}", op->str());
      }
      auto found = schedsPerIPU.find(id);
      if (found == schedsPerIPU.end()) {
        schedsPerIPU.insert({id, {op}});
      } else {
        found->second.push_back(op);
      }
    }

    for (auto ipu_ops : schedsPerIPU) {
      std::stringstream ss;
      ss << ipu_ops.first << "On IPU : "
         << "\n";
      for (auto op : ipu_ops.second) {
        op->append(ss);
      }
      std::cout << ss.str();
    }

    std::map<Op *, int, POpCmp> schedIndex;
    for (int i = 0; i < opSchedule.size(); ++i) {
      schedIndex.insert({opSchedule[i], i});
    }

    for (auto op : opSchedule) {
      auto opIndex = schedIndex.at(op);

      // If it's a Stash, we check that
      // 1) Stash appears before the corresponding Restore (we
      // confirm there is 1 corresponding Restore)
      // 2) Stash has ScheduledPreLoss::Yes.
      // 3) Consumers of stashed Tensor with PathFromLoss::Yes appear after
      // Stash
      auto stashOp = dynamic_cast<StashOp *>(op);
      if (stashOp) {
        int nRestores = 0;
        for (auto consumer : stashOp->output->tensor(0)->consumers.getOps()) {
          // 1)
          if (dynamic_cast<RestoreOp *>(consumer)) {
            ++nRestores;
            BOOST_CHECK(schedIndex.at(consumer) > opIndex);
          }
        }
        // 1)
        BOOST_CHECK(nRestores == 1);

        // 2)
        BOOST_CHECK(stashOp->scheduledPreLoss == ScheduledPreLoss::Yes);
        for (auto consumer : stashOp->input->tensor(0)->consumers.getOps()) {
          // 3)
          if (consumer->fromLoss == PathFromLoss::Yes) {
            BOOST_CHECK(schedIndex.at(consumer) > opIndex);
          }
        }
      }

      // if it's a Restore Op, we don't check any ordering conditions in the Ir,
      // because the backend is expected to control the scheduling of Restore
      // Ops, which happens later
      auto restoreOp = dynamic_cast<RestoreOp *>(op);
      if (restoreOp) {
        int nStash   = 0;
        auto stash   = restoreOp->input->tensor(restoreOp->getStashInIndex());
        auto stashOp = stash->getProducer();
        auto act     = stashOp->input->tensor(StashOp::getInIndex());
        for (auto consumer : act->consumers.getOps()) {
          if (dynamic_cast<StashOp *>(consumer)) {
            ++nStash;
            BOOST_CHECK(schedIndex.at(consumer) < opIndex);
          }
        }
        BOOST_CHECK(nStash == 1);
      }
    }
  };
  Ir ir0;
  prepareIr0(ir0);
  test(ir0);

  Ir ir1;
  prepareIr1(ir1);
  test(ir1);
}