//
//  main.cpp
//  FormulaEngine
//
//  Created by Matt on 10/10/2015.
//  Copyright © 2015 Matt. All rights reserved.
//

#include "tree.h"
#include <boost/lexical_cast.hpp>
#include <boost/variant/apply_visitor.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/contains.hpp>
#include <boost/utility/enable_if.hpp>

struct StdVisitor
{
    typedef std::string result_type;

    StdVisitor() {}

    std::string operator()(double d) {
        return boost::lexical_cast<std::string>(d);
    }
    std::string operator()(const std::string& s) {
        return s;
    }
    std::string operator()(const binary_op& b) {
        std::string left = boost::apply_visitor(*this, b.left.expr);
        std::string right = boost::apply_visitor(*this, b.right.expr);
        std::stringstream s;
        s << '(' << left << b.op << right << ')';
        return s.str();
    }
    std::string operator()(const unary_op& u) {
        std::stringstream s;
        if(u.op == '-') {
            s << u.op;
        }
        s << boost::apply_visitor(*this, u.subject.expr);
        return s.str();
    }
    std::string operator()(const tree& t) {
        return boost::apply_visitor(*this, t.expr);
    }
};

struct PassthroughVisitor :
public boost::static_visitor<tree>
{
    typedef tree result_type;

    virtual tree operator()(double d) { return d; }

    virtual tree operator()(const std::string& s) { return s; }
    virtual tree operator()(const binary_op& b) {
        tree left = boost::apply_visitor(*this, b.left.expr);
        tree right = boost::apply_visitor(*this, b.right.expr);
        return binary_op(b.op, left, right);
    }
    virtual tree operator()(const unary_op& u) {
        tree subject = boost::apply_visitor(*this, u.subject.expr);
        return unary_op(u.op, subject);
    }
    virtual tree operator()(const tree& t) {
        return boost::apply_visitor(*this, t.expr);
    }
};

struct ModifyingVisitor : public PassthroughVisitor
{
    tree operator()(const tree& t) {
        return boost::apply_visitor(*this, t.expr);
    }

    tree operator()(const std::string& s) {
        return s + ":Field";
    }

};

int main(int argc, const char * argv[]) {

    double x = 10;
    std::string y = "XXX";
    binary_op b('+', x, y);
    unary_op u('-', 2);

    tree t( binary_op('*', u, b) );

    ModifyingVisitor mv;
    tree t2 = mv(t);

    StdVisitor v;
    std::cout << v(t) << std::endl;
    std::cout << v(t2) << std::endl;
    return 0;
}


#include "mesh2.hpp"
#include <boost/python.hpp>
#include <stdio.h>
#include <algorithm>
#include "polynomial.hpp"

namespace gd {

using namespace boost::python;
using namespace boost;
using namespace std;

void py_export_mesh2() {
	//class_<BasisTriMesh1dRegular<> >("BasisTriMesh1dRegular", init<double, double, int>())
	//.def("test", (&BasisTriMesh1dRegular<>::test))
	//.def("testprofile", (&BasisTriMesh1dRegular<>::testprofile))
	//;
	{
		typedef MeshRegularNodal<3, LagrangeBasis<0>> T;
		class_<T>("MeshRegularNodal3dLagrange0", init<double, double, double, double, double, double, int, int, int>())
			//.def("test", (&T::test))
			.def("get_dof", (&T::get_dof))
			.def("get_dof_per_cell", (&T::get_dof_per_cell))
			.def("eval", (&T::eval_))
			.def("dof_index", &T::dof_index)
			.def("basis_uv", &T::basis_uv)
			.def("solve_coordinates", &T::solve_coordinates)
			;
	}
	{
		typedef MeshRegularNodal<3, LagrangeBasis<1>> T;
		class_<T>("MeshRegularNodal3dLagrange1", init<double, double, double, double, double, double, int, int, int>())
			//.def("test", (&T::test))
			.def("get_dof", (&T::get_dof))
			.def("get_dof_per_cell", (&T::get_dof_per_cell))
			.def("eval", (&T::eval_))
			.def("dof_index", &T::dof_index)
			.def("basis_uv", &T::basis_uv)
			.def("solve_coordinates", &T::solve_coordinates)
			;
	}
	{
		typedef MeshRegularNodal<2, LagrangeBasis<0>> T;
		class_<T>("MeshRegularNodal2dLagrange0", init<double, double, double, double, int, int>())
			.def("test", (&T::test))
			.def("get_dof", (&T::get_dof))
			.def("get_dof_per_cell", (&T::get_dof_per_cell))
			.def("eval", (&T::eval_))
			.def("dof_index", &T::dof_index)
			.def("basis_uv", &T::basis_uv)
			.def("solve_coordinates", &T::solve_coordinates)
			;
	}
	{
		typedef MeshRegularNodal<2, LagrangeBasis<1>> T;
		class_<T>("MeshRegularNodal2dLagrange1", init<double, double, double, double, int, int>())
			.def("test", (&T::test))
			.def("get_dof", (&T::get_dof))
			.def("get_dof_per_cell", (&T::get_dof_per_cell))
			.def("eval", (&T::eval_))
			.def("dof_index", &T::dof_index)
			.def("basis_uv", &T::basis_uv)
			.def("solve_coordinates", &T::solve_coordinates)
			;
	}
	{
		typedef MeshRegularNodal<2, LagrangeBasis<2>> T;
		class_<T>("MeshRegularNodal2dLagrange2", init<double, double, double, double, int, int>())
			.def("test", (&T::test))
			.def("get_dof", (&T::get_dof))
			.def("get_dof_per_cell", (&T::get_dof_per_cell))
			.def("eval", (&T::eval_))
			.def("dof_index", &T::dof_index)
			.def("basis_uv", &T::basis_uv)
			.def("solve_coordinates", &T::solve_coordinates)
			;
	}

	{
		typedef MeshRegularNodal<1, LagrangeBasis<0>> T;
		class_<T>("MeshRegularNodal1dLagrange0", init<double, double,  int>())
			.def("get_dof", (&T::get_dof))
			.def("get_dof_per_cell", (&T::get_dof_per_cell))
			.def("eval", (&T::eval_))
			.def("dof_index", &T::dof_index)
			.def("basis_uv", &T::basis_uv)
			.def("solve_coordinates", &T::solve_coordinates)
			;
	}
	{
		typedef MeshRegularNodal<1, LagrangeBasis<1>> T;
		class_<T>("MeshRegularNodal1dLagrange1", init<double, double, int>())
			.def("get_dof", (&T::get_dof))
			.def("get_dof_per_cell", (&T::get_dof_per_cell))
			.def("eval", (&T::eval_))
			.def("dof_index", &T::dof_index)
			.def("basis_uv", &T::basis_uv)
			.def("solve_coordinates", &T::solve_coordinates)
			;
	}
	{
		typedef MeshRegularNodal<1, LagrangeBasis<2>> T;
		class_<T>("MeshRegularNodal1dLagrange2", init<double, double, int>())
			.def("get_dof", (&T::get_dof))
			.def("get_dof_per_cell", (&T::get_dof_per_cell))
			.def("eval", (&T::eval_))
			.def("dof_index", &T::dof_index)
			.def("basis_uv", &T::basis_uv)
			.def("solve_coordinates", &T::solve_coordinates)
			;
	}
/*{
		typedef MeshRegularNodal1d<LagrangeBasis<2>> T;
		class_<T>("MeshRegularNodal1dLagrange2", init<double, double, int, Transformation1d_in_3d*>())
			//.def("test", (&BasisTriMesh1dRegular<>::test))
			.def("testprofile", (&T::testprofile))
			.def("get_dof", (&T::get_dof))
			;
	}
	{
		typedef MeshRegularNodal1d<LagrangeBasis<3>> T;
		class_<T>("MeshRegularNodal1dLagrange3", init<double, double, int, Transformation1d_in_3d*>())
			//.def("test", (&BasisTriMesh1dRegular<>::test))
			.def("testprofile", (&T::testprofile))
			.def("get_dof", (&T::get_dof))
			;
	}*/
}

}


#pragma once
#include <string>
#include "Command.hpp"
#include <boost/serialization/base_object.hpp>
#include <Eigen/Core>
#include <vizkit3d_debug_drawings/commands/BoostSerializationHelpers.hpp>

namespace vizkit3dDebugDrawings
{

/**A command that draws a primitive */
class PlotCommand : public Command
{

    friend class boost::serialization::access;
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version)
    {
        // serialize base class information
        ar & boost::serialization::base_object<Command>(*this);
        ar & dataPoint;
    }

public:
    PlotCommand(const std::string& drawingChannel, const Eigen::Vector2d& dataPoint);
    PlotCommand();

    virtual void execute(DrawingManager* drawingManager) const;

    virtual PlotCommand* clone() const;

    virtual ~PlotCommand();

private:
    Eigen::Vector2d dataPoint;
};
}


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

#include <../random_util.hpp>
#include <boost/test/unit_test.hpp>
#include <filereader.hpp>
#include <popart/builder.hpp>
#include <popart/dataflow.hpp>
#include <popart/devicemanager.hpp>
#include <popart/inputshapeinfo.hpp>
#include <popart/ndarraywrapper.hpp>
#include <popart/session.hpp>
#include <popart/tensorinfo.hpp>
#include <popart/tensornames.hpp>
#include <popart/testdevice.hpp>

#include <vector>

BOOST_AUTO_TEST_CASE(Basic0TopK_Opset9) {

  using namespace popart;

  // generate random input data
  int seed = 1013;
  DefaultRandomEngine eng(seed);
  UniformRealDistribution<float> fdis(-4.f, +4.f);

  // prepare to build an onnx model
  auto builder = Builder::create();
  auto aiOnnx  = builder->aiOnnxOpset9();

  // input tensor X
  // The input tensor will be rank 3, D0 x D1 x D2
  int D0 = 3;
  int D1 = 11;
  int D2 = 7;
  TensorInfo xInfo{"FLOAT", std::vector<int64_t>{D0, D1, D2}};
  TensorId xId = builder->addInputTensor(xInfo);
  std::vector<float> vXData(xInfo.nelms());
  for (auto &val : vXData) {
    val = fdis(eng);
  }

  // We will perform top-k on axis "axis" and so the output will
  // be of size D0 x top_k x D2, where top_k <= D1.
  int axis  = 1;
  int top_k = 4;

  Shape outShape = xInfo.shape();
  outShape[axis] = top_k;
  TensorInfo outValuesInfo{xInfo.dataType(), outShape};

  // Prepare the baseline data, perform D0*D2 sorts of vectors of size D1
  std::vector<int> expectedOutputIndices(outValuesInfo.nelms(), -1);
  std::vector<float> expectedOutputValues(outValuesInfo.nelms(), -1.0f);
  for (int i = 0; i < D0; ++i) {
    for (int j = 0; j < D2; ++j) {
      std::vector<std::tuple<float, int>> oneD2Sort(D1);
      for (int k_ = 0; k_ < D1; ++k_) {
        oneD2Sort[k_] =
            std::tuple<float, int>(vXData[i * D1 * D2 + D2 * k_ + j], k_);
      }
      // sort largest to smallest
      std::sort(oneD2Sort.rbegin(), oneD2Sort.rend());
      for (int k_ = 0; k_ < top_k; ++k_) {
        expectedOutputIndices[i * top_k * D2 + D2 * k_ + j] =
            std::get<1>(oneD2Sort[k_]);
        expectedOutputValues[i * top_k * D2 + D2 * k_ + j] =
            std::get<0>(oneD2Sort[k_]);
      }
    }
  }

  auto topkOut = aiOnnx.topk({xId}, top_k, axis);

  // Top-K has 2 oututs, the indices and values, both of size D0 x top_k x D1
  BOOST_CHECK(topkOut.size() == 2);
  auto values  = topkOut[0];
  auto indices = topkOut[1];

  builder->addOutputTensor(values);
  builder->addOutputTensor(indices);

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

  // create the IR
  auto art      = AnchorReturnType("All");
  auto dataFlow = DataFlow(1, {{values, art}, {indices, art}});

  auto device = popart::createTestDevice(TEST_TARGET);

  auto opts = SessionOptions();

  auto session = popart::InferenceSession::createFromOnnxModel(
      proto,
      dataFlow,
      device,
      popart::InputShapeInfo(),
      opts,
      popart::Patterns(PatternsLevel::NoPatterns).enableRuntimeAsserts(false));

  // prepare the anchors
  std::vector<float> rawOutputValues(outValuesInfo.nelms());
  std::vector<int> rawOutputIndices(outValuesInfo.nelms());
  popart::NDArrayWrapper<float> outValues(rawOutputValues.data(), outShape);
  popart::NDArrayWrapper<int> outIndices(rawOutputIndices.data(), outShape);

  std::map<popart::TensorId, popart::IArray &> anchors = {
      {values, outValues}, {indices, outIndices}};

  session->prepareDevice();

  popart::NDArrayWrapper<float> xData(vXData.data(), xInfo);
  std::map<popart::TensorId, popart::IArray &> inputs = {{xId, xData}};

  popart::StepIO stepio(inputs, anchors);

  session->run(stepio);

  // confirm the indices agree
  BOOST_CHECK_EQUAL_COLLECTIONS(rawOutputIndices.begin(),
                                rawOutputIndices.end(),
                                expectedOutputIndices.begin(),
                                expectedOutputIndices.end());
  // confirm the values agree
  float sumAbsDiff{0.0f};
  for (int i = 0; i < expectedOutputIndices.size(); ++i) {
    sumAbsDiff += std::abs(rawOutputValues[i] - expectedOutputValues[i]);
  }
  BOOST_CHECK(sumAbsDiff < 1e-9f);
}

BOOST_AUTO_TEST_CASE(Basic0TopK_Opset10) {

  using namespace popart;

  // generate random input data
  int seed = 1013;
  DefaultRandomEngine eng(seed);
  UniformRealDistribution<float> fdis(-4.f, +4.f);

  // prepare to build an onnx model
  auto builder = Builder::create();
  auto aiOnnx  = builder->aiOnnxOpset10();

  // input tensor X
  // The input tensor will be rank 3, D0 x D1 x D2
  int D0 = 3;
  int D1 = 11;
  int D2 = 7;
  TensorInfo xInfo{"FLOAT", std::vector<int64_t>{D0, D1, D2}};
  TensorId xId = builder->addInputTensor(xInfo);

  int top_k = 4;
  TensorInfo kShape{"INT64", std::vector<int64_t>{1}};
  int64_t kVals[1]    = {top_k};
  ConstVoidData kData = {kVals, kShape};

  std::vector<float> vXData(xInfo.nelms());
  for (auto &val : vXData) {
    val = fdis(eng);
  }

  // We will perform top-k on axis "axis" and so the output will
  // be of size D0 x top_k x D2, where top_k <= D1.
  int axis = 1;

  Shape outShape = xInfo.shape();
  outShape[axis] = top_k;
  TensorInfo outValuesInfo{xInfo.dataType(), outShape};

  // Prepare the baseline data, perform D0*D2 sorts of vectors of size D1
  std::vector<int> expectedOutputIndices(outValuesInfo.nelms(), -1);
  std::vector<float> expectedOutputValues(outValuesInfo.nelms(), -1.0f);
  for (int i = 0; i < D0; ++i) {
    for (int j = 0; j < D2; ++j) {
      std::vector<std::tuple<float, int>> oneD2Sort(D1);
      for (int k_ = 0; k_ < D1; ++k_) {
        oneD2Sort[k_] =
            std::tuple<float, int>(vXData[i * D1 * D2 + D2 * k_ + j], k_);
      }
      // sort largest to smallest
      std::sort(oneD2Sort.rbegin(), oneD2Sort.rend());
      for (int k_ = 0; k_ < top_k; ++k_) {
        expectedOutputIndices[i * top_k * D2 + D2 * k_ + j] =
            std::get<1>(oneD2Sort[k_]);
        expectedOutputValues[i * top_k * D2 + D2 * k_ + j] =
            std::get<0>(oneD2Sort[k_]);
      }
    }
  }

  auto kId     = aiOnnx.constant(kData);
  auto topkOut = aiOnnx.topk({xId, kId}, axis);

  // Top-K has 2 oututs, the indices and values, both of size D0 x top_k x D1
  BOOST_CHECK(topkOut.size() == 2);
  auto values  = topkOut[0];
  auto indices = topkOut[1];

  builder->addOutputTensor(values);
  builder->addOutputTensor(indices);

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

  // create the IR
  auto art      = AnchorReturnType("All");
  auto dataFlow = DataFlow(1, {{values, art}, {indices, art}});

  auto device = popart::createTestDevice(TEST_TARGET);

  auto opts = SessionOptions();

  auto session = popart::InferenceSession::createFromOnnxModel(
      proto,
      dataFlow,
      device,
      popart::InputShapeInfo(),
      opts,
      popart::Patterns(PatternsLevel::NoPatterns).enableRuntimeAsserts(false));

  // prepare the anchors
  std::vector<float> rawOutputValues(outValuesInfo.nelms());
  std::vector<int> rawOutputIndices(outValuesInfo.nelms());
  popart::NDArrayWrapper<float> outValues(rawOutputValues.data(), outShape);
  popart::NDArrayWrapper<int> outIndices(rawOutputIndices.data(), outShape);

  std::map<popart::TensorId, popart::IArray &> anchors = {
      {values, outValues}, {indices, outIndices}};

  session->prepareDevice();

  popart::NDArrayWrapper<float> xData(vXData.data(), xInfo);
  std::map<popart::TensorId, popart::IArray &> inputs = {{xId, xData}};

  popart::StepIO stepio(inputs, anchors);

  session->run(stepio);

  // confirm the indices agree
  BOOST_CHECK_EQUAL_COLLECTIONS(rawOutputIndices.begin(),
                                rawOutputIndices.end(),
                                expectedOutputIndices.begin(),
                                expectedOutputIndices.end());
  // confirm the values agree
  float sumAbsDiff{0.0f};
  for (int i = 0; i < expectedOutputIndices.size(); ++i) {
    sumAbsDiff += std::abs(rawOutputValues[i] - expectedOutputValues[i]);
  }
  BOOST_CHECK(sumAbsDiff < 1e-9f);
}


#ifndef MPLLIBS_METAPARSE_V1_UTIL_IN_RANGE_HPP
#define MPLLIBS_METAPARSE_V1_UTIL_IN_RANGE_HPP

// Copyright Abel Sinkovics (abel@sinkovics.hu)  2009 - 2010.
// Distributed under the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE_1_0.txt or copy at
//          http://www.boost.org/LICENSE_1_0.txt)

#include <boost/mpl/less_equal.hpp>
#include <boost/mpl/comparison.hpp>
#include <boost/mpl/and.hpp>
#include <boost/mpl/apply.hpp>
#include <boost/mpl/quote.hpp>

namespace mpllibs
{
  namespace metaparse
  {
    namespace v1
    {
      namespace util
      {
        template <class LowerBound, class UpperBound>
        struct in_range
        {
          typedef in_range type;

          template <class Item>
          struct apply :
            boost::mpl::and_<
              boost::mpl::less_equal<LowerBound, Item>,
              boost::mpl::less_equal<Item, UpperBound>
            >
          {};
        };
      }
    }
  }
}

#endif


//=======================================================================
// Copyright 2001 Indiana University.
// Author: Jeremy G. Siek
//
// This file is part of the Boost Graph Library
//
// You should have received a copy of the License Agreement for the
// Boost Graph Library along with the software; see the file LICENSE.
//
// Permission to modify the code and to distribute modified code is
// granted, provided the text of this NOTICE is retained, a notice that
// the code was modified is included with the above COPYRIGHT NOTICE and
// with the COPYRIGHT NOTICE in the LICENSE file, and that the LICENSE
// file is distributed with the modified code.
//
// LICENSOR MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED.
// By way of example, but not limitation, Licensor MAKES NO
// REPRESENTATIONS OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY
// PARTICULAR PURPOSE OR THAT THE USE OF THE LICENSED SOFTWARE COMPONENTS
// OR DOCUMENTATION WILL NOT INFRINGE ANY PATENTS, COPYRIGHTS, TRADEMARKS
// OR OTHER RIGHTS.
//=======================================================================

#include <boost/config.hpp>
#include <boost/graph/adjacency_list.hpp>

int
main()
{
  using namespace boost;
  typedef adjacency_list<vecS, vecS, bidirectionalS, no_property,
    property<int, edge_weight_t>, no_property, vecS> Graph;

  const std::size_t n = 3;
  typedef std::pair<std::size_t, std::size_t> E;
  E edge_array[] = { E(0,1), E(0,2), E(0,1) };
  const std::size_t m = sizeof(edge_array) / sizeof(E);
  Graph g(edge_array, edge_array + m, n);
  for (std::size_t i = 0; i < m; ++i)
    std::cout << edges(g).first[i] << " ";
  std::cout << std::endl;

  return 0;
}


// Copyright 2015-2020 The ALMA Project Developers
//
// 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
/// Test the wrapper around spglib.

#include <iostream>
#include <boost/filesystem.hpp>
#include "gtest/gtest.h"
#include <cmakevars.hpp>
#include <constants.hpp>
#include <vasp_io.hpp>
#include <symmetry.hpp>

TEST(symmetry_test, Si_test) {
    auto basedir = boost::filesystem::path(TEST_RESOURCE_DIR);
    auto si_dir = basedir / boost::filesystem::path("Si");
    auto poscar_path = si_dir / boost::filesystem::path("POSCAR");
    auto poscar = alma::load_POSCAR(poscar_path.string().c_str());
    auto symops = alma::Symmetry_operations(*poscar);

    EXPECT_EQ(227, symops.get_spacegroup_number());
    EXPECT_EQ("Fd-3m", symops.get_spacegroup_symbol());
    EXPECT_EQ(48u, symops.get_nsym());
    EXPECT_EQ("bb", symops.get_wyckoff());
    auto equivalences = symops.get_equivalences();
    EXPECT_EQ(0, equivalences[0]);
    EXPECT_EQ(0, equivalences[1]);

    // Since operation 0 is always the identity,
    // it must leave any vector untouched.
    Eigen::Vector3d vector;
    vector.setRandom();
    auto transformed = symops.transform_v(vector, 0);
    auto ctransformed = symops.transform_v(vector, 0, true);
    auto rotated = symops.rotate_v(vector, 0);
    auto crotated = symops.rotate_v(vector, 0, true);

    for (auto i = 0; i < 3; ++i) {
        EXPECT_NEAR(vector(i), transformed(i), 1e-8);
        EXPECT_NEAR(vector(i), ctransformed(i), 1e-8);
        EXPECT_NEAR(vector(i), rotated(i), 1e-8);
        EXPECT_NEAR(vector(i), crotated(i), 1e-8);
    }
}

TEST(symmetry_test, InAs_test) {
    auto basedir = boost::filesystem::path(TEST_RESOURCE_DIR);
    auto inas_dir = basedir / boost::filesystem::path("InAs");
    auto poscar_path = inas_dir / boost::filesystem::path("POSCAR");
    auto poscar = alma::load_POSCAR(poscar_path.string().c_str());
    auto symops = alma::Symmetry_operations(*poscar);

    EXPECT_EQ(216, symops.get_spacegroup_number());
    EXPECT_EQ("F-43m", symops.get_spacegroup_symbol());
    EXPECT_EQ(24u, symops.get_nsym());
    EXPECT_EQ("ad", symops.get_wyckoff());
    auto equivalences = symops.get_equivalences();
    EXPECT_EQ(0, equivalences[0]);
    EXPECT_EQ(1, equivalences[1]);

    Eigen::Vector3d vector;
    vector.setRandom();
    auto transformed = symops.transform_v(vector, 0);
    auto ctransformed = symops.transform_v(vector, 0, true);
    auto rotated = symops.rotate_v(vector, 0);
    auto crotated = symops.rotate_v(vector, 0, true);

    for (auto i = 0; i < 3; ++i) {
        EXPECT_NEAR(vector(i), transformed(i), 1e-8);
        EXPECT_NEAR(vector(i), ctransformed(i), 1e-8);
        EXPECT_NEAR(vector(i), rotated(i), 1e-8);
        EXPECT_NEAR(vector(i), crotated(i), 1e-8);
    }
}

TEST(symmetry_test, Bi2O3_test) {
    auto basedir = boost::filesystem::path(TEST_RESOURCE_DIR);
    auto bi2o3_dir = basedir / boost::filesystem::path("Bi2O3");
    auto poscar_path = bi2o3_dir / boost::filesystem::path("POSCAR");
    auto poscar = alma::load_POSCAR(poscar_path.string().c_str());
    auto symops = alma::Symmetry_operations(*poscar);

    EXPECT_EQ(14, symops.get_spacegroup_number());
    EXPECT_EQ("P2_1/c", symops.get_spacegroup_symbol());
    EXPECT_EQ(4u, symops.get_nsym());

    for (auto& i : symops.get_wyckoff())
        EXPECT_EQ('e', i);
    constexpr int ref_equivalences[] = {1, 1, 1,  1,  5,  5,  5,  5,  9,  9,
                                        9, 9, 13, 13, 13, 13, 17, 17, 17, 17};
    auto equivalences = symops.get_equivalences();

    for (decltype(equivalences.size()) i = 0; i < equivalences.size(); ++i)
        EXPECT_EQ(ref_equivalences[i] - 1, equivalences[i]);

    Eigen::Vector3d vector;
    vector.setRandom();
    auto transformed = symops.transform_v(vector, 0);
    auto ctransformed = symops.transform_v(vector, 0, true);
    auto rotated = symops.rotate_v(vector, 0);
    auto crotated = symops.rotate_v(vector, 0, true);

    for (auto i = 0; i < 3; ++i) {
        EXPECT_NEAR(vector(i), transformed(i), 1e-8);
        EXPECT_NEAR(vector(i), ctransformed(i), 1e-8);
        EXPECT_NEAR(vector(i), rotated(i), 1e-8);
        EXPECT_NEAR(vector(i), crotated(i), 1e-8);
    }
}

TEST(symmetry_test, borophene_test) {
    auto basedir = boost::filesystem::path(TEST_RESOURCE_DIR);
    auto b_dir = basedir / boost::filesystem::path("B-Pmmn");
    auto poscar_path = b_dir / boost::filesystem::path("POSCAR");
    auto poscar = alma::load_POSCAR(poscar_path.string().c_str());
    auto symops = alma::Symmetry_operations(*poscar);

    EXPECT_EQ(59, symops.get_spacegroup_number());
    EXPECT_EQ("Pmmn", symops.get_spacegroup_symbol());
    EXPECT_EQ(8u, symops.get_nsym());
    EXPECT_EQ("ffeeefef", symops.get_wyckoff());
    constexpr int ref_equivalences[] = {1, 1, 3, 3, 3, 1, 3, 1};
    auto equivalences = symops.get_equivalences();

    for (decltype(equivalences.size()) i = 0; i < equivalences.size(); ++i)
        EXPECT_EQ(ref_equivalences[i] - 1, equivalences[i]);

    Eigen::Vector3d vector;
    vector.setRandom();
    auto transformed = symops.transform_v(vector, 0);
    auto ctransformed = symops.transform_v(vector, 0, true);
    auto rotated = symops.rotate_v(vector, 0);
    auto crotated = symops.rotate_v(vector, 0, true);

    for (auto i = 0; i < 3; ++i) {
        EXPECT_NEAR(vector(i), transformed(i), 1e-8);
        EXPECT_NEAR(vector(i), ctransformed(i), 1e-8);
        EXPECT_NEAR(vector(i), rotated(i), 1e-8);
        EXPECT_NEAR(vector(i), crotated(i), 1e-8);
    }
}


//---------------------------------------------------------------------------//
// Copyright (c) 2013 Kyle Lutz <kyle.r.lutz@gmail.com>
//
// Distributed under the Boost Software License, Version 1.0
// See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt
//
// See http://boostorg.github.com/compute for more information.
//---------------------------------------------------------------------------//

#define BOOST_TEST_MODULE TestComplex
#include <boost/test/unit_test.hpp>

#include <boost/compute/algorithm/copy.hpp>
#include <boost/compute/algorithm/fill.hpp>
#include <boost/compute/algorithm/transform.hpp>
#include <boost/compute/container/vector.hpp>
#include <boost/compute/types/complex.hpp>
#include <boost/compute/type_traits/type_name.hpp>

#include "context_setup.hpp"

// copies a vector of complex<float>'s on the host to the device
BOOST_AUTO_TEST_CASE(copy_complex_vector)
{
    std::vector<std::complex<float> > host_vector;
    host_vector.push_back(std::complex<float>(1.0f, 2.0f));
    host_vector.push_back(std::complex<float>(-2.0f, 1.0f));
    host_vector.push_back(std::complex<float>(1.0f, -2.0f));
    host_vector.push_back(std::complex<float>(-2.0f, -1.0f));

    boost::compute::vector<std::complex<float> > device_vector(context);
    boost::compute::copy(
        host_vector.begin(),
        host_vector.end(),
        device_vector.begin(),
        queue
    );
    queue.finish();
    BOOST_CHECK_EQUAL(std::complex<float>(device_vector[0]), std::complex<float>(1.0f, 2.0f));
    BOOST_CHECK_EQUAL(std::complex<float>(device_vector[1]), std::complex<float>(-2.0f, 1.0f));
    BOOST_CHECK_EQUAL(std::complex<float>(device_vector[2]), std::complex<float>(1.0f, -2.0f));
    BOOST_CHECK_EQUAL(std::complex<float>(device_vector[3]), std::complex<float>(-2.0f, -1.0f));
}

// fills a vector of complex<float>'s on the device with a constant value
BOOST_AUTO_TEST_CASE(fill_complex_vector)
{
    boost::compute::vector<std::complex<float> > vector(6, context);
    boost::compute::fill(
        vector.begin(),
        vector.end(),
        std::complex<float>(2.0f, 5.0f),
        queue
    );
    queue.finish();
    BOOST_CHECK_EQUAL(std::complex<float>(vector[0]), std::complex<float>(2.0f, 5.0f));
    BOOST_CHECK_EQUAL(std::complex<float>(vector[1]), std::complex<float>(2.0f, 5.0f));
    BOOST_CHECK_EQUAL(std::complex<float>(vector[2]), std::complex<float>(2.0f, 5.0f));
    BOOST_CHECK_EQUAL(std::complex<float>(vector[3]), std::complex<float>(2.0f, 5.0f));
    BOOST_CHECK_EQUAL(std::complex<float>(vector[4]), std::complex<float>(2.0f, 5.0f));
    BOOST_CHECK_EQUAL(std::complex<float>(vector[5]), std::complex<float>(2.0f, 5.0f));
}

// extracts the real and imag components of a vector of complex<float>'s using
// transform with the real() and imag() functions
BOOST_AUTO_TEST_CASE(extract_real_and_imag)
{
    boost::compute::vector<std::complex<float> > vector(context);
    vector.push_back(std::complex<float>(1.0f, 3.0f), queue);
    vector.push_back(std::complex<float>(3.0f, 1.0f), queue);
    vector.push_back(std::complex<float>(5.0f, -1.0f), queue);
    vector.push_back(std::complex<float>(7.0f, -3.0f), queue);
    vector.push_back(std::complex<float>(9.0f, -5.0f), queue);
    BOOST_CHECK_EQUAL(vector.size(), size_t(5));

    boost::compute::vector<float> reals(5, context);
    boost::compute::transform(
        vector.begin(),
        vector.end(),
        reals.begin(),
        boost::compute::real<float>(),
        queue
    );
    queue.finish();
    BOOST_CHECK_EQUAL(float(reals[0]), float(1.0f));
    BOOST_CHECK_EQUAL(float(reals[1]), float(3.0f));
    BOOST_CHECK_EQUAL(float(reals[2]), float(5.0f));
    BOOST_CHECK_EQUAL(float(reals[3]), float(7.0f));
    BOOST_CHECK_EQUAL(float(reals[4]), float(9.0f));

    boost::compute::vector<float> imags(5, context);
    boost::compute::transform(
        vector.begin(),
        vector.end(),
        imags.begin(),
        boost::compute::imag<float>(),
        queue
    );
    queue.finish();
    BOOST_CHECK_EQUAL(float(imags[0]), float(3.0f));
    BOOST_CHECK_EQUAL(float(imags[1]), float(1.0f));
    BOOST_CHECK_EQUAL(float(imags[2]), float(-1.0f));
    BOOST_CHECK_EQUAL(float(imags[3]), float(-3.0f));
    BOOST_CHECK_EQUAL(float(imags[4]), float(-5.0f));
}

// compute the complex conjugate of a vector of complex<float>'s
BOOST_AUTO_TEST_CASE(complex_conj)
{
    boost::compute::vector<std::complex<float> > input(context);
    input.push_back(std::complex<float>(1.0f, 3.0f), queue);
    input.push_back(std::complex<float>(3.0f, 1.0f), queue);
    input.push_back(std::complex<float>(5.0f, -1.0f), queue);
    input.push_back(std::complex<float>(7.0f, -3.0f), queue);
    input.push_back(std::complex<float>(9.0f, -5.0f), queue);
    BOOST_CHECK_EQUAL(input.size(), size_t(5));

    boost::compute::vector<std::complex<float> > output(5, context);
    boost::compute::transform(
        input.begin(),
        input.end(),
        output.begin(),
        boost::compute::conj<float>(),
        queue
    );
    queue.finish();
    BOOST_CHECK_EQUAL(std::complex<float>(output[0]), std::complex<float>(1.0f, -3.0f));
    BOOST_CHECK_EQUAL(std::complex<float>(output[1]), std::complex<float>(3.0f, -1.0f));
    BOOST_CHECK_EQUAL(std::complex<float>(output[2]), std::complex<float>(5.0f, 1.0f));
    BOOST_CHECK_EQUAL(std::complex<float>(output[3]), std::complex<float>(7.0f, 3.0f));
    BOOST_CHECK_EQUAL(std::complex<float>(output[4]), std::complex<float>(9.0f, 5.0f));
}

// check type_name() for std::complex
BOOST_AUTO_TEST_CASE(complex_type_name)
{
    BOOST_CHECK(
        std::strcmp(
            boost::compute::type_name<std::complex<float> >(),
            "float2"
        ) == 0
    );
}

BOOST_AUTO_TEST_CASE(transform_multiply)
{
    boost::compute::vector<std::complex<float> > x(context);
    x.push_back(std::complex<float>(1.0f, 2.0f), queue);
    x.push_back(std::complex<float>(-2.0f, 5.0f), queue);

    boost::compute::vector<std::complex<float> > y(context);
    y.push_back(std::complex<float>(3.0f, 4.0f), queue);
    y.push_back(std::complex<float>(2.0f, -1.0f), queue);

    boost::compute::vector<std::complex<float> > z(2, context);

    // z = x * y
    boost::compute::transform(
        x.begin(),
        x.end(),
        y.begin(),
        z.begin(),
        boost::compute::multiplies<std::complex<float> >(),
        queue
    );
    queue.finish();

    BOOST_CHECK_EQUAL(std::complex<float>(z[0]), std::complex<float>(-5.0f, 10.0f));
    BOOST_CHECK_EQUAL(std::complex<float>(z[1]), std::complex<float>(1.0f, 12.0f));
}

BOOST_AUTO_TEST_SUITE_END()


/******************************************************************************
 * Author:   Laurent Kneip                                                    *
 * Contact:  kneip.laurent@gmail.com                                          *
 * License:  Copyright (c) 2013 Laurent Kneip, ANU. All rights reserved.      *
 *                                                                            *
 * Redistribution and use in source and binary forms, with or without         *
 * modification, are permitted provided that the following conditions         *
 * are met:                                                                   *
 * * Redistributions of source code must retain the above copyright           *
 *   notice, this list of conditions and the following disclaimer.            *
 * * Redistributions in binary form must reproduce the above copyright        *
 *   notice, this list of conditions and the following disclaimer in the      *
 *   documentation and/or other materials provided with the distribution.     *
 * * Neither the name of ANU nor the names of its contributors may be         *
 *   used to endorse or promote products derived from this software without   *
 *   specific prior written permission.                                       *
 *                                                                            *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"*
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE  *
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE *
 * ARE DISCLAIMED. IN NO EVENT SHALL ANU OR THE CONTRIBUTORS BE LIABLE        *
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL *
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER *
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT         *
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY  *
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF     *
 * SUCH DAMAGE.                                                               *
 ******************************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <iomanip>
#include <limits.h>
#include <Eigen/Eigen>
#include <opengv/absolute_pose/methods.hpp>
#include <opengv/absolute_pose/NoncentralAbsoluteMultiAdapter.hpp>
#include <opengv/sac/MultiRansac.hpp>
#include <opengv/sac_problems/absolute_pose/MultiNoncentralAbsolutePoseSacProblem.hpp>
#include <sstream>
#include <fstream>

#include "random_generators.hpp"
#include "experiment_helpers.hpp"
#include "time_measurement.hpp"

using namespace std;
using namespace Eigen;
using namespace opengv;

int main( int argc, char** argv )
{
  //initialize random seed
  initializeRandomSeed();

  //set experiment parameters
  double noise = 0.5;
  double outlierFraction = 0.1;
  size_t pointsPerCam = 25;
  int numberCameras = 4;

  //create a random viewpoint pose
  translation_t position = generateRandomTranslation(2.0);
  rotation_t rotation = generateRandomRotation(0.5);

  //create a random camera-system
  translations_t camOffsets;
  rotations_t camRotations;
  generateRandomCameraSystem( numberCameras, camOffsets, camRotations );

  //derive correspondences based on random point-cloud
  std::vector<std::shared_ptr<points_t> > multiPoints;
  std::vector<std::shared_ptr<bearingVectors_t> > multiBearingVectors;
  std::vector< std::shared_ptr<Eigen::MatrixXd> > gt;
  generateMulti2D3DCorrespondences(
      position, rotation, camOffsets, camRotations,
      pointsPerCam, noise, outlierFraction,
      multiPoints, multiBearingVectors, gt );

  //print the experiment characteristics
  printExperimentCharacteristics(
      position, rotation, noise, outlierFraction );

  //create a non-central absolute adapter
  absolute_pose::NoncentralAbsoluteMultiAdapter adapter(
      multiBearingVectors,
      multiPoints,
      camOffsets,
      camRotations );

  //Create a AbsolutePoseSacProblem and Ransac
  //The method is set to GP3P
  sac::MultiRansac<
      sac_problems::absolute_pose::MultiNoncentralAbsolutePoseSacProblem> ransac;
  std::shared_ptr<
      sac_problems::absolute_pose::MultiNoncentralAbsolutePoseSacProblem> absposeproblem_ptr(
      new sac_problems::absolute_pose::MultiNoncentralAbsolutePoseSacProblem(
      adapter ));

  ransac.sac_model_ = absposeproblem_ptr;
  ransac.threshold_ = 1.0 - cos(atan(sqrt(2.0)*0.5/800.0));
  ransac.max_iterations_ = 50;

  //Run the experiment
  struct timeval tic;
  struct timeval toc;
  gettimeofday( &tic, 0 );
  ransac.computeModel();
  gettimeofday( &toc, 0 );
  double ransac_time = TIMETODOUBLE(timeval_minus(toc,tic));

  //print the results
  std::cout << "the ransac results is: " << std::endl;
  std::cout << ransac.model_coefficients_ << std::endl << std::endl;
  std::cout << "Ransac needed " << ransac.iterations_ << " iterations and ";
  std::cout << ransac_time << " seconds" << std::endl << std::endl;
  size_t numberInliers = 0;
  for(size_t i = 0; i < ransac.inliers_.size(); i++)
    numberInliers += ransac.inliers_[i].size();
  std::cout << "the number of inliers is: " << numberInliers;
  std::cout << std::endl << std::endl;
  std::cout << "the found inliers are: " << std::endl;
  for(size_t i = 0; i < ransac.inliers_.size(); i++)
  {
    for(size_t j = 0; j < ransac.inliers_[i].size(); j++)
      std::cout << ransac.inliers_[i][j] << " ";
  }
  std::cout << std::endl << std::endl;
}


#include <string>
#include <vector>
#include <iostream>
#include <fstream>
#include <boost/test/unit_test.hpp>
#include <idmlib/tdt/macd_histogram.h>
#include <idmlib/tdt/ema.h>

BOOST_AUTO_TEST_SUITE( t_macd )

using namespace idmlib::tdt;

BOOST_AUTO_TEST_CASE(init_test )
{
    std::cout<<"print macd"<<std::endl;
    MacdHistogram<4,8,5> macd;
    macd.Print();
    std::cout<<"print macd finished"<<std::endl;
}

BOOST_AUTO_TEST_CASE(ema_print )
{
    std::cout<<"print ema"<<std::endl;
    Ema<5> ema;
    ema.Print();
    std::cout<<"print ema finished"<<std::endl;
}

BOOST_AUTO_TEST_SUITE_END()


/*
  gpcxx/generate/uniform_symbol_erc.hpp

  Copyright 2013 Karsten Ahnert

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

#ifndef GPCXX_GENERATE_UNIFORM_SYMBOL_ERC_HPP_DEFINED
#define GPCXX_GENERATE_UNIFORM_SYMBOL_ERC_HPP_DEFINED

#include <gpcxx/util/assert.hpp>

#include <boost/variant.hpp>

#include <random>

namespace gpcxx {

template< typename Result , typename ErcDist >
struct uniform_symbol_erc
{
    using erc_dist_type = ErcDist;
    using result_type   = Result;

    template< typename Symbols >
    uniform_symbol_erc( Symbols const& symbols , double prob_fraction_erc , erc_dist_type const& erc_dist )
    : m_symbols( symbols.begin() , symbols.end() ) , m_prob_fraction_erc( prob_fraction_erc ) , m_erc_dist( erc_dist )
    {
        GPCXX_ASSERT( !m_symbols.empty() );
        GPCXX_ASSERT( m_prob_fraction_erc > 0.0 );
    }

    template< typename Rng >
    result_type operator()( Rng &rng )
    {
        std::discrete_distribution< size_t > dist( { 1.0 , m_prob_fraction_erc } );
        if( dist( rng ) == 0 )
        {
            return random_symbol( rng );
        }
        else
        {
            return erc( rng );
        }
    }

    template< typename Rng >
    result_type random_symbol( Rng &rng ) const
    {
        GPCXX_ASSERT( !m_symbols.empty() );
        std::uniform_int_distribution< size_t > dist( 0 , m_symbols.size() - 1 );
        return m_symbols[ dist( rng ) ];
    }

    template< typename Rng >
    result_type erc( Rng &rng )
    {
        return m_erc_dist( rng );
    }

    size_t num_symbols( void ) const { return m_symbols.size(); }

private:

    std::vector< result_type > m_symbols;
    double m_prob_fraction_erc;
    erc_dist_type m_erc_dist;
};

template< typename Result , typename ErcDist , typename Symbols >
uniform_symbol_erc< Result , ErcDist >
make_uniform_symbol_erc(
    Symbols const& symbols ,
    double prob_fraction_erc ,
    ErcDist const& erc_dist )
{
    return uniform_symbol_erc< Result , ErcDist >( symbols , prob_fraction_erc , erc_dist );
}

} // namespace gpcxx

#endif // GPCXX_GENERATE_UNIFORM_SYMBOL_ERC_HPP_DEFINED


#ifndef PHOTOMETRIC_AUGMENTATION_LAYER_HPP_
#define PHOTOMETRIC_AUGMENTATION_LAYER_HPP_

#include <boost/shared_ptr.hpp>
#include <gflags/gflags.h>
#include <glog/logging.h>

#include <cmath>

#include "caffe/blob.hpp"
#include "caffe/common.hpp"
#include "caffe/layer.hpp"
#include "caffe/proto/caffe.pb.h"

namespace caffe {

/*
notes

gaussian noise, sigma uniformly sampled from 0-0.4
contrast: [-0.8, 0.4]
multiplicative color changes: [0.05,2]
gamma: [0.7 1.5]
additive brightness: gaussian sigma 0.2

*/

template <typename Dtype>
class PhotometricAugmentationLayer: public Layer<Dtype> {

public:
	explicit PhotometricAugmentationLayer(const LayerParameter& param): Layer<Dtype>(param) {      }
	~PhotometricAugmentationLayer();

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

	virtual inline const char* type() const { return "PhotometricAugmentation"; }
	virtual inline int MinBottomBlobs() const { return 1; }
	virtual inline int MinTopBlobs() const { return 1; }
	virtual inline bool EqualNumBottomTopBlobs() const { return true; }

protected:
	virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom,
      const vector<Blob<Dtype>*>& top);
	virtual void Forward_gpu(const vector<Blob<Dtype>*>& bottom,
      const vector<Blob<Dtype>*>& top);
	virtual void Backward_cpu(const vector<Blob<Dtype>*>& top,
      const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom);
	virtual void Backward_gpu(const vector<Blob<Dtype>*>& top,
      const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom);

private:
	bool hasMeans;
	Blob<Dtype> means;

	vector<Blob<Dtype>*> noise;
	Blob<Dtype> contrast;
	Blob<Dtype> colour;
	Blob<Dtype> gamma;
	Blob<Dtype> brightness;
};

}  // namespace caffe

#endif  // CAFFE_COMMON_HPP_


//==================================================================================================
/*!

  Copyright 2016 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
#include <boost/simd/function/simd/make.hpp>
#include <boost/simd/logical.hpp>
#include <boost/simd/pack.hpp>
#include <algorithm>
#include <simd_test.hpp>

#if !(defined(BOOST_INTEL_CXX_VERSION) && BOOST_INTEL_CXX_VERSION < 1600)
// Unless variadic template support sucks big time, these must be tested

STF_CASE_TPL( "Check make(a0,a1)", STF_NUMERIC_TYPES )
{
  namespace bs = boost::simd;
  std::array<bs::logical<T>,2> ref = {{ true,false }};
  auto p = bs::make<bs::pack<bs::logical<T>,2>>(true,false);

  STF_ALL_EQUAL(p, ref);
}

STF_CASE_TPL( "Check make(a0,...,a3)", STF_NUMERIC_TYPES )
{
  namespace bs = boost::simd;

  std::array<bs::logical<T>,4> ref = {{ true,false,false,true }};
  auto p = bs::make<bs::pack<bs::logical<T>,4>>(true,false,false,true);

  STF_ALL_EQUAL(p, ref);
}

STF_CASE_TPL( "Check make(a0,...,a7)", STF_NUMERIC_TYPES )
{
  namespace bs = boost::simd;

  std::array<bs::logical<T>,8> ref = {{ true,false,false,true,true,false,false,true }};
  auto p = bs::make<bs::pack<bs::logical<T>,8>>(true,false,false,true,true,false,false,true);

  STF_ALL_EQUAL(p, ref);
}

STF_CASE_TPL( "Check make(a0,...,a15)", STF_NUMERIC_TYPES )
{
  namespace bs = boost::simd;
  std::array<bs::logical<T>,16> ref  =  {{  true,false,false,true,true,false,false,true
                                        ,   true,false,false,true,true,false,false,true
                                        }};

  auto p = bs::make< bs::pack<bs::logical<T>,16> >
                    ( true,false,false,true,true,false,false,true
                    , true,false,false,true,true,false,false,true
                    );

  STF_ALL_EQUAL(p, ref);
}

STF_CASE_TPL( "Check make(a0,...,a31)", STF_NUMERIC_TYPES )
{
  namespace bs = boost::simd;
  std::array<bs::logical<T>,32> ref = {{  true,false,false,true,true,false,false,true
                                      ,   true,false,false,true,true,false,false,true
                                      ,   true,false,false,true,true,false,false,true
                                      ,   true,false,false,true,true,false,false,true
                                      }};

  auto p = bs::make< bs::pack<bs::logical<T>,32> >
                    ( true,false,false,true,true,false,false,true
                    , true,false,false,true,true,false,false,true
                    , true,false,false,true,true,false,false,true
                    , true,false,false,true,true,false,false,true
                    );

  STF_ALL_EQUAL(p, ref);
}

#endif // BOOST_INTEL_CXX_VERSION

// These must work anyway

STF_CASE_TPL( "Check make(as_<tag>{},a0,a1)", STF_NUMERIC_TYPES )
{
  namespace bs = boost::simd;
  std::array<bs::logical<T>,2> ref = {{ true,false }};
  auto p = bs::make(bs::as_<bs::pack<bs::logical<T>,2>>{}, true,false);

  STF_ALL_EQUAL(p, ref);
}

STF_CASE_TPL( "Check make(as_<tag>{},a0,...,a3)", STF_NUMERIC_TYPES )
{
  namespace bs = boost::simd;

  std::array<bs::logical<T>,4> ref = {{ true,false,false,true }};
  auto p = bs::make(bs::as_<bs::pack<bs::logical<T>,4>>{}, true,false,false,true);

  STF_ALL_EQUAL(p, ref);
}

STF_CASE_TPL( "Check make(as_<tag>{},a0,...,a7)", STF_NUMERIC_TYPES )
{
  namespace bs = boost::simd;

  std::array<bs::logical<T>,8> ref = {{ true,false,false,true,true,false,false,true }};
  auto p = bs::make(bs::as_<bs::pack<bs::logical<T>,8>>{},true,false,false,true,true,false,false,true);

  STF_ALL_EQUAL(p, ref);
}

STF_CASE_TPL( "Check make(as_<tag>{},a0,...,a15)", STF_NUMERIC_TYPES )
{
  namespace bs = boost::simd;
  std::array<bs::logical<T>,16> ref  =  {{  true,false,false,true,true,false,false,true
                                        ,   true,false,false,true,true,false,false,true
                                        }};

  auto p = bs::make(bs::as_< bs::pack<bs::logical<T>,16> >{}
                   , true,false,false,true,true,false,false,true
                   , true,false,false,true,true,false,false,true
                   );

  STF_ALL_EQUAL(p, ref);
}

STF_CASE_TPL( "Check make(as_<tag>{},a0,...,a31)", STF_NUMERIC_TYPES )
{
  namespace bs = boost::simd;
  std::array<bs::logical<T>,32> ref = {{  true,false,false,true,true,false,false,true
                                      ,   true,false,false,true,true,false,false,true
                                      ,   true,false,false,true,true,false,false,true
                                      ,   true,false,false,true,true,false,false,true
                                      }};

  auto p = bs::make(bs::as_< bs::pack<bs::logical<T>,32> >{}
                    , true,false,false,true,true,false,false,true
                    , true,false,false,true,true,false,false,true
                    , true,false,false,true,true,false,false,true
                    , true,false,false,true,true,false,false,true
                    );

  STF_ALL_EQUAL(p, ref);
}


/*
 * function.cpp
 *
 */

#include <list>
#include <map>
#include <random>
#include <iostream>
#include <chrono>
#include <math.h>
#include <boost/pool/object_pool.hpp>
#include <sstream>

#include "function.h"

using namespace std;

int func_id = 0;

PVariable variable_construct_for_function(Function *f, int rows, int cols) {
    PVariable r = PVariable(variable_construct(rows, cols), variable_destroy);
    r->creator = f;

    return r;
}

// Function class //////////////////////////////////////////////////////////////
Function::Function(){
    name = "Function";
    this->id = func_id;
    func_id += 1;
    count_function += 1;
}

Function::~Function(){
    init();
    count_function--;

}

void Function::init() {
    inputs.clear();
    outputs.clear();
}

PVariable Function::forward(vector<PVariable> &inputs, vector<PVariable > &outputs) {
    return NULL;
}

void Function::backward(cuMat &p_grad, vector<PVariable > &inputs, vector<PVariable > &outputs) {
    //TODO
}

PVariable Function::forward(PVariable v){

    v->forward_count++;

    inputs.push_back(v);
    PVariable r = forward(inputs, outputs);

    return r;
}

PVariable Function::forward(PVariable v1, PVariable v2){

    v1->forward_count++;
    v2->forward_count++;

    inputs.push_back(v1);
    inputs.push_back(v2);
    PVariable r = forward(inputs, outputs);

    return r;
}

PVariable Function::forward(PVariable v1, PVariable v2, PVariable v3){

    v1->forward_count++;
    v2->forward_count++;
    v3->forward_count++;

    inputs.push_back(v1);
    inputs.push_back(v2);
    inputs.push_back(v3);
    PVariable r = forward(inputs, outputs);

    return r;
}

PVariable Function::forward(PVariable v1, PVariable v2, PVariable v3, PVariable v4){
    v1->forward_count++;
    v2->forward_count++;

    inputs.push_back(v1);
    inputs.push_back(v2);
    inputs.push_back(v3);
    inputs.push_back(v4);
    PVariable r = forward(inputs, outputs);

    return r;
}

PVariable Function::forward(PVariable v1, PVariable v2, PVariable v3, PVariable v4,
                            PVariable v5, PVariable v6, PVariable v7, PVariable v8,
                            PVariable v9, PVariable v10, PVariable v11, PVariable v12
){
    v1->forward_count++;
    v2->forward_count++;

    inputs.push_back(v1);
    inputs.push_back(v2);
    inputs.push_back(v3);
    inputs.push_back(v4);
    inputs.push_back(v5);
    inputs.push_back(v6);
    inputs.push_back(v7);
    inputs.push_back(v8);
    inputs.push_back(v9);
    inputs.push_back(v10);
    inputs.push_back(v11);
    inputs.push_back(v12);
    PVariable r = forward(inputs, outputs);

    return r;
}

void Function::backward(cuMat &p_grad){
    backward(p_grad, inputs, outputs);
}

void Function::clip_grad(Variable *v){
    float clip_grad_threshold = 5.0;
    float sq = v->grad.l2();
    float rate = clip_grad_threshold/sq;
    if (rate < 1.){
        v->grad.mul(rate, v->grad);
    }
}

void Function::reset_state(){}

FunctionPlus::FunctionPlus() : Function() {
    name = "FunctionPlus";
}

PVariable FunctionPlus::forward(vector<PVariable > &inputs, vector<PVariable > &outputs){

    PVariable v1 = inputs.at(0);
    PVariable v2 = inputs.at(1);

    PVariable r = variable_construct_for_function(this, v1->data.rows, v1->data.cols);

    v1->data.plus(v2->data, r->data);

    return r;
}
void FunctionPlus::backward(cuMat &p_grad, vector<PVariable > &inputs, vector<PVariable > &outputs){

    PVariable v1 = inputs.at(0);
    PVariable v2 = inputs.at(1);
    //v1->grad += p_grad*1.0;
    //v2->grad += p_grad*1.0;
    if (v1->isGetGrad) p_grad.mul_plus(1.0, v1->grad);
    if (v2->isGetGrad) p_grad.mul_plus(1.0, v2->grad);
}

FunctionMinus::FunctionMinus() : Function() {
    name = "FunctionMinus";
}
PVariable FunctionMinus::forward(vector<PVariable > &inputs, vector<PVariable > &outputs){

    PVariable v1 = inputs.at(0);
    PVariable v2 = inputs.at(1);

    PVariable r = variable_construct_for_function(this, v1->data.rows, v1->data.cols);

    outputs.push_back(r);

    v1->data.minus(v2->data, r->data);

    return r;

}
void FunctionMinus::backward(cuMat &p_grad, vector<PVariable > &inputs, vector<PVariable > &outputs){
    PVariable v1 = inputs.at(0);
    PVariable v2 = inputs.at(1);
    //v1->grad += p_grad*1.0;
    //v2->grad += p_grad*(-1.0);
    if (v1->isGetGrad) p_grad.mul_plus(1.0, v1->grad);
    if (v2->isGetGrad) p_grad.mul_plus(-1.0, v2->grad);
}

FunctionMul::FunctionMul() : Function() {
    name = "FunctionMul";
}
PVariable FunctionMul::forward(vector<PVariable > &inputs, vector<PVariable > &outputs){

    PVariable v1 = inputs.at(0);
    PVariable v2 = inputs.at(1);

    PVariable r = variable_construct_for_function(this, v1->data.rows, v1->data.cols);

    outputs.push_back(r);
    v1->data.mul(v2->data, r->data);

    return r;

}
void FunctionMul::backward(cuMat &p_grad, vector<PVariable > &inputs, vector<PVariable > &outputs){

    PVariable v1 = inputs.at(0);
    PVariable v2 = inputs.at(1);

    //v1->grad += p_grad * v2->data;
    //v2->grad += p_grad * v1->data;
    if (v1->isGetGrad) p_grad.mul_plus(v2->data, v1->grad, 1.0, 1.0);
    if (v2->isGetGrad) p_grad.mul_plus(v1->data, v2->grad, 1.0, 1.0);
}

FunctionInverse::FunctionInverse() : Function() {
    name = "FunctionInverse";
}
PVariable FunctionInverse::forward(vector<PVariable > &inputs, vector<PVariable > &outputs){

    PVariable v = inputs.at(0);

    PVariable r = variable_construct_for_function(this, v->data.rows, v->data.cols);

    outputs.push_back(r);
    v->data.inverse(r->data);

    return r;

}
void FunctionInverse::backward(cuMat &p_grad, vector<PVariable > &inputs, vector<PVariable > &outputs){

    PVariable v = inputs.at(0);

    if (v->isGetGrad) v->grad += p_grad * v->data.inverse_d();
}

FunctionSqrt::FunctionSqrt() : Function() {
    name = "FunctionSqrt";
}
PVariable FunctionSqrt::forward(vector<PVariable > &inputs, vector<PVariable > &outputs){

    PVariable v = inputs.at(0);

    PVariable r = variable_construct_for_function(this, v->data.rows, v->data.cols);

    outputs.push_back(r);
    v->data.sqrt(r->data, 1e-8);

    return r;

}
void FunctionSqrt::backward(cuMat &p_grad, vector<PVariable > &inputs, vector<PVariable > &outputs){

    PVariable v = inputs.at(0);

    if (v->isGetGrad) v->grad += p_grad * v->data.sqrt_d();
}

FunctionSin::FunctionSin() : Function() { }
PVariable FunctionSin::forward(vector<PVariable > &inputs, vector<PVariable > &outputs){

    PVariable v1 = inputs.at(0);

    PVariable r;
    r = PVariable(new Variable(this, v1->data.rows, v1->data.cols));
    outputs.push_back(r);

    v1->data.sin(r->data);
    return r;
}
void FunctionSin::backward(cuMat &p_grad, vector<PVariable > &inputs, vector<PVariable > &outputs){
    PVariable v1 = inputs.at(0);

    if (rr.get() == NULL) rr = PVariable(new Variable(this, v1->data.rows, v1->data.cols));
    v1->data.cos(rr->data);
    if (v1->isGetGrad) v1->grad += p_grad * rr->data;
}

FunctionCos::FunctionCos() : Function() { }
PVariable FunctionCos::forward(vector<PVariable > &inputs, vector<PVariable > &outputs){

    PVariable v1 = inputs.at(0);

    PVariable r;
    r = PVariable(new Variable(this, v1->data.rows, v1->data.cols));
    outputs.push_back(r);
    v1->data.cos(r->data);
    return r;

}
void FunctionCos::backward(cuMat &p_grad, vector<PVariable > &inputs, vector<PVariable > &outputs){
    PVariable v1 = inputs.at(0);

    if (rr.get() == NULL) rr = PVariable(new Variable(this, v1->data.rows, v1->data.cols));
    v1->data.sin(rr->data);
    if (v1->isGetGrad) v1->grad += p_grad * rr->data * (-1.0);
}

FunctionLog::FunctionLog() : Function() {}
PVariable FunctionLog::forward(vector<PVariable > &inputs, vector<PVariable > &outputs){

    PVariable v1 = inputs.at(0);

    PVariable r;
    r = PVariable(new Variable(this, v1->data.rows, v1->data.cols));
    outputs.push_back(r);
    v1->data.log(r->data, 0);
    return r;

}
void FunctionLog::backward(cuMat &p_grad, vector<PVariable > &inputs, vector<PVariable > &outputs){
    PVariable v1 = inputs.at(0);

    if (v1->isGetGrad) v1->grad += p_grad * 1.0/v1->data;
}

FunctionLinear::FunctionLinear() : Function() {
    name = "FunctionLinear";
}
FunctionLinear::FunctionLinear(Variable *w, Variable *b,  bool isTranspose) : Function() {
    name = "FunctionLinear";
    this->w  = w;
    this->b = b;
    this->isTranspose = isTranspose;
}
FunctionLinear::FunctionLinear(Variable *w,  bool isTranspose) : Function() {
    name = "FunctionLinear";
    noBias = true;
    this->w  = w;
    this->isTranspose = isTranspose;

}
FunctionLinear::FunctionLinear(int output_size, int input_size) : Function() {
    name = "FunctionLinear";

    this->w = new Variable(output_size, input_size);
    this->b = new Variable(output_size, 1);
    this->w->randoms(0., sqrt((1./(float)input_size)));

}

FunctionLinear::FunctionLinear(int output_size, int input_size, bool no_bias) : Function() {
    name = "FunctionLinear";

    noBias = no_bias;

    this->w = new Variable(output_size, input_size);
    this->w->randoms(0., sqrt((1./(float)input_size)));

    if (!noBias){
        this->b = new Variable(output_size, 1);
    }
}

void FunctionLinear::toHostArray(){
    i1.toHostArray();
    w->data.toHostArray();
    w->grad.toHostArray();
    if (!noBias){
        b->data.toHostArray();
        b->grad.toHostArray();
    }
}
void FunctionLinear::fromHostArray(){
    i1.fromHostArray();
    w->data.fromHostArray();
    w->grad.fromHostArray();
    if (!noBias){
        b->data.fromHostArray();
        b->grad.fromHostArray();
    }
}

PVariable FunctionLinear::forward(vector<PVariable > &inputs, vector<PVariable > &outputs){

    PVariable x = inputs.at(0);

    int w_size = w->data.rows;
    if (isTranspose) w_size = w->data.cols;

    PVariable r = PVariable(new Variable(this, w_size, x->data.cols));

    if (i1.cols == 0 || i1.cols != x->data.cols){
        i1 = cuMat(1, x->data.cols);
        i1.ones();
    }

    if (!noBias) b->data.dot(i1, r->data);
    if (!isTranspose) w->data.dot_plus(x->data, r->data);
    else w->data.transpose().dot_plus(x->data, r->data);
    //r->data = w->data.dot(x->data) + b->data.dot(i1);

    return r;
}
void FunctionLinear::backward(cuMat &p_grad, vector<PVariable > &inputs, vector<PVariable > &outputs){

    PVariable x = inputs.at(0);

    if (x->isGetGrad){
        if (!isTranspose) w->data.transpose_dot_plus(p_grad, x->grad);
        else w->data.transpose().transpose_dot_plus(p_grad, x->grad);
    }
    //x->grad += w->data.transpose().dot(p_grad);

    if (!isTranspose) p_grad.dot_transpose_plus(x->data, w->grad);
    else {
        cuMat tmpwg = w->grad.transpose();
        tmpwg *= 0;
        p_grad.dot_transpose_plus(x->data, tmpwg);
        w->grad += tmpwg.transpose();
    }
    //w->grad += p_grad.dot(x->data.transpose());

    if (!noBias){
        p_grad.dot_transpose_plus(i1, b->grad);
    }
    //b->grad += p_grad.dot(i1.transpose());
}

FunctionSparseLinear::FunctionSparseLinear() : Function() {
    name = "FunctionSparseLinear";
}

FunctionSparseLinear::FunctionSparseLinear(Variable *w, Variable *b, float beta, float p, Variable *ph) : Function() {
    name = "FunctionSparseLinear";
    this->w  = w;
    this->b = b;

    this->beta = beta;
    this->p = p;
    this->ph = ph;
}

FunctionSparseLinear::FunctionSparseLinear(Variable *w, float beta, float p, Variable *ph) : Function() {
    name = "FunctionSparseLinear";
    noBias = true;
    this->w  = w;

    this->beta = beta;
    this->p = p;
    this->ph = ph;
}

void FunctionSparseLinear::toHostArray(){
    i1.toHostArray();
    w->data.toHostArray();
    w->grad.toHostArray();
    if (!noBias){
        b->data.toHostArray();
        b->grad.toHostArray();
    }
}

void FunctionSparseLinear::fromHostArray(){
    i1.fromHostArray();
    w->data.fromHostArray();
    w->grad.fromHostArray();
    if (!noBias){
        b->data.fromHostArray();
        b->grad.fromHostArray();
    }
}

PVariable FunctionSparseLinear::forward(vector<PVariable > &inputs, vector<PVariable > &outputs){

    PVariable x = inputs.at(0);
    PVariable r = PVariable(new Variable(this, w->data.rows, x->data.cols));

    if (i1.cols == 0 || i1.cols != x->data.cols){
        i1 = cuMat(1, x->data.cols);
        i1.ones();
    }

    if (!noBias) b->data.dot(i1, r->data);
    w->data.dot_plus(x->data, r->data);

    outputs.push_back(r);

    PVariable r2 = PVariable(new Variable(this, w->data.rows, x->data.cols));
    r->data.relu(r2->data);
    //r->data.sigmoid(r2->data);

    return r2;
}

void FunctionSparseLinear::backward(cuMat &p_grad, vector<PVariable > &inputs, vector<PVariable > &outputs){

    PVariable o = outputs.at(0);
    cuMat sg = o->data.relu_d();
    //cuMat sg = o->data.sigmoid_d();

    PVariable x = inputs.at(0);

    if (x->isGetGrad){
        w->data.transpose_dot_plus(p_grad, x->grad);
    }

    // KL
    cuMat ones(ph->data.rows, ph->data.cols);
    ones.ones();
    cuMat p_tmp(ph->data.rows, ph->data.cols);
    p_tmp.ones();
    p_tmp *= p;

    cuMat kl = beta * (-1.0 * p_tmp / ph->data + (ones - p_tmp) / (ones - ph->data));

    p_grad += kl;
    p_grad *= sg;

    p_grad.dot_transpose_plus(x->data, w->grad);

    if (!noBias){
        p_grad.dot_transpose_plus(i1, b->grad);
    }

}

FunctionEmbed::FunctionEmbed() : Function() {
    name = "FunctionEmbed";
}
FunctionEmbed::FunctionEmbed(int output_size, int input_size, bool no_bias){
    name = "FunctionEmbed";

    noBias = no_bias;

    Variable w(output_size, input_size);
    this->w = w;
    this->w.randoms(0., sqrt((1./(float)input_size)));

    if (!noBias){
        Variable b(output_size, 1);
        this->b = b;
    }

}
PVariable FunctionEmbed::forward(vector<PVariable > &inputs, vector<PVariable > &outputs){

    PVariable x = inputs.at(0);

    int x_cols = 0;
    if (!x->isSparse) x_cols = x->data.cols;
    else x_cols = x->data_sparse.rows;

    PVariable r = variable_construct_for_function(this, w.data.rows, x_cols);

    outputs.push_back(r);

    if (i1.cols == 0 || i1.cols != x_cols){
        i1 = cuMat(1, x_cols);
        i1.ones();
    }

    if (!noBias) b.data.dot(i1, r->data);

    if (!x->isSparse){
        w.data.dot_plus(x->data, r->data);
    }
    else{
        if (wt.rows == 0) wt = cuMat(w.data.cols, w.data.rows);
        w.data.transpose(wt);
        if (rt.rows == 0) rt = cuMat(r->data.cols, r->data.rows);
        x->data_sparse.s_d_dot(wt, rt);
        if (rtmp.rows == 0) rtmp = cuMat(r->data.rows, r->data.cols);
        rt.transpose(rtmp);
        r->data.plus(rtmp, r->data);
    }

    return r;
}
void FunctionEmbed::backward(cuMat &p_grad, vector<PVariable > &inputs, vector<PVariable > &outputs){

    PVariable x = inputs.at(0);

    float batchSizeNorm = 1.0/((float)x->data.cols);

    if (x->isGetGrad) w.data.transpose_dot_plus(p_grad, x->grad);

    if (!x->isSparse){
        p_grad.dot_transpose_plus(x->data, w.grad);
    }
    else{
        cuMat xd = x->data_sparse.toDense();
        p_grad.dot_plus(xd, w.grad);
    }
    w.grad.mul(batchSizeNorm, w.grad);

    if (!noBias){
        p_grad.dot_transpose_plus(i1, b.grad);
        b.grad.mul(batchSizeNorm, b.grad);
    }

}
void FunctionEmbed::toHostArray(){
    i1.toHostArray();
    w.data.toHostArray();
    w.grad.toHostArray();
    if (!noBias){
        b.data.toHostArray();
        b.grad.toHostArray();
    }
}
void FunctionEmbed::fromHostArray(){
    i1.fromHostArray();
    w.data.fromHostArray();
    w.grad.fromHostArray();
    if (!noBias){
        b.data.fromHostArray();
        b.grad.fromHostArray();
    }
}

FunctionReLU::FunctionReLU() : Function() {
    name = "FunctionReLU";
}

PVariable FunctionReLU::forward(vector<PVariable > &inputs, vector<PVariable > &outputs){

    PVariable x = inputs.at(0);

    PVariable r = PVariable(new Variable(this, x->data.rows, x->data.cols));

    x->data.relu(r->data);

    return r;
}

void FunctionReLU::backward(cuMat &p_grad, vector<PVariable > &inputs, vector<PVariable > &outputs){

    PVariable x = inputs.at(0);

    if (rr.get() == NULL || rr->data.cols != x->data.cols){
        rr = PVariable(new Variable(x->data.rows, x->data.cols));
    }

    x->data.relu_d(rr->data);

    if (x->isGetGrad) rr->data.mul_plus(p_grad, x->grad, 1.0, 1.0);
}

FunctionPReLU::FunctionPReLU() : Function() {
    name = "FunctionPReLU";
}
FunctionPReLU::FunctionPReLU(Variable *a) {
    name = "FunctionPReLU";
    this->a = a;
}

PVariable FunctionPReLU::forward(vector<PVariable > &inputs, vector<PVariable > &outputs){

    PVariable x = inputs.at(0);

    PVariable r = variable_construct_for_function(this, x->data.rows, x->data.cols);

    outputs.push_back(r);

    x->data.prelu(a->data, r->data);

    return r;
}
void FunctionPReLU::backward(cuMat &p_grad, vector<PVariable > &inputs, vector<PVariable > &outputs){
    PVariable x = inputs.at(0);

    if (xd.get() == NULL || xd->data.cols != x->data.cols){
        xd = PVariable(new Variable(x->data.rows, x->data.cols));
        ad = PVariable(new Variable(x->data.rows, x->data.cols));
    }

    x->data.prelu_d(a->data, xd->data, ad->data);

    ad->data.mul_plus(p_grad, a->grad, 1.0, 1.0);
    if (x->isGetGrad) xd->data.mul_plus(p_grad, x->grad, 1.0, 1.0);

}

FunctionSigmoid::FunctionSigmoid() : Function() {
    name = "FunctionSigmoid";
}
PVariable FunctionSigmoid::forward(vector<PVariable > &inputs, vector<PVariable > &outputs){

    PVariable x = inputs.at(0);

    PVariable r = variable_construct_for_function(this, x->data.rows, x->data.cols);

    outputs.push_back(r);
    x->data.sigmoid(r->data);

    return r;
}
void FunctionSigmoid::backward(cuMat &p_grad, vector<PVariable > &inputs, vector<PVariable > &outputs){
    PVariable x = inputs.at(0);

    if (rr.get() == NULL || rr->data.cols != x->data.cols){
        rr = PVariable(new Variable(x->data.rows, x->data.cols));
    }
    x->data.sigmoid_d(rr->data);

    if (x->isGetGrad) rr->data.mul_plus(p_grad, x->grad, 1.0, 1.0);
}

FunctionTanh::FunctionTanh() : Function() {
    name = "FunctionTanh";
}
PVariable FunctionTanh::forward(vector<PVariable > &inputs, vector<PVariable > &outputs){

    PVariable x = inputs.at(0);

    PVariable r = variable_construct_for_function(this, x->data.rows, x->data.cols);

    outputs.push_back(r);

    x->data.tanh(r->data);

    return r;
}
void FunctionTanh::backward(cuMat &p_grad, vector<PVariable > &inputs, vector<PVariable > &outputs){
    PVariable x = inputs.at(0);

    if (rr.get() == NULL || rr->data.cols != x->data.cols){
        rr = PVariable(new Variable(x->data.rows, x->data.cols));
    }
    x->data.tanh_d(rr->data);

    if (x->isGetGrad) rr->data.mul_plus(p_grad, x->grad, 1.0, 1.0);
}

FunctionSoftmax::FunctionSoftmax() : Function() {
    name = "FunctionSoftmax";
}
PVariable FunctionSoftmax::forward(vector<PVariable > &inputs, vector<PVariable > &outputs){

    PVariable x = inputs.at(0);

    PVariable r = PVariable(new Variable(x->data.rows, x->data.cols));

    outputs.push_back(r);

    x->data.softmax(r->data);

    return r;
}

FunctionSoftmaxCrossEntropy::FunctionSoftmaxCrossEntropy() : Function() {
    name = "FunctionSoftmaxCrossEntropy";
    loss = cuMat(1,1);
    loss.ones();

}
PVariable FunctionSoftmaxCrossEntropy::forward(vector<PVariable > &inputs, vector<PVariable > &outputs){

    PVariable x = inputs.at(0);
    PVariable t = inputs.at(1);

    if (rr3.get() == NULL || rr3->data.cols != x->data.cols){
        rr3 = PVariable(new Variable(x->data.rows, x->data.cols));
    }

    x->data.softmax(rr3->data);

    if (rr.get() == NULL || rr->data.cols != rr3->data.cols){
        rr = PVariable(new Variable(rr3->data));
    }
    else rr->data = rr3->data;

    rr3->data.softmax_cross_entropy(t->data, rr3->data);
    float sum = rr3->data.sum();

    sum /= rr3->data.cols;

    PVariable r = PVariable(new Variable(this, loss.rows, loss.cols));

    outputs.push_back(r);

    r->data = loss * sum;

    return r;
}
void FunctionSoftmaxCrossEntropy::backward(cuMat &p_grad, vector<PVariable > &inputs, vector<PVariable > &outputs){

    PVariable x = inputs.at(0);
    PVariable t = inputs.at(1);
    PVariable y = rr;

    if (x->isGetGrad) x->grad += y->data - t->data;
}

FunctionMeanSquaredError::FunctionMeanSquaredError() : Function() {
    name = "FunctionMeanSquaredError";
    loss = cuMat(1,1);
    loss.ones();
}

PVariable FunctionMeanSquaredError::forward(vector<PVariable > &inputs, vector<PVariable > &outputs){

    PVariable x = inputs.at(0);
    PVariable t = inputs.at(1);

    if (rr.get() == NULL || rr->data.cols != x->data.cols){
        rr = PVariable(new Variable(x->data));
    }

    x->data.minus(t->data, rr->data);

    rr->data.mul(rr->data, rr->data);

    float sum = rr->data.sum();
    sum /= (2*rr->data.cols);

    PVariable r = variable_construct_for_function(this, loss.rows, loss.cols);

    outputs.push_back(r);

    loss.mul(sum, r->data);

    return r;
}
void FunctionMeanSquaredError::backward(cuMat &p_grad, vector<PVariable > &inputs, vector<PVariable > &outputs){
    PVariable x = inputs.at(0);
    PVariable t = inputs.at(1);

    if (rr.get() == NULL || rr->data.cols != x->data.cols){

        rr = PVariable(new Variable(x->data));
    }

    x->data.minus(t->data, rr->data);
    float batch_size = rr->data.cols;
    if (x->isGetGrad) x->grad.plus(rr->data, x->grad);
}

FunctionDropout::FunctionDropout(float p) : Function() {
    name = "FunctionDropout";
    this->p = p;
}
PVariable FunctionDropout::forward(vector<PVariable > &inputs, vector<PVariable > &outputs){

    PVariable x = inputs.at(0);

    PVariable r = PVariable(new Variable(this, x->data.rows, x->data.cols));

    outputs.push_back(r);

    if (rr.get() == NULL || rr->data.cols != x->data.cols){
        rr = PVariable(new Variable(x->data.rows, x->data.cols));
    }

    x->data.dropout(r->data, rr->data, p);

    return r;
}
void FunctionDropout::backward(cuMat &p_grad, vector<PVariable > &inputs, vector<PVariable > &outputs){
    PVariable x = inputs.at(0);
    PVariable idx = rr;

    if (x->isGetGrad) idx->data.mul_plus(p_grad, x->grad, 1.0, 1.0);
}

FunctionIdentity::FunctionIdentity() : Function() {
    name = "FunctionIdentity";
}

PVariable FunctionIdentity::forward(vector<PVariable> &inputs, vector<PVariable> &outputs){

    PVariable x = inputs.at(0);

    PVariable r = variable_construct_for_function(this, x->data.rows, x->data.cols);

    r->data = x->data;

    return r;
}

void FunctionIdentity::backward(cuMat &p_grad, vector<PVariable> &inputs, vector<PVariable> &outputs){
    PVariable x = inputs.at(0);

    if (x->isGetGrad) p_grad.mul_plus(1.0, x->grad);
}

// LSTM ----------------------------------
FunctionLSTM::FunctionLSTM() : Function() {
    name = "FunctionLSTM";
}

PVariable FunctionLSTM::forward(vector<PVariable> &inputs, vector<PVariable> &outputs){

    PVariable x = inputs.at(0);
    PVariable c = inputs.at(1);
    PVariable c_next = inputs.at(2);

    int offset = x->data.rows/4;

    this->i = x->data.sliceRows(0, offset);
    this->f = x->data.sliceRows(offset, offset);
    this->g = x->data.sliceRows(offset*2, offset);
    this->o = x->data.sliceRows(offset*3, offset);

    cuMat _i = this->i;
    cuMat _f = this->f;
    cuMat _g = this->g;
    cuMat _o = this->o;

    this->i.sigmoid(_i);
    this->f.sigmoid(_f);
    this->g.tanh(_g);
    this->o.sigmoid(_o);

    c_next->data = _g * _i + _f * c->data;

    cuMat tmp = c_next->data;
    c_next->data.tanh(tmp);

    PVariable r = variable_construct_for_function(this, x->data.rows, x->data.cols);

    r->data = _o * tmp;

    return r;
}

void FunctionLSTM::backward(cuMat &gh, vector<PVariable> &inputs, vector<PVariable> &outputs){

    PVariable x = inputs.at(0);
    PVariable c = inputs.at(1);
    PVariable c_next = inputs.at(2);

    int offset = x->data.rows/4;

    cuMat co = c_next->data.tanh();

    c->grad = gh * this->o * co.tanh_d() + c_next->grad;

    cuMat gg = c->grad * this->i * this->g.tanh_d();

    cuMat gi = c->grad * this->g * this->i.sigmoid_d();

    cuMat gf = c->grad * c->data * this->f.sigmoid_d();

    cuMat go = gh * co * this->o.sigmoid_d();

    c->grad *= this->f;

    cuMat tmp = x->grad;
    tmp.joinRows(gi, 0, offset);
    tmp.joinRows(gf, offset, offset);
    tmp.joinRows(gg, offset*2, offset);
    tmp.joinRows(go, offset*3, offset);
    if (x->isGetGrad) x->grad += tmp;
}

//FullLSTM
FunctionFullLSTM::FunctionFullLSTM(
        Variable *f_c_w, Variable *f_h_w, Variable *f_x_w, Variable *f_x_b,
        Variable *i_c_w, Variable *i_h_w, Variable *i_x_w, Variable *i_x_b,
        Variable *o_c_w, Variable *o_h_w, Variable *o_x_w, Variable *o_x_b,
        Variable *g_h_w, Variable *g_x_w, Variable *g_x_b) : Function(){

    name = "FunctionFullLSTM";

    this->f_c_w = f_c_w; this->f_h_w = f_h_w; this->f_x_w = f_x_w; this->f_x_b = f_x_b;
    this->i_c_w = i_c_w; this->i_h_w = i_h_w; this->i_x_w = i_x_w; this->i_x_b = i_x_b;
    this->o_c_w = o_c_w; this->o_h_w = o_h_w; this->o_x_w = o_x_w; this->o_x_b = o_x_b;
    this->g_h_w = g_h_w; this->g_x_w = g_x_w; this->g_x_b = g_x_b;
}

PVariable FunctionFullLSTM::forward(vector<PVariable> &inputs, vector<PVariable> &outputs) {

    PVariable x = inputs.at(0);
    PVariable h = inputs.at(1);
    PVariable c = inputs.at(2);
    PVariable c_next = inputs.at(3);

    cuMat ones(1, x->data.cols);
    ones.ones();

    f_hat = f_c_w->data.dot(c->data) + f_h_w->data.dot(h->data) + f_x_w->data.dot(x->data) + f_x_b->data.dot(ones);
    f = f_hat.sigmoid();
    i_hat = i_c_w->data.dot(c->data) + i_h_w->data.dot(h->data) + i_x_w->data.dot(x->data) + i_x_b->data.dot(ones);
    i = i_hat.sigmoid();
    g_hat = g_h_w->data.dot(h->data) + g_x_w->data.dot(x->data) + g_x_b->data.dot(ones);
    g = g_hat.tanh();

    c_next->data = c->data * f + i * g;

    o_hat = o_c_w->data.dot(c_next->data) + o_h_w->data.dot(h->data) + o_x_w->data.dot(x->data) + o_x_b->data.dot(ones);
    o = o_hat.sigmoid();

    PVariable h_next = variable_construct_for_function(this, f_x_w->data.rows, x->data.cols);

    h_next->data = c_next->data.tanh() * o;

    return h_next;
}

void FunctionFullLSTM::backward(cuMat &delta_h, vector<PVariable> &inputs, vector<PVariable> &outputs) {

    PVariable x = inputs.at(0);
    PVariable h = inputs.at(1);
    PVariable c = inputs.at(2);
    PVariable c_next = inputs.at(3);
    PVariable f_for_grad = inputs.at(4);
    PVariable f_next_for_grad = inputs.at(5);
    PVariable i_for_grad = inputs.at(6);
    PVariable i_next_for_grad = inputs.at(7);
    PVariable o_for_grad = inputs.at(8);
    PVariable o_next_for_grad = inputs.at(9);
    PVariable g_for_grad = inputs.at(10);
    PVariable g_next_for_grad = inputs.at(11);

    cuMat ones(1, x->data.cols);
    ones.ones();

    cuMat delta_o = delta_h *  c_next->data.tanh() * o_hat.sigmoid_d();
    c_next->grad += delta_h * o * c_next->data.tanh_d() + o_c_w->data.transpose().dot(delta_o);

    cuMat delta_i = c_next->grad * g * i_hat.sigmoid_d();
    cuMat delta_f = c_next->grad * c->data * f_hat.sigmoid_d();
    cuMat delta_g = c_next->grad * i * g_hat.tanh_d();

    i_for_grad->grad = delta_i;
    f_for_grad->grad = delta_f;
    o_for_grad->grad = delta_o;
    g_for_grad->grad = delta_g;

    c->grad = c_next->grad * f + i_c_w->data.transpose().dot(delta_i) + f_c_w->data.transpose().dot(delta_f);

    o_c_w->grad += delta_o.dot(c_next->grad.transpose());
    i_c_w->grad += i_next_for_grad->grad.dot(c_next->grad.transpose());
    f_c_w->grad += f_next_for_grad->grad.dot(c_next->grad.transpose());

    x->grad += g_x_w->data.transpose().dot(delta_g)
               + i_x_w->data.transpose().dot(delta_i)
               + f_x_w->data.transpose().dot(delta_f)
               + o_x_w->data.transpose().dot(delta_o);

    h->grad += g_h_w->data.transpose().dot(delta_g)
               + i_h_w->data.transpose().dot(delta_i)
               + f_h_w->data.transpose().dot(delta_f)
               + o_h_w->data.transpose().dot(delta_o);

    g_x_w->grad += delta_g.dot(x->data.transpose());
    g_h_w->grad += g_next_for_grad->grad.dot(h->data.transpose());
    i_x_w->grad += delta_i.dot(x->data.transpose());
    i_h_w->grad += i_next_for_grad->grad.dot(h->data.transpose());
    f_x_w->grad += delta_f.dot(x->data.transpose());
    f_h_w->grad += f_next_for_grad->grad.dot(h->data.transpose());
    o_x_w->grad += delta_o.dot(x->data.transpose());
    o_h_w->grad += o_next_for_grad->grad.dot(h->data.transpose());

    g_x_b->grad += delta_g.dot(ones.transpose());
    i_x_b->grad += delta_i.dot(ones.transpose());
    f_x_b->grad += delta_f.dot(ones.transpose());
    o_x_b->grad += delta_o.dot(ones.transpose());
}

FunctionGRU::FunctionGRU(Variable *w_r, Variable *u_r, Variable *b_r,
                         Variable *w_z, Variable *u_z, Variable *b_z,
                         Variable *w_g, Variable *u_g, Variable *b_g){
    this->w_r = w_r;
    this->u_r = u_r;
    this->b_r = b_r;
    this->w_z = w_z;
    this->u_z = u_z;
    this->b_z = b_z;
    this->w_g = w_g;
    this->u_g = u_g;
    this->b_g = b_g;

    name = "FunctionGRU";
}

PVariable FunctionGRU::forward(vector<PVariable> &inputs, vector<PVariable> &outputs) {
    PVariable x = inputs[0];
    PVariable h = inputs[1];

    cuMat ones(w_z->data.rows, x->data.cols);
    ones.ones();
    cuMat ones_b(1, x->data.cols);
    ones_b.ones();

    r_hat = w_r->data.dot(h->data) + u_r->data.dot(x->data) + b_r->data.dot(ones_b);
    r = r_hat.sigmoid();
    z_hat = w_z->data.dot(h->data) + u_z->data.dot(x->data) + b_z->data.dot(ones_b);
    z = z_hat.sigmoid();
    g_hat = w_g->data.dot(h->data * r) + u_g->data.dot(x->data) + b_g->data.dot(ones_b);
    g = g_hat.tanh();

    PVariable h_new = variable_construct_for_function(this, w_r->data.rows, x->data.cols);

    h_new->data = h->data * (ones - z) + z * g;

    return h_new;
}

void FunctionGRU::backward(cuMat &delta_h, vector<PVariable> &inputs, vector<PVariable> &outputs) {
    PVariable x = inputs[0];
    PVariable h = inputs[1];

    cuMat zeros(w_z->data.rows, x->data.cols);
    //ones.ones();
    cuMat ones_b(1, x->data.cols);
    ones_b.ones();

    cuMat delta4 = (zeros - z) * delta_h;
    cuMat delta5 = delta_h * h->data;
    cuMat delta6 = zeros - delta5;
    cuMat delta7 = delta_h * g;
    cuMat delta8 = delta_h * z;

    cuMat delta9 = delta6 + delta7;

    cuMat delta10 = delta8 * g_hat.tanh_d();
    cuMat delta11 = delta9 * z_hat.sigmoid_d();

    cuMat delta12 = u_g->data.transpose().dot(delta10);
    cuMat delta13 = w_g->data.transpose().dot(delta10);
    cuMat delta14 = u_z->data.transpose().dot(delta11);
    cuMat delta15 = w_z->data.transpose().dot(delta11);

    cuMat delta16 = delta13 * h->data;
    cuMat delta17 = delta13 * r;
    cuMat delta18 = delta16 * r_hat.sigmoid_d();
    cuMat delta19 = delta17 + delta4;
    cuMat delta20 = u_r->data.transpose().dot(delta18);
    cuMat delta21 = w_r->data.transpose().dot(delta18);
    cuMat delta22 = delta21 + delta15;
    h->grad += delta19 + delta22;
    x->grad += delta12 + delta14 + delta20;

    w_r->grad += delta18.dot(h->data.transpose());
    u_r->grad += delta18.dot(x->data.transpose());
    w_z->grad += delta11.dot(h->data.transpose());
    u_z->grad += delta11.dot(x->data.transpose());

    w_g->grad += delta10.dot((h->data * r).transpose());
    u_g->grad += delta10.dot(x->data.transpose());

    b_r->grad += delta18.dot(ones_b.transpose());
    b_z->grad += delta11.dot(ones_b.transpose());
    b_g->grad += delta10.dot(ones_b.transpose());

}

FunctionBatchNorm::FunctionBatchNorm(int element_size, int channel_num, Variable *gamma, Variable *beta, Variable *x_mean, Variable *x_var) {
    this->gamma = gamma;
    this->beta = beta;

    this->x_mean = x_mean;
    this->x_var = x_var;

    this->element_size = element_size;
    this->channel_num = channel_num;

    xhat.resize(channel_num);
    rmu.resize(channel_num);
    xmu.resize(channel_num);
    ivar.resize(channel_num);
    sqrtvar.resize(channel_num);
    var.resize(channel_num);

}

PVariable FunctionBatchNorm::forward(vector<PVariable> &inputs, vector<PVariable> &outputs) {

    PVariable x_org = inputs[0];

    PVariable r = variable_construct_for_function(this, x_org->data.rows, x_org->data.cols);

    for(int i=0; i<channel_num; i++) {

        int idx = i*element_size;
        cuMat x_data = x_org->data.sliceRows(idx, element_size);

        int N = x_data.cols;
        int D = x_data.rows;

        cuMat ones(D, N);
        ones.ones();

        //step 1
        if (is_train) rmu[i] = 1.0 / N * x_data.batch_sum();
        else{
            rmu[i] = cuMat(element_size, 1);
            rmu[i].memSetDevice(this->x_mean->data.mDevice + idx);
        }
        cuMat mu = rmu[i].vec_to_mat(N);

        //step 2
        xmu[i] = x_data - mu;

        //step 3
        cuMat sq = xmu[i] * xmu[i];

        //step 4
        if (is_train) var[i] = 1.0 / N * sq.batch_sum();
        else {
            var[i] = cuMat(element_size, 1);
            var[i].memSetDevice(x_var->data.mDevice + idx);
            var[i] = ((float) N) / (((float) N) - 1.0) * var[i];
        } //use unbiased variance

        //step 5
        sqrtvar[i] = var[i].sqrt();

        //step 6
        ivar[i] = sqrtvar[i].inverse();
        cuMat tmp = ivar[i].vec_to_mat(N);

        //step 7
        xhat[i] = xmu[i] * tmp;

        //step 8
        cuMat gamma_tmp(element_size, 1);
        gamma_tmp.memSetDevice(gamma->data.mDevice + idx);
        cuMat gammax = xhat[i].mat_vec_mul(gamma_tmp, 0);

        //step 9
        cuMat beta_tmp(element_size, 1);
        beta_tmp.memSetDevice(beta->data.mDevice + idx);
        cuMat r_c = gammax + ones.mat_vec_mul(beta_tmp, 0);
        r->data.joinRows(r_c, idx, element_size);

    }

    return r;
}

void FunctionBatchNorm::backward(cuMat &dout_org, vector<PVariable> &inputs, vector<PVariable> &outputs) {

    PVariable x = inputs[0];

    for(int i=0; i<channel_num; i++) {

        int idx = i*element_size;
        cuMat dout = dout_org.sliceRows(idx, element_size);

        int N = dout.cols;
        int D = dout.rows;

        //step 9
        beta->grad.memSetDeviceRow(dout.batch_sum().mDevice, i);
        cuMat dgammax = dout;

        //step 8
        cuMat tmp = dgammax * xhat[i];
        gamma->grad.memSetDeviceRow(tmp.batch_sum().mDevice, i);
        cuMat gamma_tmp(element_size, 1);
        gamma_tmp.memSetDevice(gamma->data.mDevice + idx);
        cuMat dxhat = dgammax.mat_vec_mul(gamma_tmp, 0);

        //step 7
        tmp = dxhat * xmu[i];
        cuMat divar = tmp.batch_sum();
        cuMat dxmu1 = dxhat.mat_vec_mul(ivar[i], 0);

        //step 6
        //tmp = sqrtvar.inverse_d();
        tmp = -1.0 * sqrtvar[i].inverse() * sqrtvar[i].inverse();
        cuMat dsqrtvar = tmp * divar;

        //step 5
        cuMat dvar = var[i].sqrt_d() * dsqrtvar;

        //step 4
        cuMat dsq = 1.0 / N * dvar.vec_to_mat(N);

        //step 3
        cuMat dxmu2 = 2.0 * xmu[i] * dsq;

        //step 2
        cuMat dx1 = dxmu1 + dxmu2;
        cuMat dmu = -1.0 * dx1.batch_sum();

        //step 1
        cuMat dx2 = 1.0 / N * dmu.vec_to_mat(N);

        //step0
        cuMat dx3 = dx1 + dx2;
        x->grad.joinRows(dx3, idx, element_size);
    }
}

FunctionConv2D::FunctionConv2D(Variable *w, Variable *b, int batch_num, int channel_num, int w_size, int h_size, int filter_size, int filter_num, int stride, int padding){

    this->batch_num = batch_num;
    this->channel_num = channel_num;
    this->w_size = w_size;
    this->h_size = h_size;
    this->filter_size = filter_size;
    this->filter_num = filter_num;
    this->stride = stride;
    this->padding = padding;

    this->w = w;
    this->b = b;

    this->name = "FunctionConv2D";

    /**
    * Each dimension h and w of the output images is computed as followed:
    * outputDim = 1 + (inputDim + 2*pad - filterDim)/convolutionStride
    */
    this->outputDim_w = 1 + (w_size + (padding+padding) - filter_size) / stride;
    this->outputDim_h = 1 + (h_size + (padding+padding) - filter_size) / stride;

    ones = new Variable(this->outputDim_w * this->outputDim_h, 1);
    ones->ones();
}

FunctionConv2D::~FunctionConv2D(){
    delete ones;
}

cuMat FunctionConv2D::forward_one(cuMat &data){

    int output_dim_w, output_dim_h;

    cuMat stacked = data.im2col(w_size, h_size, channel_num, filter_size, filter_size, stride, stride, padding, padding, padding, padding, output_dim_w, output_dim_h);

    cols.push_back(stacked);

    cuMat r = stacked.dot(w->data.transpose()) + ones->data.dot(b->data.transpose());

    return r;
}

cuMat FunctionConv2D::backward_one(cuMat &col, cuMat &p_grad) {

    cuMat p_grad_t = p_grad.transpose();

    w->grad += p_grad_t.dot(col);

    b->grad += p_grad_t.dot(ones->data);

    cuMat dcol = p_grad.dot(w->data);

    cuMat dx = dcol.col2im(w_size, h_size, channel_num, filter_size, filter_size, stride, stride, padding, padding, padding, padding);

    return dx;
}

PVariable FunctionConv2D::forward(vector<PVariable> &inputs, vector<PVariable> &outputs){

    PVariable x = inputs[0];

    PVariable r = PVariable(new Variable(this, filter_num * outputDim_w * outputDim_h, batch_num));

    for(int i=0; i<batch_num; i++) {
        int data_index = i*(channel_num * w_size * h_size);
        float *one_m = x->data.mDevice + data_index;
        cuMat one_m_dev(w_size * h_size, channel_num);
        one_m_dev.memSetDevice(one_m);
        cuMat r_array = forward_one(one_m_dev);
        r->data.memSetDeviceCol(r_array.mDevice, i);
    }
    return r;
}

void FunctionConv2D::backward(cuMat &p_grad, vector<PVariable> &inputs, vector<PVariable> &outputs) {

    PVariable x = inputs[0];

    cuMat dx(channel_num * w_size * h_size, batch_num);

    for(int i=0; i<batch_num; i++) {

        int data_index = i*(filter_num * outputDim_w * outputDim_h);

        float *p_grad_one = p_grad.mDevice + data_index;

        cuMat p_grad_one_dev(outputDim_w * outputDim_h, filter_num);
        p_grad_one_dev.memSetDevice(p_grad_one);

        cuMat r_array = backward_one(cols[i], p_grad_one_dev);

        dx.memSetDeviceCol(r_array.mDevice, i);
    }

    x->grad += dx;
}

FunctionPooling::FunctionPooling(int width, int height, int depth, int windowWidth, int windowHeight, int stride, int padding){

    name = "FunctionPooling";

    this->width = width;
    this->height = height;
    this->depth = depth;
    this->windowWidth = windowWidth;
    this->windowHeight = windowHeight;
    this->stride = stride;
    this->padding = padding;
}

PVariable FunctionPooling::forward(vector<PVariable> &inputs, vector<PVariable> &outputs){
    PVariable x = inputs[0];

    int batch_num = x->data.cols;

    //* Pooling size as followed:
    //* outputDim = 1 + (inputDim + 2*padding - windowDim)/poolingStride;

    int pooled_w = 1 + (width + (padding+padding) - windowWidth)/stride;
    int pooled_h = 1 + (height + (padding+padding) - windowHeight)/stride;

    PVariable r = PVariable(new Variable(this, depth * pooled_w * pooled_h, batch_num));

    r->data = x->data.pooling(batch_num, width, height, depth, windowWidth, windowHeight, stride, stride, padding, padding, padding, padding);
    return r;
}

void FunctionPooling::backward(cuMat &p_grad, vector<PVariable> &inputs, vector<PVariable> &outputs){

    PVariable x = inputs[0];

    int batch_num = x->data.cols;

    int pooled_w = 1 + (width + (padding+padding) - windowWidth)/stride;
    int pooled_h = 1 + (height + (padding+padding) - windowHeight)/stride;

    x->grad = x->data.pooling_backward(batch_num, p_grad.mDevice, width, height, depth, windowWidth, windowHeight, stride, stride, padding, padding, padding, padding);
}


/*
 [auto_generated]
  boost/numeric/odeint/iterator/detail/adaptive_iterator_impl.hpp

  [begin_description]
  tba.
  [end_description]

  Copyright 2009-2012 Karsten Ahnert
  Copyright 2009-2012 Mario Mulansky

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

#ifndef BOOST_NUMERIC_ODEINT_ITERATOR_DETAIL_ADAPTIVE_ITERATOR_IMPL_HPP_DEFINED
#define BOOST_NUMERIC_ODEINT_ITERATOR_DETAIL_ADAPTIVE_ITERATOR_IMPL_HPP_DEFINED

#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/throw_exception.hpp>

#include <boost/numeric/odeint/util/unit_helper.hpp>
#include <boost/numeric/odeint/util/copy.hpp>
#include <boost/numeric/odeint/stepper/controlled_step_result.hpp>
#include <boost/numeric/odeint/iterator/detail/ode_iterator_base.hpp>

namespace boost {
namespace numeric {
namespace odeint {

    template< class Iterator , class Stepper , class System , class State , typename Tag , typename StepperTag >
    class adaptive_iterator_impl;

    /*
     * Specilization for controlled steppers
     */
    /**
     * \brief ODE Iterator with adaptive step size control. The value type of this iterator is the state type of the stepper.
     *
     * Implements an ODE iterator with adaptive step size control. Uses controlled steppers. adaptive_iterator is a model
     * of single-pass iterator.
     *
     * The value type of this iterator is the state type of the stepper. Hence one can only access the state and not the current time.
     *
     * \tparam Stepper The stepper type which should be used during the iteration.
     * \tparam System The type of the system function (ODE) which should be solved.
     */
    template< class Iterator , class Stepper , class System , class State , typename Tag >
    class adaptive_iterator_impl< Iterator , Stepper , System , State , Tag , controlled_stepper_tag >
        : public detail::ode_iterator_base< Iterator , Stepper , System , State , Tag >
    {
    private:

        typedef Stepper stepper_type;
        typedef System system_type;
        typedef typename boost::numeric::odeint::unwrap_reference< stepper_type >::type unwrapped_stepper_type;
        typedef State state_type;
        typedef typename traits::time_type< stepper_type >::type time_type;
        typedef typename traits::value_type< stepper_type >::type ode_value_type;
        #ifndef DOXYGEN_SKIP
        typedef detail::ode_iterator_base< Iterator , Stepper , System , State , Tag > base_type;
        #endif

    public:

        /**
         * \brief Constructs an adaptive_iterator. This constructor should be used to construct the begin iterator.
         *
         * \param stepper The stepper to use during the iteration.
         * \param sys The system function (ODE) to solve.
         * \param s The initial state. adaptive_iterator stores a reference of s and changes its value during the iteration.
         * \param t The initial time.
         * \param t_end The end time, at which the iteration should stop.
         * \param dt The initial time step.
         */
        adaptive_iterator_impl( stepper_type stepper , system_type sys , state_type &s , time_type t , time_type t_end , time_type dt )
            : base_type( stepper , sys , t , dt ) , m_t_end( t_end ) , m_state( &s )
        {
            if( detail::less_with_sign( this->m_t_end , this->m_t , this->m_dt ) )
                this->m_at_end = true;
        }

        /**
         * \brief Constructs an adaptive_iterator. This constructor should be used to construct the end iterator.
         *
         * \param stepper The stepper to use during the iteration.
         * \param sys The system function (ODE) to solve.
         * \param s The initial state. adaptive_iterator store a reference of s and changes its value during the iteration.
         */
        adaptive_iterator_impl( stepper_type stepper , system_type sys , state_type &s )
            : base_type( stepper , sys ) , m_state( &s ) { }

    protected:

        friend class boost::iterator_core_access;

        void increment()
        {
            if( detail::less_with_sign( this->m_t , this->m_t_end , this->m_dt) )
            {
                if( detail::less_with_sign( this->m_t_end ,
                                            static_cast<time_type>(this->m_t + this->m_dt) ,
                                            this->m_dt ) )
                {
                    this->m_dt = this->m_t_end - this->m_t;
                }
                unwrapped_stepper_type &stepper = this->m_stepper;
                const size_t max_attempts = 1000;
                size_t trials = 0;
                controlled_step_result res = success;
                do
                {
                    res = stepper.try_step( this->m_system , *( this->m_state ) , this->m_t , this->m_dt );
                    ++trials;
                }
                while( ( res == fail ) && ( trials < max_attempts ) );
                if( trials == max_attempts )
                {
                    BOOST_THROW_EXCEPTION( std::overflow_error( "Adaptive iterator : Maximal number of iterations reached. A step size could not be found." ));
                }
            } else {
                this->m_at_end = true;
            }
        }
    public:
        const state_type& get_state() const
        {
            return *this->m_state;
        }

    private:
        time_type m_t_end;
        state_type* m_state;
    };

    /*
     * Specilization for dense outputer steppers
     */
    /**
     * \brief ODE Iterator with adaptive step size control. The value type of this iterator is the state type of the stepper.
     *
     * Implements an ODE iterator with adaptive step size control. Uses dense-output steppers. adaptive_iterator is a model
     * of single-pass iterator.
     *
     * The value type of this iterator is the state type of the stepper. Hence one can only access the state and not the current time.
     *
     * \tparam Stepper The stepper type which should be used during the iteration.
     * \tparam System The type of the system function (ODE) which should be solved.
     */
    template< class Iterator , class Stepper , class System , class State , typename Tag >
    class adaptive_iterator_impl< Iterator , Stepper , System , State , Tag , dense_output_stepper_tag >
        : public detail::ode_iterator_base< Iterator , Stepper , System , State , Tag >
    {
    private:

        typedef Stepper stepper_type;
        typedef System system_type;
        typedef typename boost::numeric::odeint::unwrap_reference< stepper_type >::type unwrapped_stepper_type;
        typedef State state_type;
        typedef typename traits::time_type< stepper_type >::type time_type;
        typedef typename traits::value_type< stepper_type >::type ode_value_type;
        #ifndef DOXYGEN_SKIP
        typedef detail::ode_iterator_base< Iterator , Stepper , System , State , Tag > base_type;
        #endif

   public:

        /**
         * \brief Constructs an adaptive_iterator. This constructor should be used to construct the begin iterator.
         *
         * \param stepper The stepper to use during the iteration.
         * \param sys The system function (ODE) to solve.
         * \param s The initial state.
         * \param t The initial time.
         * \param t_end The end time, at which the iteration should stop.
         * \param dt The initial time step.
         */
        adaptive_iterator_impl( stepper_type stepper , system_type sys , state_type &s , time_type t , time_type t_end , time_type dt )
            : base_type( stepper , sys , t , dt ) , m_t_end( t_end )
        {
            if( detail::less_eq_with_sign( this->m_t , this->m_t_end , this->m_dt ) )
            {
                unwrapped_stepper_type &st = this->m_stepper;
                st.initialize( s , this->m_t , this->m_dt );
            } else {
                this->m_at_end = true;
            }
        }

        /**
         * \brief Constructs an adaptive_iterator. This constructor should be used to construct the end iterator.
         *
         * \param stepper The stepper to use during the iteration.
         * \param sys The system function (ODE) to solve.
         * \param s The initial state.
         */
        adaptive_iterator_impl( stepper_type stepper , system_type sys , state_type& /* s */ )
            : base_type( stepper , sys ) { }

    protected:

        friend class boost::iterator_core_access;

        void increment()
        {
            unwrapped_stepper_type &stepper = this->m_stepper;
            if( detail::less_with_sign( this->m_t ,
                                        this->m_t_end ,
                                        stepper.current_time_step() ) )
            {
                if( detail::less_with_sign( this->m_t_end ,
                                            static_cast<time_type>(this->m_t + stepper.current_time_step()) ,
                                            stepper.current_time_step() ) )
                {
                    // make stpper to end exactly at t_end
                    stepper.initialize( stepper.current_state() , stepper.current_time() ,
                                        static_cast<time_type>(this->m_t_end-this->m_t) );
                }
                stepper.do_step( this->m_system );
                this->m_t = stepper.current_time();
            } else { // we have reached t_end
                this->m_at_end = true;
            }
        }

    public:
        const state_type& get_state() const
        {
            const unwrapped_stepper_type &stepper = this->m_stepper;
            return stepper.current_state();
        }

    private:
        time_type m_t_end;
    };

} // namespace odeint
} // namespace numeric
} // namespace boost

#endif // BOOST_NUMERIC_ODEINT_ITERATOR_DETAIL_ADAPTIVE_ITERATOR_IMPL_HPP_DEFINED


#include <boost/compute/algorithm/random_shuffle.hpp>


//=======================================================================
// Copyright 1997, 1998, 1999, 2000 University of Notre Dame.
// Authors: Andrew Lumsdaine, Lie-Quan Lee, Jeremy G. Siek
//
// This file is part of the Boost Graph Library
//
// You should have received a copy of the License Agreement for the
// Boost Graph Library along with the software; see the file LICENSE.
// If not, contact Office of Research, University of Notre Dame, Notre
// Dame, IN 46556.
//
// Permission to modify the code and to distribute modified code is
// granted, provided the text of this NOTICE is retained, a notice that
// the code was modified is included with the above COPYRIGHT NOTICE and
// with the COPYRIGHT NOTICE in the LICENSE file, and that the LICENSE
// file is distributed with the modified code.
//
// LICENSOR MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED.
// By way of example, but not limitation, Licensor MAKES NO
// REPRESENTATIONS OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY
// PARTICULAR PURPOSE OR THAT THE USE OF THE LICENSED SOFTWARE COMPONENTS
// OR DOCUMENTATION WILL NOT INFRINGE ANY PATENTS, COPYRIGHTS, TRADEMARKS
// OR OTHER RIGHTS.
//=======================================================================
#include <boost/config.hpp>
#include <iostream>
#include <vector>
#ifdef __GNUC__
#define __STL_NO_DRAND48
#endif
#include <algorithm>
#include <boost/pending/fibonacci_heap.hpp>
#include <boost/graph/graph_utility.hpp>
#include <boost/pending/indirect_cmp.hpp>

using namespace boost;

int
main()
{
  typedef std::vector<int> ValArray;
  typedef indirect_cmp<ValArray::const_iterator,std::less<int> > Cmp;
  int i;
  for (int N = 2; N < 200; ++N) {
    for (int t = 0; t < 10; ++t) {
      fibonacci_heap<int, std::less<int> > Q(N, std::less<int>());
      std::vector<int> v, w(N);

      for (int c = 0; c < w.size(); ++c)
        w[c] = c;
      std::random_shuffle(w.begin(), w.end());

      for (i = 0; i < N; ++i)
        Q.push(w[i]);

      for (i = 0; i < N; ++i) {
        v.push_back(Q.top());
        Q.pop();
      }
      std::sort(w.begin(), w.end());

      if (! std::equal(v.begin(), v.end(), w.begin())) {
        std::cout << std::endl << "heap sorted: ";
        std::copy(v.begin(), v.end(),
                  std::ostream_iterator<int>(std::cout," "));
        std::cout << std::endl << "correct: ";
        std::copy(w.begin(), w.end(),
                  std::ostream_iterator<int>(std::cout," "));
        std::cout << std::endl;
        return -1;
      }
    }
  }
  std::cout << "fibonacci heap passed test" << std::endl;
  return 0;
}