#include "headers/NVertex.hpp"
#include <armadillo>
#include <vector>
#include <omp.h>

using namespace arma;
using namespace std;

NVertex::NVertex() {
}

int NVertex::N(vector <Object *> &objetos, const int &cont_tri){

    mat aux;
    aux.zeros(cont_tri,3);

    #pragma omp parallel for
    for (unsigned int w=0;w<objetos.size();w++){
	if (objetos[w]->getTipo() == 1){

            vec turnA = objetos[w]->getA();
            vec turnB = objetos[w]->getB();
            vec turnC = objetos[w]->getC();

            for (unsigned int j=w+1;j<objetos.size();j++){
                if (objetos[j]->getTipo() == 1){

                    vec compareA = objetos[j]->getA();
                    vec compareB = objetos[j]->getB();
                    vec compareC = objetos[j]->getC();

                    if ((turnA(0) == compareA(0)) && (turnA(1) == compareA(1)) && (turnA(2) == compareA(2))){

			    aux.row(w) = aux.row(w) + objetos[j]->getNormal().t();
			    aux.row(j) = aux.row(j) + objetos[w]->getNormal().t();
                    }

                    if ((turnA(0) == compareB(0)) && (turnA(1) == compareB(1)) && (turnA(2) == compareB(2))){

			    aux.row(w) = aux.row(w) + objetos[j]->getNormal().t();
			    aux.row(j) = aux.row(j) + objetos[w]->getNormal().t();
                    }

                    if ((turnA(0) == compareC(0)) && (turnA(1) == compareC(1)) && (turnA(2) == compareC(2))){

			aux.row(w) = aux.row(w) + objetos[j]->getNormal().t();
			aux.row(j) = aux.row(j) + objetos[w]->getNormal().t();
                    }

                    if ((turnB(0) == compareA(0)) && (turnB(1) == compareA(1)) && (turnB(2) == compareA(2))){

			aux.row(w) = aux.row(w) + objetos[j]->getNormal().t();
			aux.row(j) = aux.row(j) + objetos[w]->getNormal().t();
                    }

                    if ((turnB(0) == compareB(0)) && (turnB(1) == compareB(1)) && (turnB(2) == compareB(2))){

			aux.row(w) = aux.row(w) + objetos[j]->getNormal().t();
			aux.row(j) = aux.row(j) + objetos[w]->getNormal().t();
                    }

                    if ((turnB(0) == compareC(0)) && (turnB(1) == compareC(1)) && (turnB(2) == compareC(2))){

                        aux.row(w) = aux.row(w) + objetos[j]->getNormal().t();
			aux.row(j) = aux.row(j) + objetos[w]->getNormal().t();
                    }

                    if ((turnC(0) == compareA(0)) && (turnC(1) == compareA(1)) && (turnC(2) == compareA(2))){

			aux.row(w) = aux.row(w) + objetos[j]->getNormal().t();
			aux.row(j) = aux.row(j) + objetos[w]->getNormal().t();
                    }

                    if ((turnC(0) == compareB(0)) && (turnC(1) == compareB(1)) && (turnC(2) == compareB(2))){

                        aux.row(w) = aux.row(w) + objetos[j]->getNormal().t();
			aux.row(j) = aux.row(j) + objetos[w]->getNormal().t();
                    }

                    if ((turnC(0) == compareC(0)) && (turnC(1) == compareC(1)) && (turnC(2) == compareC(2))){

			aux.row(w) = aux.row(w) + objetos[j]->getNormal().t();
			aux.row(j) = aux.row(j) + objetos[w]->getNormal().t();
                    }
		}
            }

            if ( !((aux(w,0) == 0) && (aux(w,1) == 0) && (aux(w,2) == 0))) {
                objetos[w]->setNormal(aux.row(w).t());
            }
        }
    }

    return 0;
}


// Software License for MTL
//
// Copyright (c) 2007 The Trustees of Indiana University.
//               2008 Dresden University of Technology and the Trustees of Indiana University.
//               2010 SimuNova UG (haftungsbeschränkt), www.simunova.com.
// All rights reserved.
// Authors: Peter Gottschling and Andrew Lumsdaine
//
// This file is part of the Matrix Template Library
//
// See also license.mtl.txt in the distribution.

#include <iostream>
#include <utility>
#include <cmath>
#include <boost/numeric/mtl/mtl.hpp>

using namespace std;

namespace std {

   template <typename T, typename U>
    inline ostream& operator<< (ostream& os, pair<T, U> const& p)
    {
        return os << '(' << p.first << ',' << p.second << ')';
    }
}

template <typename Matrix>
void init(Matrix& A)
{
    A= 0.0;
    mtl::mat::inserter<Matrix>  ins(A);

    ins[0][1] << 3; ins[1][4] << 7;
    ins[2][3] << -2; ins[2][4] << 5;
}

template <typename Coll, typename Pos>
void test(Coll& coll, const char* name, Pos exp_min, Pos exp_max)
{
    cout << "\n" << name << " =\n" << coll;

    Pos my_min= Pos(min_pos(coll));
    cout << "\nPosition of minimum is " << my_min << "\n";
    MTL_THROW_IF(my_min != exp_min, mtl::runtime_error("Minimum not at expected position"));

    Pos my_max= Pos(max_pos(coll));
    cout << "Position of maximum is " << my_max << "\n";
    MTL_THROW_IF(my_max != exp_max, mtl::runtime_error("Maximum not at expected position"));
}

int main(int, char**)
{
    using namespace mtl;

    dense_vector<double>  x(5);
    x= 1, 2, -3, 5, 4;
    dense2D<double>       A(3, 5);
    init(A);
    compressed2D<double>  B(3, 5);
    init(B);

    typedef mtl::Collection<dense2D<double> >::size_type size_type;
    std::pair<size_type, size_type> minp(2, 3), maxp(1, 4);

    test(x, "double vector", 2, 3);
    test(A, "dense matrix", minp, maxp);
    test(B, "sparse matrix", minp, maxp);

    return 0;
}


////////////////////////////////////////////////////////////////////////////////
//  Copyright (c) 2015 Bryce Adelstein Lelbach aka wash
//
//  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 <mdspan>

#include <boost/core/lightweight_test.hpp>

using std::is_same;

using std::experimental::dyn;
using std::experimental::dimensions;
using std::experimental::extract_dimensions_t;

int main()
{
    BOOST_TEST((is_same<
        extract_dimensions_t<int>, dimensions<>
    >::value));

    BOOST_TEST((is_same<
        extract_dimensions_t<int[ ]>, dimensions<dyn>
    >::value));

    BOOST_TEST((is_same<
        extract_dimensions_t<int[3]>, dimensions<3  >
    >::value));

    BOOST_TEST((is_same<
        extract_dimensions_t<int[ ][5]>, dimensions<dyn, 5>
    >::value));
    BOOST_TEST((is_same<
        extract_dimensions_t<int[3][5]>, dimensions<3,   5>
    >::value));

    BOOST_TEST((is_same<
        extract_dimensions_t<int[ ][5][9]>, dimensions<dyn, 5, 9>
    >::value));
    BOOST_TEST((is_same<
        extract_dimensions_t<int[3][5][9]>, dimensions<3,   5, 9>
    >::value));

    return boost::report_errors();
}


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

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

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

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

/*! \file test/crossassetmodel.hpp
    \brief cross asset model test
*/

#ifndef quantext_test_crossassetmodel_hpp
#define quantext_test_crossassetmodel_hpp

#include <boost/test/unit_test.hpp>

namespace testsuite {

//! CrossAssetModel tests
class CrossAssetModelTest {
public:
    /*! Test whether the prices of a bermudan swaption are consistent in the LGM model with the
     * NumericLgmSwaptionEngine, in the LGM model with the Gaussian1dSwaptionEngine and in the GSR model with the
     * Gaussian1dSwaptionEngine. */
    static void testBermudanLgm1fGsr();

    /*! Test if the price of a bermudan swaption is invariant under the LGM model invariances (scaling and shifting) in
     * the LGM model with the NumericLgmSwaptionEngine and in the LGM model with the Gaussian1dSwaptionEngine. */
    static void testBermudanLgmInvariances();

    /*! Test the non-standard Bermudan swaption engine against the standard engine */
    static void testNonstandardBermudanSwaption();

    /*! Calibrate the LGM and the GSR model to a coterminal swaption basket and compare the calibrated model parameters.
     * Perform the same calibration in the LGM model as a component of the CrossAssetModel and check whether the results
     * are the same and if other components are not affected by this calibration. */
    static void testLgm1fCalibration();

    /*! In a EUR-USD CrossAssetModel, test a Monte Carlo pricing of an USD cashflow under the EUR numeraire against the
     * analytical expectation. Perform similar checks for an USD zero bond under the EUR numeraire and an USD-EUR FX
     * option. */
    static void testCcyLgm3fForeignPayouts();

    /*! Compare the calibration of FX components in a EUR-USD model to those in a EUR-USD-GBP model, i.e. if the
     * calibration results in the smaller model stay the same when embed this model into a model with more components.
     */
    static void testLgm5fFxCalibration();

    /*! Test the calibration of all components of a EUR-USD-GBP CrossAssetModel (3 IR LGM models and 2 FX Black Scholes
     * models) by comparing the model prices and market prices of the calibration instruments. */
    static void testLgm5fFullCalibration();

    /*! Compare the analytical (unconditional) expectation and covariance matrix of the 5 dimensional stochastic process
     * of a EUR-USD-GBP CrossAssetModel at t=10 against Monte Carlo estimates using both an exact and an Euler
     * discretisation. */
    static void testLgm5fMoments();

    /*! Test whether the GSR model (i.e. the Hull White model under the T forward measure) is equivalent to the LGM
     * model with shift T in the sense that they generate identical paths up to numerical accuracy. */
    static void testLgmGsrEquivalence();

    /*! Test the pricing of a single cashflow at T=50 in a LGM model with Monte Carlo using different LGM shifts and
     * check whether the error estimates go to zero when the shift approaches T=50. */
    static void testLgmMcWithShift();

    /*! Test martingale properties analogous to the test above, but with dynamic credit compenent. */
    static void testIrFxCrMartingaleProperty();

    /*! Test moments analogous to the test above, but with dynamic credit compenent. */
    static void testIrFxCrMoments();

    /*! Test martingale properties analogous to the test above, but with dynamic inflation and credit compenent. */
    static void testIrFxInfCrMartingaleProperty();

    /*! Test moments analogous to the test above, but with dynamic inflation and credit compenent. */
    static void testIrFxInfCrMoments();

    /*! Test martingale properties analogous to the test above, but with dynamic inflation, credit, equity compenent. */
    static void testIrFxInfCrEqMartingaleProperty();

    /*! Test moments analogous to the test above, but with dynamic inflation, credit, equity compenent. */
    static void testIrFxInfCrEqMoments();

    /*! Test DK component calibration in alpha against MC full repricing of calibration instruments. */
    static void testCpiCalibrationByAlpha();

    /*! Test DK component calibration in H against MC full repricing of calibration instruments. */
    static void testCpiCalibrationByH();

    /*! Test CR component calibration against MC full repricing of calibration instruments. */
    static void testCrCalibration();

    /*! In a EUR-USD CrossAssetModel with two equities, test a Monte Carlo pricing of an equiy forward under the base
     *  currency numeraire against the analytical expectation. Perform similar checks for an equity option. */
    static void testEqLgm5fPayouts();

    /*! Test the calibration of all components of a full CrossAssetModel (3 IR LGM models and 2 FX Black Scholes
     * models and 2 equities) by comparing the model prices and market prices of the calibration instruments. */
    static void testEqLgm5fCalibration();

    /*! Compare the analytical (unconditional) expectation and covariance matrix of the 7 dimensional stochastic process
     * of a CrossAssetModel at t=10 against Monte Carlo estimates using both an exact and an Euler
     * discretisation. Special attention paid to the equity components of the process. */
    static void testEqLgm5fMoments();

    /*! Test whether the input correlation matrix for a CrossAssetModel with 1 up to 100 currencies is recovered as the
     * analytical and Euler correlation matrix estimated over a small time step dt. */
    static void testCorrelationRecovery();

    /*! Test correlation recovery analogous to the test above, but with dynamic credit compenent. */
    static void testIrFxCrCorrelationRecovery();

    /*! Test correlation recovery analogous to the test above, but with dynamic inflation and credit compenent. */
    static void testIrFxInfCrCorrelationRecovery();

    /*! Test correlation recovery analogous to the test above, but with dynamic inflation, credit, equity compenent. */
    static void testIrFxInfCrEqCorrelationRecovery();

    static boost::unit_test_framework::test_suite* suite();
};
} // namespace testsuite

#endif


//
//=======================================================================
// Copyright 2012
// Author: Alex Hagen-Zanker
//
// 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)
//=======================================================================
//
// The target visitor implements the dijkstra visitor and interruptor
// concepts. It returns true on do_interrupt once a desired set of target
// vertices is finalized (black)
//
//=======================================================================
//

#ifndef BLINK_GRAPH_DIJKSTRA_VISITOR_TARGET_VISITOR_HPP
#define BLINK_GRAPH_DIJKSTRA_VISITOR_TARGET_VISITOR_HPP

#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/property_map/property_map.hpp>
#include <boost/graph/properties.hpp>
#include <boost/graph/iteration_macros.hpp>
#include <boost/range/algorithm/for_each.hpp>

namespace blink{

template<typename Graph, typename ColorMap>
class target_visitor : public boost::default_dijkstra_visitor
{
public:
  typedef typename boost::property_traits<ColorMap>::value_type color_type;
  typedef typename boost::property_traits<ColorMap>::key_type vertex_descriptor;
  typedef boost::color_traits<color_type> color_traits;

  // m_progress_map keeps track of targets, it is not the same colormap
  // as used by the dijkstra shortest path algorithm
  // color_traits::white() not found and not required
  // color_traits::gray()  not found but required
  // color_traits::black() found

   target_visitor(const Graph& graph, ColorMap progress_map)
    : m_progress_map(progress_map), m_graph(&graph)
  {
    init_count();
  }

  void init_progress_map()
  {
    typename boost::graph_traits<Graph>::vertex_iterator ui, ui_end;
    for (boost::tie(ui, ui_end) = vertices(*m_graph); ui != ui_end; ++ui) {
       put(m_progress_map, *ui, color_traits::white() );
    }
  }

  void init_count()
  {
    m_num_white.reset(new size_t(num_vertices(*m_graph) ) );
    m_num_gray.reset(new size_t(0) );
  }

  template<typename U, typename G>
  inline void finish_vertex(const U& u, const G& g)
  {
    color_type& c = m_progress_map[u];
    if(c == color_traits::white() ) {
      c = color_traits::black();
      (*m_num_white)--;
    } else if(c == color_traits::gray() ) {
      c = color_traits::black();
      (*m_num_gray)--;
    } // else black{ do nothing }
  }

  inline bool do_interrupt() const {
    return (*m_num_gray) == 0;
  }

  void add_target(const vertex_descriptor& v)
  {
    if( get(m_progress_map,v) == color_traits::white() ) {
      put(m_progress_map, v, color_traits::gray() );
      (*m_num_white)--;
      (*m_num_gray)++;
    }
  }
  template<typename TargetRange>
  void add_targets(const TargetRange& r)
  {
    boost::range_iterator<const TargetRange>::type i = boost::begin(r);
    boost::range_iterator<const TargetRange>::type end = boost::end(r);
    for(; i != end; ++i) {
      add_target(*i);
    }
  }

  const Graph* m_graph;
  ColorMap m_progress_map;
  boost::shared_ptr<size_t> m_num_white;
  boost::shared_ptr<size_t> m_num_gray;
};

template<typename DijkstraState>
struct target_helper
{
  typedef typename DijkstraState::template param<boost::vertex_index_t>::type index_type;
  typedef typename DijkstraState::graph_type graph_type;

  typedef typename boost::graph_traits<graph_type>::vertex_descriptor vertex_descriptor;
  typedef typename vertex_property_map_helper<boost::default_color_type, graph_type, index_type> helper;
  typedef typename helper::type map_type;
  typedef typename target_visitor<graph_type, map_type> visitor_type;

  static map_type make_map(DijkstraState& state)
  {
    return helper::make(state.get_graph(), state.get<boost::vertex_index_t>());
  }

  static visitor_type make_visitor(DijkstraState& state)
  {
    map_type map = make_map(state);
    visitor_type visitor(state.get_graph(), map);
    visitor.init_count();
    visitor.init_progress_map();

    return visitor;
  }
};

template<typename Graph, typename Params>
struct target_helper_indirect : target_helper<typename dijkstra_state_helper<Graph, Params>::type>
{};

template<typename DijkstraState>
typename typename target_helper<DijkstraState>::visitor_type
make_target_visitor(DijkstraState& state)
{
  return target_helper<DijkstraState>::make_visitor(state);
}

} // namespace boost;

#endif //BLINK_GRAPH_DIJKSTRA_VISITOR_TARGET_VISITOR_HPP


//
// Copyright © 2017 Arm Ltd. All rights reserved.
// SPDX-License-Identifier: MIT
//

#include <boost/test/unit_test.hpp>
#include "ParserFlatbuffersSerializeFixture.hpp"
#include "../Deserializer.hpp"

#include <string>
#include <iostream>

BOOST_AUTO_TEST_SUITE(Deserializer)

struct Convolution2dFixture : public ParserFlatbuffersSerializeFixture
{
    explicit Convolution2dFixture(const std::string & inputShape1,
                                  const std::string & outputShape,
                                  const std::string & weightsShape,
                                  const std::string & dataType)
    {
        m_JsonString = R"(
        {
            inputIds: [0],
            outputIds: [2],
            layers: [{
                layer_type: "InputLayer",
                layer: {
                    base: {
                        layerBindingId: 0,
                        base: {
                            index: 0,
                            layerName: "InputLayer",
                            layerType: "Input",
                            inputSlots: [{
                                index: 0,
                                connection: {sourceLayerIndex:0, outputSlotIndex:0 },
                                }],
                            outputSlots: [{
                                index: 0,
                                tensorInfo: {
                                    dimensions: )" + inputShape1 + R"(,
                                    dataType: )" + dataType + R"(,
                                    quantizationScale: 0.5,
                                    quantizationOffset: 0
                                    },
                                }]
                            },
                        }
                    },
                },
            {
            layer_type: "Convolution2dLayer",
            layer : {
                base: {
                    index:1,
                    layerName: "Convolution2dLayer",
                    layerType: "Convolution2d",
                    inputSlots: [{
                            index: 0,
                            connection: {sourceLayerIndex:0, outputSlotIndex:0 },
                        }],
                    outputSlots: [{
                        index: 0,
                        tensorInfo: {
                            dimensions: )" + outputShape + R"(,
                            dataType: )" + dataType + R"(
                        },
                        }],
                    },
                descriptor: {
                    padLeft: 1,
                    padRight: 1,
                    padTop: 1,
                    padBottom: 1,
                    strideX: 2,
                    strideY: 2,
                    biasEnabled: false,
                    dataLayout: NHWC
                    },
                weights: {
                    info: {
                             dimensions: )" + weightsShape + R"(,
                             dataType: )" + dataType + R"(
                         },
                    data_type: IntData,
                    data: {
                        data: [
                            1082130432, 1084227584, 1086324736,
                            0 ,0 ,0 ,
                            1077936128, 1073741824, 1065353216
                            ],
                        }
                    }
                },
            },
            {
            layer_type: "OutputLayer",
            layer: {
                base:{
                    layerBindingId: 0,
                    base: {
                        index: 2,
                        layerName: "OutputLayer",
                        layerType: "Output",
                        inputSlots: [{
                            index: 0,
                            connection: {sourceLayerIndex:1, outputSlotIndex:0 },
                        }],
                        outputSlots: [ {
                            index: 0,
                            tensorInfo: {
                                dimensions: )" + outputShape + R"(,
                                dataType: )" + dataType + R"(
                            },
                        }],
                    }
                }},
            }]
        }
        )";
        Setup();
    }
};

struct SimpleConvolution2dFixture : Convolution2dFixture
{
    SimpleConvolution2dFixture() : Convolution2dFixture("[ 1, 5, 5, 1 ]",
                                     "[ 1, 3, 3, 1 ]",
                                     "[ 1, 3, 3, 1 ]",
                                     "Float32") {}
};

BOOST_FIXTURE_TEST_CASE(Convolution2dFloat32, SimpleConvolution2dFixture)
{
    RunTest<4, armnn::DataType::Float32>(
            0,
            {{"InputLayer", {1, 5, 2, 3, 5, 8, 7, 3, 6, 3, 3, 3, 9, 1, 9, 4, 1, 8, 1, 3, 6, 8, 1, 9, 2}}},
            {{"OutputLayer", {23, 33, 24, 91, 99, 48, 26, 50, 19}}});
}

BOOST_AUTO_TEST_SUITE_END()


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

#include "main.h"

#include <Eigen/CXX11/Tensor>

using Eigen::Tensor;

void test_simple_patch()
{
  Tensor<float, 4> tensor(2,3,5,7);
  tensor.setRandom();
  Tensor<float, 4, RowMajor> tensor_row_major = tensor.swap_layout();
  VERIFY_IS_EQUAL(tensor.dimension(0), tensor_row_major.dimension(3));
  VERIFY_IS_EQUAL(tensor.dimension(1), tensor_row_major.dimension(2));
  VERIFY_IS_EQUAL(tensor.dimension(2), tensor_row_major.dimension(1));
  VERIFY_IS_EQUAL(tensor.dimension(3), tensor_row_major.dimension(0));

  // Single pixel patch: ColMajor
  Tensor<float, 5> single_pixel_patch;
  single_pixel_patch = tensor.extract_image_patches(1, 1);
  VERIFY_IS_EQUAL(single_pixel_patch.dimension(0), 2);
  VERIFY_IS_EQUAL(single_pixel_patch.dimension(1), 1);
  VERIFY_IS_EQUAL(single_pixel_patch.dimension(2), 1);
  VERIFY_IS_EQUAL(single_pixel_patch.dimension(3), 3*5);
  VERIFY_IS_EQUAL(single_pixel_patch.dimension(4), 7);

  // Single pixel patch: RowMajor
  Tensor<float, 5, RowMajor> single_pixel_patch_row_major;
  single_pixel_patch_row_major = tensor_row_major.extract_image_patches(1, 1);
  VERIFY_IS_EQUAL(single_pixel_patch_row_major.dimension(0), 7);
  VERIFY_IS_EQUAL(single_pixel_patch_row_major.dimension(1), 3*5);
  VERIFY_IS_EQUAL(single_pixel_patch_row_major.dimension(2), 1);
  VERIFY_IS_EQUAL(single_pixel_patch_row_major.dimension(3), 1);
  VERIFY_IS_EQUAL(single_pixel_patch_row_major.dimension(4), 2);

  for (int i = 0; i < tensor.size(); ++i) {
    // ColMajor
    if (tensor.data()[i] != single_pixel_patch.data()[i]) {
      std::cout << "Mismatch detected at index " << i << " : "
           << tensor.data()[i] << " vs " << single_pixel_patch.data()[i]
           << std::endl;
    }
    VERIFY_IS_EQUAL(single_pixel_patch.data()[i], tensor.data()[i]);
    // RowMajor
    if (tensor_row_major.data()[i] != single_pixel_patch_row_major.data()[i]) {
      std::cout << "Mismatch detected at index " << i << " : "
           << tensor.data()[i] << " vs "
           << single_pixel_patch_row_major.data()[i] << std::endl;
    }
    VERIFY_IS_EQUAL(single_pixel_patch_row_major.data()[i],
                    tensor_row_major.data()[i]);
    VERIFY_IS_EQUAL(tensor.data()[i], tensor_row_major.data()[i]);
    VERIFY_IS_EQUAL(single_pixel_patch.data()[i],
                    single_pixel_patch_row_major.data()[i]);
  }

  // Entire image patch: ColMajor
  Tensor<float, 5> entire_image_patch;
  entire_image_patch = tensor.extract_image_patches(3, 5);
  VERIFY_IS_EQUAL(entire_image_patch.dimension(0), 2);
  VERIFY_IS_EQUAL(entire_image_patch.dimension(1), 3);
  VERIFY_IS_EQUAL(entire_image_patch.dimension(2), 5);
  VERIFY_IS_EQUAL(entire_image_patch.dimension(3), 3*5);
  VERIFY_IS_EQUAL(entire_image_patch.dimension(4), 7);

  // Entire image patch: RowMajor
  Tensor<float, 5, RowMajor> entire_image_patch_row_major;
  entire_image_patch_row_major = tensor_row_major.extract_image_patches(3, 5);
  VERIFY_IS_EQUAL(entire_image_patch_row_major.dimension(0), 7);
  VERIFY_IS_EQUAL(entire_image_patch_row_major.dimension(1), 3*5);
  VERIFY_IS_EQUAL(entire_image_patch_row_major.dimension(2), 5);
  VERIFY_IS_EQUAL(entire_image_patch_row_major.dimension(3), 3);
  VERIFY_IS_EQUAL(entire_image_patch_row_major.dimension(4), 2);

  for (int i = 0; i < 3; ++i) {
    for (int j = 0; j < 5; ++j) {
      int patchId = i+3*j;
      for (int r = 0; r < 3; ++r) {
        for (int c = 0; c < 5; ++c) {
          for (int d = 0; d < 2; ++d) {
            for (int b = 0; b < 7; ++b) {
              float expected = 0.0f;
              float expected_row_major = 0.0f;
              if (r-1+i >= 0 && c-2+j >= 0 && r-1+i < 3 && c-2+j < 5) {
                expected = tensor(d, r-1+i, c-2+j, b);
                expected_row_major = tensor_row_major(b, c-2+j, r-1+i, d);
              }
              // ColMajor
              if (entire_image_patch(d, r, c, patchId, b) != expected) {
                std::cout << "Mismatch detected at index i=" << i << " j=" << j << " r=" << r << " c=" << c << " d=" << d << " b=" << b << std::endl;
              }
              VERIFY_IS_EQUAL(entire_image_patch(d, r, c, patchId, b), expected);
              // RowMajor
              if (entire_image_patch_row_major(b, patchId, c, r, d) !=
                  expected_row_major) {
                std::cout << "Mismatch detected at index i=" << i << " j=" << j
                     << " r=" << r << " c=" << c << " d=" << d << " b=" << b
                     << std::endl;
              }
              VERIFY_IS_EQUAL(entire_image_patch_row_major(b, patchId, c, r, d),
                              expected_row_major);
              // Check that ColMajor and RowMajor agree.
              VERIFY_IS_EQUAL(expected, expected_row_major);
            }
          }
        }
      }
    }
  }

  // 2D patch: ColMajor
  Tensor<float, 5> twod_patch;
  twod_patch = tensor.extract_image_patches(2, 2);
  VERIFY_IS_EQUAL(twod_patch.dimension(0), 2);
  VERIFY_IS_EQUAL(twod_patch.dimension(1), 2);
  VERIFY_IS_EQUAL(twod_patch.dimension(2), 2);
  VERIFY_IS_EQUAL(twod_patch.dimension(3), 3*5);
  VERIFY_IS_EQUAL(twod_patch.dimension(4), 7);

  // 2D patch: RowMajor
  Tensor<float, 5, RowMajor> twod_patch_row_major;
  twod_patch_row_major = tensor_row_major.extract_image_patches(2, 2);
  VERIFY_IS_EQUAL(twod_patch_row_major.dimension(0), 7);
  VERIFY_IS_EQUAL(twod_patch_row_major.dimension(1), 3*5);
  VERIFY_IS_EQUAL(twod_patch_row_major.dimension(2), 2);
  VERIFY_IS_EQUAL(twod_patch_row_major.dimension(3), 2);
  VERIFY_IS_EQUAL(twod_patch_row_major.dimension(4), 2);

  // Based on the calculation described in TensorTraits.h, padding happens to be 0.
  int row_padding = 0;
  int col_padding = 0;
  int stride = 1;

  for (int i = 0; i < 3; ++i) {
    for (int j = 0; j < 5; ++j) {
      int patchId = i+3*j;
      for (int r = 0; r < 2; ++r) {
        for (int c = 0; c < 2; ++c) {
          for (int d = 0; d < 2; ++d) {
            for (int b = 0; b < 7; ++b) {
              float expected = 0.0f;
              float expected_row_major = 0.0f;
              int row_offset = r*stride + i - row_padding;
              int col_offset = c*stride + j - col_padding;
              // ColMajor
              if (row_offset >= 0 && col_offset >= 0 && row_offset < tensor.dimension(1) && col_offset < tensor.dimension(2)) {
                expected = tensor(d, row_offset, col_offset, b);
              }
              if (twod_patch(d, r, c, patchId, b) != expected) {
                std::cout << "Mismatch detected at index i=" << i << " j=" << j << " r=" << r << " c=" << c << " d=" << d << " b=" << b << std::endl;
              }
              VERIFY_IS_EQUAL(twod_patch(d, r, c, patchId, b), expected);

              // RowMajor
              if (row_offset >= 0 && col_offset >= 0 && row_offset < tensor_row_major.dimension(2) && col_offset < tensor_row_major.dimension(1)) {
                expected_row_major = tensor_row_major(b, col_offset, row_offset, d);

              }
              if (twod_patch_row_major(b, patchId, c, r, d) != expected_row_major) {
                std::cout << "Mismatch detected at index i=" << i << " j=" << j << " r=" << r << " c=" << c << " d=" << d << " b=" << b << std::endl;
              }
              VERIFY_IS_EQUAL(twod_patch_row_major(b, patchId, c, r, d), expected_row_major);
              // Check that ColMajor and RowMajor agree.
              VERIFY_IS_EQUAL(expected, expected_row_major);
            }
          }
        }
      }
    }
  }
}

// Verifies VALID padding (no padding) with incrementing values.
void test_patch_padding_valid()
{
  int input_depth = 3;
  int input_rows = 3;
  int input_cols = 3;
  int input_batches = 1;
  int ksize = 2;  // Corresponds to the Rows and Cols for tensor.extract_image_patches<>.
  int stride = 2;  // Only same stride is supported.
  Tensor<float, 4> tensor(input_depth, input_rows, input_cols, input_batches);
  // Initializes tensor with incrementing numbers.
  for (int i = 0; i < tensor.size(); ++i) {
    tensor.data()[i] = i + 1;
  }
  // ColMajor
  Tensor<float, 5> result = tensor.extract_image_patches(ksize, ksize, stride, stride, 1, 1, PADDING_VALID);

  VERIFY_IS_EQUAL(result.dimension(0), input_depth);  // depth
  VERIFY_IS_EQUAL(result.dimension(1), ksize);  // kernel rows
  VERIFY_IS_EQUAL(result.dimension(2), ksize);  // kernel cols
  VERIFY_IS_EQUAL(result.dimension(3), 1);  // number of patches
  VERIFY_IS_EQUAL(result.dimension(4), input_batches);  // number of batches

  // RowMajor
  Tensor<float, 4, RowMajor> tensor_row_major = tensor.swap_layout();
  VERIFY_IS_EQUAL(tensor.dimension(0), tensor_row_major.dimension(3));
  VERIFY_IS_EQUAL(tensor.dimension(1), tensor_row_major.dimension(2));
  VERIFY_IS_EQUAL(tensor.dimension(2), tensor_row_major.dimension(1));
  VERIFY_IS_EQUAL(tensor.dimension(3), tensor_row_major.dimension(0));

  Tensor<float, 5, RowMajor> result_row_major = tensor_row_major.extract_image_patches(ksize, ksize, stride, stride, 1, 1, PADDING_VALID);
  VERIFY_IS_EQUAL(result.dimension(0), result_row_major.dimension(4));
  VERIFY_IS_EQUAL(result.dimension(1), result_row_major.dimension(3));
  VERIFY_IS_EQUAL(result.dimension(2), result_row_major.dimension(2));
  VERIFY_IS_EQUAL(result.dimension(3), result_row_major.dimension(1));
  VERIFY_IS_EQUAL(result.dimension(4), result_row_major.dimension(0));

  // No padding is carried out.
  int row_padding = 0;
  int col_padding = 0;

  for (int i = 0; (i+stride+ksize-1) < input_rows; i += stride) {  // input rows
    for (int j = 0; (j+stride+ksize-1) < input_cols; j += stride) {  // input cols
      int patchId = i+input_rows*j;
      for (int r = 0; r < ksize; ++r) {  // patch rows
        for (int c = 0; c < ksize; ++c) {  // patch cols
          for (int d = 0; d < input_depth; ++d) {  // depth
            for (int b = 0; b < input_batches; ++b) {  // batch
              float expected = 0.0f;
              float expected_row_major = 0.0f;
              int row_offset = r + i - row_padding;
              int col_offset = c + j - col_padding;
              if (row_offset >= 0 && col_offset >= 0 && row_offset < input_rows && col_offset < input_cols) {
                expected = tensor(d, row_offset, col_offset, b);
                expected_row_major = tensor_row_major(b, col_offset, row_offset, d);
              }
              // ColMajor
              if (result(d, r, c, patchId, b) != expected) {
                std::cout << "Mismatch detected at index i=" << i << " j=" << j << " r=" << r << " c=" << c << " d=" << d << " b=" << b << std::endl;
              }
              VERIFY_IS_EQUAL(result(d, r, c, patchId, b), expected);
              // RowMajor
              if (result_row_major(b, patchId, c, r, d) != expected_row_major) {
                std::cout << "Mismatch detected at index i=" << i << " j=" << j << " r=" << r << " c=" << c << " d=" << d << " b=" << b << std::endl;
              }
              VERIFY_IS_EQUAL(result_row_major(b, patchId, c, r, d), expected_row_major);
              // Check that ColMajor and RowMajor agree.
              VERIFY_IS_EQUAL(expected, expected_row_major);
            }
          }
        }
      }
    }
  }
}

// Verifies VALID padding (no padding) with the same value.
void test_patch_padding_valid_same_value()
{
  int input_depth = 1;
  int input_rows = 5;
  int input_cols = 5;
  int input_batches = 2;
  int ksize = 3;  // Corresponds to the Rows and Cols for tensor.extract_image_patches<>.
  int stride = 2;  // Only same stride is supported.
  // ColMajor
  Tensor<float, 4> tensor(input_depth, input_rows, input_cols, input_batches);
  tensor = tensor.constant(11.0f);
  Tensor<float, 5> result = tensor.extract_image_patches(ksize, ksize, stride, stride, 1, 1, PADDING_VALID);

  VERIFY_IS_EQUAL(result.dimension(0), input_depth);  // depth
  VERIFY_IS_EQUAL(result.dimension(1), ksize);  // kernel rows
  VERIFY_IS_EQUAL(result.dimension(2), ksize);  // kernel cols
  VERIFY_IS_EQUAL(result.dimension(3), 4);  // number of patches
  VERIFY_IS_EQUAL(result.dimension(4), input_batches);  // number of batches

  // RowMajor
  Tensor<float, 4, RowMajor> tensor_row_major = tensor.swap_layout();
  VERIFY_IS_EQUAL(tensor.dimension(0), tensor_row_major.dimension(3));
  VERIFY_IS_EQUAL(tensor.dimension(1), tensor_row_major.dimension(2));
  VERIFY_IS_EQUAL(tensor.dimension(2), tensor_row_major.dimension(1));
  VERIFY_IS_EQUAL(tensor.dimension(3), tensor_row_major.dimension(0));

  Tensor<float, 5, RowMajor> result_row_major = tensor_row_major.extract_image_patches(ksize, ksize, stride, stride, 1, 1, PADDING_VALID);
  VERIFY_IS_EQUAL(result.dimension(0), result_row_major.dimension(4));
  VERIFY_IS_EQUAL(result.dimension(1), result_row_major.dimension(3));
  VERIFY_IS_EQUAL(result.dimension(2), result_row_major.dimension(2));
  VERIFY_IS_EQUAL(result.dimension(3), result_row_major.dimension(1));
  VERIFY_IS_EQUAL(result.dimension(4), result_row_major.dimension(0));

  // No padding is carried out.
  int row_padding = 0;
  int col_padding = 0;

  for (int i = 0; (i+stride+ksize-1) <= input_rows; i += stride) {  // input rows
    for (int j = 0; (j+stride+ksize-1) <= input_cols; j += stride) {  // input cols
      int patchId = i+input_rows*j;
      for (int r = 0; r < ksize; ++r) {  // patch rows
        for (int c = 0; c < ksize; ++c) {  // patch cols
          for (int d = 0; d < input_depth; ++d) {  // depth
            for (int b = 0; b < input_batches; ++b) {  // batch
              float expected = 0.0f;
              float expected_row_major = 0.0f;
              int row_offset = r + i - row_padding;
              int col_offset = c + j - col_padding;
              if (row_offset >= 0 && col_offset >= 0 && row_offset < input_rows && col_offset < input_cols) {
                expected = tensor(d, row_offset, col_offset, b);
                expected_row_major = tensor_row_major(b, col_offset, row_offset, d);
              }
              // ColMajor
              if (result(d, r, c, patchId, b) != expected) {
                std::cout << "Mismatch detected at index i=" << i << " j=" << j << " r=" << r << " c=" << c << " d=" << d << " b=" << b << std::endl;
              }
              VERIFY_IS_EQUAL(result(d, r, c, patchId, b), expected);
              // RowMajor
              if (result_row_major(b, patchId, c, r, d) != expected_row_major) {
                std::cout << "Mismatch detected at index i=" << i << " j=" << j << " r=" << r << " c=" << c << " d=" << d << " b=" << b << std::endl;
              }
              VERIFY_IS_EQUAL(result_row_major(b, patchId, c, r, d), expected_row_major);
              // Check that ColMajor and RowMajor agree.
              VERIFY_IS_EQUAL(expected, expected_row_major);
            }
          }
        }
      }
    }
  }
}

// Verifies SAME padding.
void test_patch_padding_same()
{
  int input_depth = 3;
  int input_rows = 4;
  int input_cols = 2;
  int input_batches = 1;
  int ksize = 2;  // Corresponds to the Rows and Cols for tensor.extract_image_patches<>.
  int stride = 2;  // Only same stride is supported.
  // ColMajor
  Tensor<float, 4> tensor(input_depth, input_rows, input_cols, input_batches);
  // Initializes tensor with incrementing numbers.
  for (int i = 0; i < tensor.size(); ++i) {
    tensor.data()[i] = i + 1;
  }
  Tensor<float, 5> result = tensor.extract_image_patches(ksize, ksize, stride, stride, PADDING_SAME);

  VERIFY_IS_EQUAL(result.dimension(0), input_depth);  // depth
  VERIFY_IS_EQUAL(result.dimension(1), ksize);  // kernel rows
  VERIFY_IS_EQUAL(result.dimension(2), ksize);  // kernel cols
  VERIFY_IS_EQUAL(result.dimension(3), 2);  // number of patches
  VERIFY_IS_EQUAL(result.dimension(4), input_batches);  // number of batches

  // RowMajor
  Tensor<float, 4, RowMajor> tensor_row_major = tensor.swap_layout();
  VERIFY_IS_EQUAL(tensor.dimension(0), tensor_row_major.dimension(3));
  VERIFY_IS_EQUAL(tensor.dimension(1), tensor_row_major.dimension(2));
  VERIFY_IS_EQUAL(tensor.dimension(2), tensor_row_major.dimension(1));
  VERIFY_IS_EQUAL(tensor.dimension(3), tensor_row_major.dimension(0));

  Tensor<float, 5, RowMajor> result_row_major = tensor_row_major.extract_image_patches(ksize, ksize, stride, stride, PADDING_SAME);
  VERIFY_IS_EQUAL(result.dimension(0), result_row_major.dimension(4));
  VERIFY_IS_EQUAL(result.dimension(1), result_row_major.dimension(3));
  VERIFY_IS_EQUAL(result.dimension(2), result_row_major.dimension(2));
  VERIFY_IS_EQUAL(result.dimension(3), result_row_major.dimension(1));
  VERIFY_IS_EQUAL(result.dimension(4), result_row_major.dimension(0));

  // Based on the calculation described in TensorTraits.h, padding happens to be
  // 0.
  int row_padding = 0;
  int col_padding = 0;

  for (int i = 0; (i+stride+ksize-1) <= input_rows; i += stride) {  // input rows
    for (int j = 0; (j+stride+ksize-1) <= input_cols; j += stride) {  // input cols
      int patchId = i+input_rows*j;
      for (int r = 0; r < ksize; ++r) {  // patch rows
        for (int c = 0; c < ksize; ++c) {  // patch cols
          for (int d = 0; d < input_depth; ++d) {  // depth
            for (int b = 0; b < input_batches; ++b) {  // batch
              float expected = 0.0f;
              float expected_row_major = 0.0f;
              int row_offset = r*stride + i - row_padding;
              int col_offset = c*stride + j - col_padding;
              if (row_offset >= 0 && col_offset >= 0 && row_offset < input_rows && col_offset < input_cols) {
                expected = tensor(d, row_offset, col_offset, b);
                expected_row_major = tensor_row_major(b, col_offset, row_offset, d);
              }
              // ColMajor
              if (result(d, r, c, patchId, b) != expected) {
                std::cout << "Mismatch detected at index i=" << i << " j=" << j << " r=" << r << " c=" << c << " d=" << d << " b=" << b << std::endl;
              }
              VERIFY_IS_EQUAL(result(d, r, c, patchId, b), expected);
              // RowMajor
              if (result_row_major(b, patchId, c, r, d) != expected_row_major) {
                std::cout << "Mismatch detected at index i=" << i << " j=" << j << " r=" << r << " c=" << c << " d=" << d << " b=" << b << std::endl;
              }
              VERIFY_IS_EQUAL(result_row_major(b, patchId, c, r, d), expected_row_major);
              // Check that ColMajor and RowMajor agree.
              VERIFY_IS_EQUAL(expected, expected_row_major);
            }
          }
        }
      }
    }
  }
}

// Verifies that SAME padding, when computed as negative values, will be clipped
// to zero.
void test_patch_padding_same_negative_padding_clip_to_zero() {
  int input_depth = 1;
  int input_rows = 15;
  int input_cols = 1;
  int input_batches = 1;
  int ksize = 1;  // Corresponds to the Rows and Cols for
                  // tensor.extract_image_patches<>.
  int row_stride = 5;
  int col_stride = 1;
  // ColMajor
  Tensor<float, 4> tensor(input_depth, input_rows, input_cols, input_batches);
  // Initializes tensor with incrementing numbers.
  for (int i = 0; i < tensor.size(); ++i) {
    tensor.data()[i] = i + 1;
  }
  Tensor<float, 5> result = tensor.extract_image_patches(
      ksize, ksize, row_stride, col_stride, 1, 1, PADDING_SAME);
  // row padding will be computed as -2 originally and then be clipped to 0.
  VERIFY_IS_EQUAL(result.coeff(0), 1.0f);
  VERIFY_IS_EQUAL(result.coeff(1), 6.0f);
  VERIFY_IS_EQUAL(result.coeff(2), 11.0f);

  VERIFY_IS_EQUAL(result.dimension(0), input_depth);    // depth
  VERIFY_IS_EQUAL(result.dimension(1), ksize);          // kernel rows
  VERIFY_IS_EQUAL(result.dimension(2), ksize);          // kernel cols
  VERIFY_IS_EQUAL(result.dimension(3), 3);              // number of patches
  VERIFY_IS_EQUAL(result.dimension(4), input_batches);  // number of batches

  // RowMajor
  Tensor<float, 4, RowMajor> tensor_row_major = tensor.swap_layout();
  VERIFY_IS_EQUAL(tensor.dimension(0), tensor_row_major.dimension(3));
  VERIFY_IS_EQUAL(tensor.dimension(1), tensor_row_major.dimension(2));
  VERIFY_IS_EQUAL(tensor.dimension(2), tensor_row_major.dimension(1));
  VERIFY_IS_EQUAL(tensor.dimension(3), tensor_row_major.dimension(0));

  Tensor<float, 5, RowMajor> result_row_major =
      tensor_row_major.extract_image_patches(ksize, ksize, row_stride,
                                             col_stride, 1, 1, PADDING_SAME);
  VERIFY_IS_EQUAL(result_row_major.coeff(0), 1.0f);
  VERIFY_IS_EQUAL(result_row_major.coeff(1), 6.0f);
  VERIFY_IS_EQUAL(result_row_major.coeff(2), 11.0f);

  VERIFY_IS_EQUAL(result.dimension(0), result_row_major.dimension(4));
  VERIFY_IS_EQUAL(result.dimension(1), result_row_major.dimension(3));
  VERIFY_IS_EQUAL(result.dimension(2), result_row_major.dimension(2));
  VERIFY_IS_EQUAL(result.dimension(3), result_row_major.dimension(1));
  VERIFY_IS_EQUAL(result.dimension(4), result_row_major.dimension(0));
}

void test_patch_no_extra_dim()
{
  Tensor<float, 3> tensor(2,3,5);
  tensor.setRandom();
  Tensor<float, 3, RowMajor> tensor_row_major = tensor.swap_layout();
  VERIFY_IS_EQUAL(tensor.dimension(0), tensor_row_major.dimension(2));
  VERIFY_IS_EQUAL(tensor.dimension(1), tensor_row_major.dimension(1));
  VERIFY_IS_EQUAL(tensor.dimension(2), tensor_row_major.dimension(0));

  // Single pixel patch: ColMajor
  Tensor<float, 4> single_pixel_patch;
  single_pixel_patch = tensor.extract_image_patches(1, 1);
  VERIFY_IS_EQUAL(single_pixel_patch.dimension(0), 2);
  VERIFY_IS_EQUAL(single_pixel_patch.dimension(1), 1);
  VERIFY_IS_EQUAL(single_pixel_patch.dimension(2), 1);
  VERIFY_IS_EQUAL(single_pixel_patch.dimension(3), 3*5);

  // Single pixel patch: RowMajor
  Tensor<float, 4, RowMajor> single_pixel_patch_row_major;
  single_pixel_patch_row_major = tensor_row_major.extract_image_patches(1, 1);
  VERIFY_IS_EQUAL(single_pixel_patch_row_major.dimension(0), 3*5);
  VERIFY_IS_EQUAL(single_pixel_patch_row_major.dimension(1), 1);
  VERIFY_IS_EQUAL(single_pixel_patch_row_major.dimension(2), 1);
  VERIFY_IS_EQUAL(single_pixel_patch_row_major.dimension(3), 2);

  for (int i = 0; i < tensor.size(); ++i) {
    // ColMajor
    if (tensor.data()[i] != single_pixel_patch.data()[i]) {
      std::cout << "Mismatch detected at index " << i << " : " << tensor.data()[i] << " vs " << single_pixel_patch.data()[i] << std::endl;
    }
    VERIFY_IS_EQUAL(single_pixel_patch.data()[i], tensor.data()[i]);
    // RowMajor
    if (tensor_row_major.data()[i] != single_pixel_patch_row_major.data()[i]) {
      std::cout << "Mismatch detected at index " << i << " : "
           << tensor.data()[i] << " vs "
           << single_pixel_patch_row_major.data()[i] << std::endl;
    }
    VERIFY_IS_EQUAL(single_pixel_patch_row_major.data()[i],
                    tensor_row_major.data()[i]);
    VERIFY_IS_EQUAL(tensor.data()[i], tensor_row_major.data()[i]);
    VERIFY_IS_EQUAL(single_pixel_patch.data()[i],
                    single_pixel_patch_row_major.data()[i]);
  }

  // Entire image patch: ColMajor
  Tensor<float, 4> entire_image_patch;
  entire_image_patch = tensor.extract_image_patches(3, 5);
  VERIFY_IS_EQUAL(entire_image_patch.dimension(0), 2);
  VERIFY_IS_EQUAL(entire_image_patch.dimension(1), 3);
  VERIFY_IS_EQUAL(entire_image_patch.dimension(2), 5);
  VERIFY_IS_EQUAL(entire_image_patch.dimension(3), 3*5);

  // Entire image patch: RowMajor
  Tensor<float, 4, RowMajor> entire_image_patch_row_major;
  entire_image_patch_row_major = tensor_row_major.extract_image_patches(3, 5);
  VERIFY_IS_EQUAL(entire_image_patch_row_major.dimension(0), 3*5);
  VERIFY_IS_EQUAL(entire_image_patch_row_major.dimension(1), 5);
  VERIFY_IS_EQUAL(entire_image_patch_row_major.dimension(2), 3);
  VERIFY_IS_EQUAL(entire_image_patch_row_major.dimension(3), 2);

  for (int i = 0; i < 3; ++i) {
    for (int j = 0; j < 5; ++j) {
      int patchId = i+3*j;
      for (int r = 0; r < 3; ++r) {
        for (int c = 0; c < 5; ++c) {
          for (int d = 0; d < 2; ++d) {
            float expected = 0.0f;
            float expected_row_major = 0.0f;
            if (r-1+i >= 0 && c-2+j >= 0 && r-1+i < 3 && c-2+j < 5) {
              expected = tensor(d, r-1+i, c-2+j);
              expected_row_major = tensor_row_major(c-2+j, r-1+i, d);
            }
            // ColMajor
            if (entire_image_patch(d, r, c, patchId) != expected) {
              std::cout << "Mismatch detected at index i=" << i << " j=" << j << " r=" << r << " c=" << c << " d=" << d << std::endl;
            }
            VERIFY_IS_EQUAL(entire_image_patch(d, r, c, patchId), expected);
            // RowMajor
            if (entire_image_patch_row_major(patchId, c, r, d) !=
                expected_row_major) {
              std::cout << "Mismatch detected at index i=" << i << " j=" << j << " r=" << r << " c=" << c << " d=" << d << std::endl;
            }
            VERIFY_IS_EQUAL(entire_image_patch_row_major(patchId, c, r, d),
                            expected_row_major);
            // Check that ColMajor and RowMajor agree.
            VERIFY_IS_EQUAL(expected, expected_row_major);
          }
        }
      }
    }
  }

  // 2D patch: ColMajor
  Tensor<float, 4> twod_patch;
  twod_patch = tensor.extract_image_patches(2, 2);
  VERIFY_IS_EQUAL(twod_patch.dimension(0), 2);
  VERIFY_IS_EQUAL(twod_patch.dimension(1), 2);
  VERIFY_IS_EQUAL(twod_patch.dimension(2), 2);
  VERIFY_IS_EQUAL(twod_patch.dimension(3), 3*5);

  // 2D patch: RowMajor
  Tensor<float, 4, RowMajor> twod_patch_row_major;
  twod_patch_row_major = tensor_row_major.extract_image_patches(2, 2);
  VERIFY_IS_EQUAL(twod_patch_row_major.dimension(0), 3*5);
  VERIFY_IS_EQUAL(twod_patch_row_major.dimension(1), 2);
  VERIFY_IS_EQUAL(twod_patch_row_major.dimension(2), 2);
  VERIFY_IS_EQUAL(twod_patch_row_major.dimension(3), 2);

  // Based on the calculation described in TensorTraits.h, padding happens to be 0.
  int row_padding = 0;
  int col_padding = 0;
  int stride = 1;

  for (int i = 0; i < 3; ++i) {
    for (int j = 0; j < 5; ++j) {
      int patchId = i+3*j;
      for (int r = 0; r < 2; ++r) {
        for (int c = 0; c < 2; ++c) {
          for (int d = 0; d < 2; ++d) {
            float expected = 0.0f;
            float expected_row_major = 0.0f;
            int row_offset = r*stride + i - row_padding;
            int col_offset = c*stride + j - col_padding;
            // ColMajor
            if (row_offset >= 0 && col_offset >= 0 && row_offset < tensor.dimension(1) && col_offset < tensor.dimension(2)) {
              expected = tensor(d, row_offset, col_offset);
            }
            if (twod_patch(d, r, c, patchId) != expected) {
              std::cout << "Mismatch detected at index i=" << i << " j=" << j << " r=" << r << " c=" << c << " d=" << d << std::endl;
            }
            VERIFY_IS_EQUAL(twod_patch(d, r, c, patchId), expected);
            // RowMajor
            if (row_offset >= 0 && col_offset >= 0 && row_offset < tensor_row_major.dimension(1) && col_offset < tensor_row_major.dimension(0)) {
              expected_row_major = tensor_row_major(col_offset, row_offset, d);
            }
            if (twod_patch_row_major(patchId, c, r, d) != expected_row_major) {
              std::cout << "Mismatch detected at index i=" << i << " j=" << j << " r=" << r << " c=" << c << " d=" << d << std::endl;
            }
            VERIFY_IS_EQUAL(twod_patch_row_major(patchId, c, r, d), expected_row_major);
            // Check that ColMajor and RowMajor agree.
            VERIFY_IS_EQUAL(expected, expected_row_major);
          }
        }
      }
    }
  }
}

void test_imagenet_patches()
{
  // Test the code on typical configurations used by the 'imagenet' benchmarks at
  // https://github.com/soumith/convnet-benchmarks
  // ColMajor
  Tensor<float, 4> l_in(3, 128, 128, 16);
  l_in.setRandom();
  Tensor<float, 5> l_out = l_in.extract_image_patches(11, 11);
  VERIFY_IS_EQUAL(l_out.dimension(0), 3);
  VERIFY_IS_EQUAL(l_out.dimension(1), 11);
  VERIFY_IS_EQUAL(l_out.dimension(2), 11);
  VERIFY_IS_EQUAL(l_out.dimension(3), 128*128);
  VERIFY_IS_EQUAL(l_out.dimension(4), 16);

  // RowMajor
  Tensor<float, 5, RowMajor> l_out_row_major = l_in.swap_layout().extract_image_patches(11, 11);
  VERIFY_IS_EQUAL(l_out_row_major.dimension(0), 16);
  VERIFY_IS_EQUAL(l_out_row_major.dimension(1), 128*128);
  VERIFY_IS_EQUAL(l_out_row_major.dimension(2), 11);
  VERIFY_IS_EQUAL(l_out_row_major.dimension(3), 11);
  VERIFY_IS_EQUAL(l_out_row_major.dimension(4), 3);

  for (int b = 0; b < 16; ++b) {
    for (int i = 0; i < 128; ++i) {
      for (int j = 0; j < 128; ++j) {
        int patchId = i+128*j;
        for (int c = 0; c < 11; ++c) {
          for (int r = 0; r < 11; ++r) {
            for (int d = 0; d < 3; ++d) {
              float expected = 0.0f;
              if (r-5+i >= 0 && c-5+j >= 0 && r-5+i < 128 && c-5+j < 128) {
                expected = l_in(d, r-5+i, c-5+j, b);
              }
              // ColMajor
              if (l_out(d, r, c, patchId, b) != expected) {
                std::cout << "Mismatch detected at index i=" << i << " j=" << j << " r=" << r << " c=" << c << " d=" << d << " b=" << b << std::endl;
              }
              VERIFY_IS_EQUAL(l_out(d, r, c, patchId, b), expected);
              // RowMajor
              if (l_out_row_major(b, patchId, c, r, d) !=
                  expected) {
                std::cout << "Mismatch detected at index i=" << i << " j=" << j
                     << " r=" << r << " c=" << c << " d=" << d << " b=" << b
                     << std::endl;
              }
              VERIFY_IS_EQUAL(l_out_row_major(b, patchId, c, r, d),
                              expected);
            }
          }
        }
      }
    }
  }

  // ColMajor
  l_in.resize(16, 64, 64, 32);
  l_in.setRandom();
  l_out = l_in.extract_image_patches(9, 9);
  VERIFY_IS_EQUAL(l_out.dimension(0), 16);
  VERIFY_IS_EQUAL(l_out.dimension(1), 9);
  VERIFY_IS_EQUAL(l_out.dimension(2), 9);
  VERIFY_IS_EQUAL(l_out.dimension(3), 64*64);
  VERIFY_IS_EQUAL(l_out.dimension(4), 32);

  // RowMajor
  l_out_row_major = l_in.swap_layout().extract_image_patches(9, 9);
  VERIFY_IS_EQUAL(l_out_row_major.dimension(0), 32);
  VERIFY_IS_EQUAL(l_out_row_major.dimension(1), 64*64);
  VERIFY_IS_EQUAL(l_out_row_major.dimension(2), 9);
  VERIFY_IS_EQUAL(l_out_row_major.dimension(3), 9);
  VERIFY_IS_EQUAL(l_out_row_major.dimension(4), 16);

  for (int b = 0; b < 32; ++b) {
    for (int i = 0; i < 64; ++i) {
      for (int j = 0; j < 64; ++j) {
        int patchId = i+64*j;
        for (int c = 0; c < 9; ++c) {
          for (int r = 0; r < 9; ++r) {
            for (int d = 0; d < 16; ++d) {
              float expected = 0.0f;
              if (r-4+i >= 0 && c-4+j >= 0 && r-4+i < 64 && c-4+j < 64) {
                expected = l_in(d, r-4+i, c-4+j, b);
              }
              // ColMajor
              if (l_out(d, r, c, patchId, b) != expected) {
                std::cout << "Mismatch detected at index i=" << i << " j=" << j << " r=" << r << " c=" << c << " d=" << d << " b=" << b << std::endl;
              }
              VERIFY_IS_EQUAL(l_out(d, r, c, patchId, b), expected);
              // RowMajor
              if (l_out_row_major(b, patchId, c, r, d) != expected) {
                std::cout << "Mismatch detected at index i=" << i << " j=" << j << " r=" << r << " c=" << c << " d=" << d << " b=" << b << std::endl;
              }
              VERIFY_IS_EQUAL(l_out_row_major(b, patchId, c, r, d), expected);
            }
          }
        }
      }
    }
  }

  // ColMajor
  l_in.resize(32, 16, 16, 32);
  l_in.setRandom();
  l_out = l_in.extract_image_patches(7, 7);
  VERIFY_IS_EQUAL(l_out.dimension(0), 32);
  VERIFY_IS_EQUAL(l_out.dimension(1), 7);
  VERIFY_IS_EQUAL(l_out.dimension(2), 7);
  VERIFY_IS_EQUAL(l_out.dimension(3), 16*16);
  VERIFY_IS_EQUAL(l_out.dimension(4), 32);

  // RowMajor
  l_out_row_major = l_in.swap_layout().extract_image_patches(7, 7);
  VERIFY_IS_EQUAL(l_out_row_major.dimension(0), 32);
  VERIFY_IS_EQUAL(l_out_row_major.dimension(1), 16*16);
  VERIFY_IS_EQUAL(l_out_row_major.dimension(2), 7);
  VERIFY_IS_EQUAL(l_out_row_major.dimension(3), 7);
  VERIFY_IS_EQUAL(l_out_row_major.dimension(4), 32);

  for (int b = 0; b < 32; ++b) {
    for (int i = 0; i < 16; ++i) {
      for (int j = 0; j < 16; ++j) {
        int patchId = i+16*j;
        for (int c = 0; c < 7; ++c) {
          for (int r = 0; r < 7; ++r) {
            for (int d = 0; d < 32; ++d) {
              float expected = 0.0f;
              if (r-3+i >= 0 && c-3+j >= 0 && r-3+i < 16 && c-3+j < 16) {
                expected = l_in(d, r-3+i, c-3+j, b);
              }
              // ColMajor
              if (l_out(d, r, c, patchId, b) != expected) {
                std::cout << "Mismatch detected at index i=" << i << " j=" << j << " r=" << r << " c=" << c << " d=" << d << " b=" << b << std::endl;
              }
              VERIFY_IS_EQUAL(l_out(d, r, c, patchId, b), expected);
              // RowMajor
              if (l_out_row_major(b, patchId, c, r, d) != expected) {
                std::cout << "Mismatch detected at index i=" << i << " j=" << j << " r=" << r << " c=" << c << " d=" << d << " b=" << b << std::endl;
              }
              VERIFY_IS_EQUAL(l_out_row_major(b, patchId, c, r, d), expected);
            }
          }
        }
      }
    }
  }

  // ColMajor
  l_in.resize(64, 13, 13, 32);
  l_in.setRandom();
  l_out = l_in.extract_image_patches(3, 3);
  VERIFY_IS_EQUAL(l_out.dimension(0), 64);
  VERIFY_IS_EQUAL(l_out.dimension(1), 3);
  VERIFY_IS_EQUAL(l_out.dimension(2), 3);
  VERIFY_IS_EQUAL(l_out.dimension(3), 13*13);
  VERIFY_IS_EQUAL(l_out.dimension(4), 32);

  // RowMajor
  l_out_row_major = l_in.swap_layout().extract_image_patches(3, 3);
  VERIFY_IS_EQUAL(l_out_row_major.dimension(0), 32);
  VERIFY_IS_EQUAL(l_out_row_major.dimension(1), 13*13);
  VERIFY_IS_EQUAL(l_out_row_major.dimension(2), 3);
  VERIFY_IS_EQUAL(l_out_row_major.dimension(3), 3);
  VERIFY_IS_EQUAL(l_out_row_major.dimension(4), 64);

  for (int b = 0; b < 32; ++b) {
    for (int i = 0; i < 13; ++i) {
      for (int j = 0; j < 13; ++j) {
        int patchId = i+13*j;
        for (int c = 0; c < 3; ++c) {
          for (int r = 0; r < 3; ++r) {
            for (int d = 0; d < 64; ++d) {
              float expected = 0.0f;
              if (r-1+i >= 0 && c-1+j >= 0 && r-1+i < 13 && c-1+j < 13) {
                expected = l_in(d, r-1+i, c-1+j, b);
              }
              // ColMajor
              if (l_out(d, r, c, patchId, b) != expected) {
                std::cout << "Mismatch detected at index i=" << i << " j=" << j << " r=" << r << " c=" << c << " d=" << d << " b=" << b << std::endl;
              }
              VERIFY_IS_EQUAL(l_out(d, r, c, patchId, b), expected);
              // RowMajor
              if (l_out_row_major(b, patchId, c, r, d) != expected) {
                std::cout << "Mismatch detected at index i=" << i << " j=" << j << " r=" << r << " c=" << c << " d=" << d << " b=" << b << std::endl;
              }
              VERIFY_IS_EQUAL(l_out_row_major(b, patchId, c, r, d), expected);
            }
          }
        }
      }
    }
  }
}

void test_cxx11_tensor_image_patch()
{
  CALL_SUBTEST_1(test_simple_patch());
  CALL_SUBTEST_2(test_patch_no_extra_dim());
  CALL_SUBTEST_3(test_patch_padding_valid());
  CALL_SUBTEST_4(test_patch_padding_valid_same_value());
  CALL_SUBTEST_5(test_patch_padding_same());
  CALL_SUBTEST_6(test_imagenet_patches());
  CALL_SUBTEST_7(test_patch_padding_same_negative_padding_clip_to_zero());
}


#include <iostream>
#include <cmath>
#include <algorithm>
#include <vector>
#include <boost/multiprecision/cpp_int.hpp>
using namespace boost::multiprecision;
using namespace std;
int main() {
    int n; cin >> n;
    vector<int> a(n, 0), b(n, 0);
    cpp_int ans = 0;
    for (int i = 0; i < n; i++) cin >> a[i];
    for (int i = 0; i < n; i++) cin >> b[i];
    for (int i = 0; i < n; i++) ans += a[i] * b[i];
    if (ans == 0) cout << "Yes" << endl;
    else cout << "No" << endl;
}


/* boost random/fisher_f_distribution.hpp header file
 *
 * Copyright Steven Watanabe 2011
 * 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://www.boost.org for most recent version including documentation.
 *
 * $Id$
 */

#ifndef BOOST_RANDOM_FISHER_F_DISTRIBUTION_HPP
#define BOOST_RANDOM_FISHER_F_DISTRIBUTION_HPP

#include <iosfwd>
#include <istream>
#include <boost/config.hpp>
#include <boost/limits.hpp>
#include <boost/random/detail/operators.hpp>
#include <boost/random/chi_squared_distribution.hpp>

namespace boost {
namespace random {

/**
 * The Fisher F distribution is a real valued distribution with two
 * parameters m and n.
 *
 * It has \f$\displaystyle p(x) =
 *   \frac{\Gamma((m+n)/2)}{\Gamma(m/2)\Gamma(n/2)}
 *   \left(\frac{m}{n}\right)^{m/2}
 *   x^{(m/2)-1} \left(1+\frac{mx}{n}\right)^{-(m+n)/2}
 * \f$.
 */
template<class RealType = double>
class fisher_f_distribution {
public:
    typedef RealType result_type;
    typedef RealType input_type;

    class param_type {
    public:
        typedef fisher_f_distribution distribution_type;

        /**
         * Constructs a @c param_type from the "m" and "n" parameters
         * of the distribution.
         *
         * Requires: m > 0 and n > 0
         */
        explicit param_type(RealType m_arg = RealType(1.0),
                            RealType n_arg = RealType(1.0))
          : _m(m_arg), _n(n_arg)
        {}

        /** Returns the "m" parameter of the distribtuion. */
        RealType m() const { return _m; }
        /** Returns the "n" parameter of the distribution. */
        RealType n() const { return _n; }

        /** Writes a @c param_type to a @c std::ostream. */
        BOOST_RANDOM_DETAIL_OSTREAM_OPERATOR(os, param_type, parm)
        { os << parm._m << ' ' << parm._n; return os; }

        /** Reads a @c param_type from a @c std::istream. */
        BOOST_RANDOM_DETAIL_ISTREAM_OPERATOR(is, param_type, parm)
        { is >> parm._m >> std::ws >> parm._n; return is; }

        /** Returns true if the two sets of parameters are the same. */
        BOOST_RANDOM_DETAIL_EQUALITY_OPERATOR(param_type, lhs, rhs)
        { return lhs._m == rhs._m && lhs._n == rhs._n; }

        /** Returns true if the two sets of parameters are the different. */
        BOOST_RANDOM_DETAIL_INEQUALITY_OPERATOR(param_type)

    private:
        RealType _m;
        RealType _n;
    };

    /**
     * Constructs a @c fisher_f_distribution from its "m" and "n" parameters.
     *
     * Requires: m > 0 and n > 0
     */
    explicit fisher_f_distribution(RealType m_arg = RealType(1.0),
                                   RealType n_arg = RealType(1.0))
      : _impl_m(m_arg), _impl_n(n_arg)
    {}
    /** Constructs an @c fisher_f_distribution from its parameters. */
    explicit fisher_f_distribution(const param_type& parm)
      : _impl_m(parm.m()), _impl_n(parm.n())
    {}

    /**
     * Returns a random variate distributed according to the
     * F distribution.
     */
    template<class URNG>
    RealType operator()(URNG& urng)
    {
        return (_impl_m(urng) * n()) / (_impl_n(urng) * m());
    }

    /**
     * Returns a random variate distributed according to the
     * F distribution with parameters specified by @c param.
     */
    template<class URNG>
    RealType operator()(URNG& urng, const param_type& parm) const
    {
        return fisher_f_distribution(parm)(urng);
    }

    /** Returns the "m" parameter of the distribution. */
    RealType m() const { return _impl_m.n(); }
    /** Returns the "n" parameter of the distribution. */
    RealType n() const { return _impl_n.n(); }

    /** Returns the smallest value that the distribution can produce. */
    RealType min BOOST_PREVENT_MACRO_SUBSTITUTION () const { return 0; }
    /** Returns the largest value that the distribution can produce. */
    RealType max BOOST_PREVENT_MACRO_SUBSTITUTION () const
    { return std::numeric_limits<RealType>::infinity(); }

    /** Returns the parameters of the distribution. */
    param_type param() const { return param_type(m(), n()); }
    /** Sets the parameters of the distribution. */
    void param(const param_type& parm)
    {
        typedef chi_squared_distribution<RealType> impl_type;
        typename impl_type::param_type m_param(parm.m());
        _impl_m.param(m_param);
        typename impl_type::param_type n_param(parm.n());
        _impl_n.param(n_param);
    }

    /**
     * Effects: Subsequent uses of the distribution do not depend
     * on values produced by any engine prior to invoking reset.
     */
    void reset() { }

    /** Writes an @c fisher_f_distribution to a @c std::ostream. */
    BOOST_RANDOM_DETAIL_OSTREAM_OPERATOR(os, fisher_f_distribution, fd)
    {
        os << fd.param();
        return os;
    }

    /** Reads an @c fisher_f_distribution from a @c std::istream. */
    BOOST_RANDOM_DETAIL_ISTREAM_OPERATOR(is, fisher_f_distribution, fd)
    {
        param_type parm;
        if(is >> parm) {
            fd.param(parm);
        }
        return is;
    }

    /**
     * Returns true if the two instances of @c fisher_f_distribution will
     * return identical sequences of values given equal generators.
     */
    BOOST_RANDOM_DETAIL_EQUALITY_OPERATOR(fisher_f_distribution, lhs, rhs)
    { return lhs._impl_m == rhs._impl_m && lhs._impl_n == rhs._impl_n; }

    /**
     * Returns true if the two instances of @c fisher_f_distribution will
     * return different sequences of values given equal generators.
     */
    BOOST_RANDOM_DETAIL_INEQUALITY_OPERATOR(fisher_f_distribution)

private:
    chi_squared_distribution<RealType> _impl_m;
    chi_squared_distribution<RealType> _impl_n;
};

} // namespace random
} // namespace boost

#endif // BOOST_RANDOM_EXTREME_VALUE_DISTRIBUTION_HPP


//
// Copyright (c) 2018 CNRS
// Authors: Pierre Fernbach
//
// This file is part of bezier_COM_traj
// hpp-core is free software: you can redistribute it
// and/or modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation, either version
// 3 of the License, or (at your option) any later version.
//
// hpp-core is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Lesser Public License for more details.  You should have
// received a copy of the GNU Lesser General Public License along with
// hpp-core  If not, see
// <http://www.gnu.org/licenses/>.

#define BOOST_TEST_MODULE transition
#include <boost/test/included/unit_test.hpp>
#include <hpp/bezier-com-traj/solve.hh>
#include <hpp/bezier-com-traj/common_solve_methods.hh>
#include <hpp/bezier-com-traj/data.hh>
#include <hpp/centroidal-dynamics/centroidal_dynamics.hh>
#include "test_helper.hh"
#include <hpp/spline/bezier_curve.h>

using namespace bezier_com_traj;
const double T = 1.5;

ProblemData buildPData(const centroidal_dynamics::EquilibriumAlgorithm algo = centroidal_dynamics::EQUILIBRIUM_ALGORITHM_PP){
    ProblemData pData;
    pData.c0_ = Vector3(0,0.5,5.);
    pData.c1_ = Vector3(2,-0.5,5.);
    pData.dc0_ = Vector3::Zero();
    pData.dc1_ = Vector3::Zero();
    pData.ddc0_ = Vector3::Zero();
    pData.ddc1_ = Vector3::Zero();
    pData.constraints_.flag_ = INIT_POS | INIT_VEL | END_VEL | END_POS;

    MatrixX3 normals(2,3),positions(2,3);
    normals.block<1,3>(0,0)=Vector3(0,0,1);
    positions.block<1,3>(0,0)=Vector3(0,0.1,0);
    normals.block<1,3>(1,0)=Vector3(0,0,1);
    positions.block<1,3>(1,0)=Vector3(0,-0.1,0);
    std::pair<MatrixX3, MatrixX3> contacts = computeRectangularContacts(normals,positions,LX,LY);
    pData.contacts_.push_back(new centroidal_dynamics::Equilibrium(ComputeContactCone(contacts.first,contacts.second,algo)));

    return pData;
}

std::vector<point_t> generate_wps(){
    return computeConstantWaypoints(buildPData(),T);
}

bezier_wp_t::t_point_t generate_wps_symbolic(){

    return computeConstantWaypointsSymbolic(buildPData(),T);
}

VectorX eval(const waypoint_t& w, const point_t& x){
    return w.first*x + w.second;
}

void vectorEqual(const VectorX& a , const VectorX& b, const double EPS = 1e-14){
    BOOST_CHECK_EQUAL(a.size(),b.size());
    BOOST_CHECK((a-b).norm() < EPS);
}

BOOST_AUTO_TEST_SUITE( symbolic )

BOOST_AUTO_TEST_CASE(symbolic_eval_c){
    std::vector<point_t> pts = generate_wps();
    bezier_wp_t::t_point_t wps =  generate_wps_symbolic();
    point_t y(1,0.2,4.5);
    pts[2] = y;

    bezier_t c (pts.begin(),pts.end(),T);
    bezier_wp_t c_sym (wps.begin(),wps.end(),T);

    double t = 0.;
    while(t<T){
        vectorEqual(c(t),eval(c_sym(t),y));
        t += 0.01;
    }
}

BOOST_AUTO_TEST_CASE(symbolic_eval_dc){
    std::vector<point_t> pts = generate_wps();
    bezier_wp_t::t_point_t wps =  generate_wps_symbolic();
    point_t y(1,0.2,4.5);
    pts[2] = y;

    bezier_t c (pts.begin(),pts.end(),T);
    bezier_t dc = c.compute_derivate(1);
    bezier_wp_t c_sym (wps.begin(),wps.end(),T);
    bezier_wp_t dc_sym = c_sym.compute_derivate(1);

    double t = 0.;
    while(t<T){
        vectorEqual(dc(t),eval(dc_sym(t),y));
        t += 0.01;
    }
}

BOOST_AUTO_TEST_CASE(symbolic_eval_ddc){
    std::vector<point_t> pts = generate_wps();
    bezier_wp_t::t_point_t wps =  generate_wps_symbolic();
    point_t y(1,0.2,4.5);
    pts[2] = y;

    bezier_t c (pts.begin(),pts.end(),T);
    bezier_t ddc = c.compute_derivate(2);
    bezier_wp_t c_sym (wps.begin(),wps.end(),T);
    bezier_wp_t ddc_sym = c_sym.compute_derivate(2);

    double t = 0.;
    while(t<T){
        vectorEqual(ddc(t),eval(ddc_sym(t),y),1e-10);
        t += 0.01;
    }
}

BOOST_AUTO_TEST_CASE(symbolic_eval_jc){
    std::vector<point_t> pts = generate_wps();
    bezier_wp_t::t_point_t wps =  generate_wps_symbolic();
    point_t y(1,0.2,4.5);
    pts[2] = y;

    bezier_t c (pts.begin(),pts.end(),T);
    bezier_t jc = c.compute_derivate(3);
    bezier_wp_t c_sym (wps.begin(),wps.end(),T);
    bezier_wp_t jc_sym = c_sym.compute_derivate(3);

    double t = 0.;
    while(t<T){
        vectorEqual(jc(t),eval(jc_sym(t),y),1e-10);
        t += 0.01;
    }
}

BOOST_AUTO_TEST_CASE(symbolic_split_c){
    std::vector<point_t> pts = generate_wps();
    bezier_wp_t::t_point_t wps =  generate_wps_symbolic();
    point_t y(1,0.2,4.5);
    pts[2] = y;

    bezier_t c (pts.begin(),pts.end(),T);
    bezier_wp_t c_sym (wps.begin(),wps.end(),T);

    double a,b,t,t1,t2;
    for(size_t i = 0 ; i < 100 ; ++i){
        a = (rand()/(double)RAND_MAX ) * T;
        b = (rand()/(double)RAND_MAX ) * T;
        t1 = std::min(a,b);
        t2 = std::max(a,b);
       // std::cout<<"try extract between : ["<<t1<<";"<<t2<<"] "<<std::endl;
        bezier_t c_e = c.extract(t1,t2);
        bezier_wp_t c_sym_e = c_sym.extract(t1,t2);
        t = t1;
        while(t < t2){
            vectorEqual(c_e(t-t1),eval(c_sym_e(t-t1),y));
            vectorEqual(c(t),eval(c_sym_e(t-t1),y));
            t+= 0.01;
        }
    }
}

BOOST_AUTO_TEST_CASE(symbolic_split_c_bench){
    using namespace std;

    std::vector<point_t> pts = generate_wps();
    bezier_wp_t::t_point_t wps =  generate_wps_symbolic();
    point_t y(1,0.2,4.5);
    pts[2] = y;

    bezier_wp_t c_sym (wps.begin(),wps.end(),T);

    std::vector<double> values;
    for (int i =0; i < 100000; ++i)
        values.push_back((double)rand()/RAND_MAX);

    clock_t s0,e0;
    std::pair<bezier_wp_t,bezier_wp_t> splitted = c_sym.split(0.5);
    s0 = clock();
    for(std::vector<double>::const_iterator cit = values.begin(); cit != values.end(); ++cit)
    {
        splitted = c_sym.split(*cit);
    }
    e0 = clock();

    std::cout<<"Time required to split a c curve : "<<((double)(e0-s0)/CLOCKS_PER_SEC)/100.<<" ms "<<std::endl;
}

BOOST_AUTO_TEST_CASE(symbolic_split_w){
    bezier_wp_t::t_point_t wps =  computeWwaypoints(buildPData(),T);
    point_t y(1,0.2,4.5);

    bezier_wp_t w (wps.begin(),wps.end(),T);

    double a,b,t,t1,t2;
    for(size_t i = 0 ; i < 100 ; ++i){
        a = (rand()/(double)RAND_MAX ) * T;
        b = (rand()/(double)RAND_MAX ) * T;
        t1 = std::min(a,b);
        t2 = std::max(a,b);
       // std::cout<<"try extract between : ["<<t1<<";"<<t2<<"] "<<std::endl;
        bezier_wp_t w_e = w.extract(t1,t2);
        t = t1;
        while(t < t2){
            vectorEqual(eval(w(t),y),eval(w_e(t-t1),y),1e-12);
            t+= 0.01;
        }
    }
}

BOOST_AUTO_TEST_CASE(symbolic_split_w_bench){
    bezier_wp_t::t_point_t wps =  computeWwaypoints(buildPData(),T);
    point_t y(1,0.2,4.5);

    bezier_wp_t w (wps.begin(),wps.end(),T);

    std::vector<double> values;
    for (int i =0; i < 100000; ++i)
        values.push_back((double)rand()/RAND_MAX);

    clock_t s0,e0;
    std::pair<bezier_wp_t,bezier_wp_t> splitted = w.split(0.5);
    s0 = clock();
    for(std::vector<double>::const_iterator cit = values.begin(); cit != values.end(); ++cit)
    {
        splitted = w.split(*cit);
    }
    e0 = clock();

    std::cout<<"Time required to split a w curve : "<<((double)(e0-s0)/CLOCKS_PER_SEC)/100.<<" ms "<<std::endl;

}

BOOST_AUTO_TEST_SUITE_END()


#pragma once
#include <tuple>
#include <boost/serialization/access.hpp>

#include "Node.hxx"
#include "Tensor.hxx"

class Epsilon : public Tensor {
 private:
  char i1, i2, i3, i4;

  friend class boost::serialization::access;

 public:
  Epsilon () = default;
  Epsilon(char const i1, char const i2, char const i3, char const i4);

  char order () const override;

  void exchangeTensorIndices (std::map<char, char> const & exchange_map) override;
  int sortIndices () override;

  int evaluate(std::map <char, char> const & eval_map) const override;
  mpq_class symmetrize() override;
  bool containsIndex (char i) const override;
  std::string print () const override;
  std::string printMaple () const override;
  int applyTensorSymmetries (int parity) override;

  bool lessThan(Node const * other) const override;
  bool equals(Node const * other) const override;

  std::unique_ptr<Node> clone () const override;

  std::tuple<int, char, std::map<char, char>> multiplyWithOther3 (char j1, char j2, char j3) const;

  template<class Archive>
  void serialize (Archive & ar, unsigned int const version);

  ~Epsilon() = default;
};