//============================================================================
// Name         : dnatemplatecalcfuncs.hpp
// Author       : Roger Fraser
// Contributors :
// Version      : 1.00
// Copyright    : Copyright 2017 Geoscience Australia
//
//                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.
//
// Description  : Advanced and common calculation functions using standard
//				  data types
//============================================================================

#ifndef DNATEMPLATECALCFUNCS_H_
#define DNATEMPLATECALCFUNCS_H_

#if defined(_MSC_VER)
	#if defined(LIST_INCLUDES_ON_BUILD)
		#pragma message("  " __FILE__)
	#endif
#endif

#include <algorithm>
#include <numeric>
#include <functional>
#include <sstream>
#include <string>
#include <vector>
#include <stdlib.h>
#include <math.h>
#include <iostream>

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

#include <include/config/dnatypes.hpp>
#include <include/config/dnaconsts.hpp>

using namespace std;
using namespace boost;

template <class U>
U sumOfConsecutiveIntegers(const U& max, const U& min = 1)
{
	//U q(static_cast<U>(average<double, U>(max, min) * max));
	U sum(0);
	for (U u=min; u<=max; u++)
		sum += u;
	return sum;
}

template <typename T, typename U>
T removeNegativeZero(const T& t, const U& precision)
{
	if (t < 0.0 || t == T(-0.0))
	{
		T v(floor((t * pow(10.0, precision)) + 0.5));
		if (fabs(v) > 0.)
			// t is non-zero at precision
			return t;
		// t is a negative zero
		return 0.;
	}
	return t;
}

template <typename T>
bool are_floats_equal(const T& lhs, const T& rhs, const T epsilon=PRECISION_1E6)
{
	return (fabs(lhs - rhs) < epsilon);
}

template <typename T, typename U, typename iterator>
T average(const iterator begin, const iterator end, U& sum)
{
	U n(static_cast<UINT32>(std::distance(begin, end)));
	sum = accumulate(begin, end, 0);
	return static_cast<T>(sum) / n;
}

template <class T, class U>
T average(const U& a, const U& b)
{
	T t(a + b);
	return t / 2.;
}

template <class T>
T average(const T& a, const T& b)
{
	T t(a + b);
	return (t / (T)(2.));
}

template <class T, typename U>
T minVal(const T& lhs, const U& rhs)
{
	if (lhs < rhs)
		return lhs;
	else
		return rhs;
}

template <class T, typename U>
T maxVal(const T& lhs, const U& rhs)
{
	if (lhs > rhs)
		return lhs;
	else
		return rhs;
}

// use this for longitude values only, where 180 degrees
// marks the boundary between east (positive) and west (negative)
template <class T>
T Rad180Mod(const T &dValue)
{
	if (dValue < -PI)
		 return dValue + (PI + PI);
	else if (dValue > PI)
		 return dValue - (PI + PI);
	return dValue;
}

template <class T>
T degrees_to_radians_(T& degrees)
{
	return (degrees * DEG_TO_RAD);
}

template <class T>
T Radians(T& degrees)
{
	return degrees_to_radians_(degrees);
}

template <class T>
T Radians(const T& degrees)
{
	return degrees_to_radians_(degrees);
}

template <class T>
void Radians(T* degrees)
{
	*degrees = degrees_to_radians_(*degrees);
}

template <class T>
T radians_to_degrees_(T& radians)
{
	return (radians * RAD_TO_DEG);
}

template <class T>
T Degrees(T& radians)
{
	return radians_to_degrees_(radians);
}

template <class T>
T Degrees(const T& radians)
{
	return radians_to_degrees_(radians);
}

template <class T>
void Degrees(T* radians)
{
	*radians = radians_to_degrees_(*radians);
}

// use DegreesL(const T& radians) for longitude values only, where 180 degrees
// marks the boundary between east (positive) and west (negative)
template <class T>
T DegreesL(const T& radians)
{
	return (Rad180Mod(radians) * RAD_TO_DEG);
}

// use DegreesL(T* dValue) for longitude values only, where 180 degrees
// marks the boundary between east (positive) and west (negative)
template <class T>
void DegreesL(T* dValue)
{
	*dValue = (Rad180Mod(*dValue) * RAD_TO_DEG);
}

// Seconds from Radians
template <class T>
T Seconds(const T& radians)
{
	return radians * RAD_TO_SEC;
}

template <class T>
T Seconds(T* radians)
{
	return *radians * RAD_TO_SEC;
}

template <class T>
T SecondstoRadians(const T& seconds)
{
	return seconds / static_cast<T>(RAD_TO_SEC);
}

template <class T, typename U>
void DegtoDms(const T& dDegrees, U* dDegMinSec)
{
	T d, m, s;

	*dDegMinSec = fabs(dDegrees);		 // retain original value
	d = floor(*dDegMinSec);
	m = floor((((*dDegMinSec) - d) * 60.0));
	s = ((*dDegMinSec) - d - (m/60.0)) * 3600.0;
	if (fabs(s - 60.0) < 0.000000001)
	{
		s = 0.0;
		m += 1.0;
	}
	*dDegMinSec = d + (m/100.0) + (s/10000.0);
	if (dDegrees < 0.0)
		*dDegMinSec *= -1;
}

// DegtoDms helper
template <class T>
T DegtoDms(const T& dDegrees)
{
	T dDegMinSec;
	DegtoDms(dDegrees, &dDegMinSec);
	return dDegMinSec;
}

template <class T, typename U>
void DmstoDeg(const T& dDegMinSec, U* dDegrees)
{
	T dh, dm, ds;

	dh = fabs(dDegMinSec);		 // retain original value
	*dDegrees = floor(dh);
	dm = floor(((dh - (*dDegrees)) * 100.0) + 0.0001);
	ds = (((dh - (*dDegrees)) * 100.0) - dm) * 100.0;
	*dDegrees += ((dm / 60.0) + (ds / 3600.0));
	if (dDegMinSec < 0.0)
		(*dDegrees) *= -1;
}

// DmstoDeg helper
template <class T>
T DmstoDeg(const T& dDegMinSec)
{
	T dDegrees;
	DmstoDeg(dDegMinSec, &dDegrees);
	return dDegrees;
}

template <class T, typename U>
void DmstoRad(const T& dDegMinSec, U* dRadians)
{
	T dh, dm, ds;

	dh = fabs(dDegMinSec);		 // retain original value
	*dRadians = floor(dh);
	dm = floor(((dh - (*dRadians)) * 100.0) + 0.0001);
	ds = (((dh - (*dRadians)) * 100.0) - dm) * 100.0;
	*dRadians += ((dm / 60.0) + (ds / 3600.0));
	if (dDegMinSec < 0.0)
		(*dRadians) *= -1;
	Radians(dRadians);
}

// DmstoRad helper
template <class T>
T DmstoRad(const T& dDegMinSec)
{
	T dRadians;
	DmstoRad(dDegMinSec, &dRadians);
	return dRadians;
}

template <class T>
T RadtoDms(const T& dRadians)
{
	T dDms;
	DegtoDms(Degrees(dRadians), &dDms);
	return dDms;
}

template <class T>
T RadtoDmsL(const T& dRadians)
{
	T dDms;
	DegtoDms(DegreesL(dRadians), &dDms);
	return dDms;
}

template <class T, typename U>
void DmintoDeg(const T& dDegMin, U* dDegrees)
{
	T dh;

	dh = fabs(dDegMin);	// retain original value
	*dDegrees = floor(dh);
	*dDegrees += (dh - *dDegrees) * 100.0 / 60.0;
	if (dDegMin < 0.0)
		*dDegrees *= -1;
}

// DmintoDeg helper
template <class T>
T DmintoDeg(const T& dDegMin)
{
	T dDegrees;
	DmintoDeg(dDegMin, &dDegrees);
	return dDegrees;
}

template <class T, typename U>
void DegtoDmin(const T& dDegrees, U* dDegMin)
{
	T d;

	*dDegMin = fabs(dDegrees);	// retain original value
	d = floor(*dDegMin);
	*dDegMin = d + ((*dDegMin - d) * 0.6);

	if (dDegrees < 0.0)
		*dDegMin *= -1;
}

// DegDmin helper
template <class T>
T DegtoDmin(const T& dDegrees)
{
	T dDegMin;
	DegtoDin(dDegrees, &dDegMin);
	return dDegMin;
}

template <class T>
//            |
//     4th    |   1st
//            |
//	----------------------
//            |
//     3rd    |   2nd
//            |
T atan_2(const T& x, const T& y)
{
	T theta(atan(x / y));			// first quadrant (default)
	if (y < 0)						// second or third quadrant
		return theta + PI;
	else
	{
		if (x > 0)					// first quadrant
			return theta;
		else						// fourth quadrant
			return theta + TWO_PI;
	}
  return 0.;
}

#endif /* DNATEMPLATECALCFUNCS_H_ */


/**
 * Copyright (C) Omar Thor <omarthoro@gmail.com> - All Rights Reserved
 * Unauthorized copying of this file, via any medium is strictly prohibited
 * Proprietary and confidential
 *
 * Written by Omar Thor <omarthoro@gmail.com>, 2017
 */

#ifndef SP_ALGO_TESTING_ASSERT_MATRIX_HPP
#define	SP_ALGO_TESTING_ASSERT_MATRIX_HPP

#include <boost/assert.hpp>
#include <iosfwd>
#include <iomanip>
#include <boost/test/unit_test.hpp>

#include "sp/config.hpp"
#include "sp/algo/nn/matrix.hpp"

/**
 * @file Matrix assertions utilities (tensor size 1-4)
 */

SP_TESTING_NAMESPACE_BEGIN

inline void assert_tensor_equals(const algo::nn::tensor_1& expected, const algo::nn::tensor_1& real, float epsilon = 1e-4f) {
    BOOST_REQUIRE_EQUAL(expected.dimension(0), real.dimension(0));
    for(size_t i = 0, len = expected.dimension(2); i < len; ++i) {
        auto expect_val = expected(i);
        auto real_val = real(i);
        BOOST_REQUIRE_CLOSE(expect_val, real_val, epsilon);
    }
}

inline void assert_tensor_equals(const algo::nn::tensor_2& expected, const algo::nn::tensor_2& real, float epsilon = 1e-4f) {
    BOOST_REQUIRE_EQUAL(expected.dimension(0), real.dimension(0));
    BOOST_REQUIRE_EQUAL(expected.dimension(1), real.dimension(1));
    for(size_t i = 0, i_len = expected.dimension(0); i < i_len; ++i) {
        for (size_t j = 0, j_len = expected.dimension(1); j < j_len; ++j) {
            auto expect_val = expected(i, j);
            auto real_val = real(i, j);
            BOOST_REQUIRE_CLOSE(expect_val, real_val, epsilon);
        }
    }
}

inline void assert_tensor_equals(const algo::nn::tensor_3& expected, const algo::nn::tensor_3& real, float epsilon = 1e-4f) {
    BOOST_REQUIRE_EQUAL(expected.dimension(0), real.dimension(0));
    BOOST_REQUIRE_EQUAL(expected.dimension(1), real.dimension(1));
    BOOST_REQUIRE_EQUAL(expected.dimension(2), real.dimension(2));
    for(size_t i = 0, i_len = expected.dimension(0); i < i_len; ++i) {
        for (size_t j = 0, j_len = expected.dimension(1); j < j_len; ++j) {
            for(size_t k = 0, k_len = expected.dimension(2); k < k_len; ++k) {
                auto expect_val = expected(i, j, k);
                auto real_val = real(i, j, k);
                BOOST_REQUIRE_CLOSE(expect_val, real_val, epsilon);
            }
        }
    }
}

inline void assert_tensor_equals(const algo::nn::tensor_4& expected, const algo::nn::tensor_4& real, float epsilon = 1e-4f) {
    BOOST_REQUIRE_EQUAL(expected.dimension(0), real.dimension(0));
    BOOST_REQUIRE_EQUAL(expected.dimension(1), real.dimension(1));
    BOOST_REQUIRE_EQUAL(expected.dimension(2), real.dimension(2));
    BOOST_REQUIRE_EQUAL(expected.dimension(3), real.dimension(3));
    for(size_t i = 0, i_len = expected.dimension(0); i < i_len; ++i) {
        for (size_t j = 0, j_len = expected.dimension(1); j < j_len; ++j) {
            for(size_t k = 0, k_len = expected.dimension(2); k < k_len; ++k) {
                for(size_t s = 0, s_len = expected.dimension(3); s < s_len; ++s) {
                    auto expect_val = expected(i, j, k, s);
                    auto real_val = real(i, j, k, s);
                    BOOST_REQUIRE_CLOSE(expect_val, real_val, epsilon);
                }
            }
        }
    }
}

inline void assert_tensor_equals(const algo::nn::tensor_5& expected, const algo::nn::tensor_5& real, float epsilon = 1e-4f) {
    BOOST_REQUIRE_EQUAL(expected.dimension(0), real.dimension(0));
    BOOST_REQUIRE_EQUAL(expected.dimension(1), real.dimension(1));
    BOOST_REQUIRE_EQUAL(expected.dimension(2), real.dimension(2));
    BOOST_REQUIRE_EQUAL(expected.dimension(3), real.dimension(3));
    BOOST_REQUIRE_EQUAL(expected.dimension(4), real.dimension(4));
    for(size_t i = 0, i_len = expected.dimension(0); i < i_len; ++i) {
        for (size_t j = 0, j_len = expected.dimension(1); j < j_len; ++j) {
            for(size_t k = 0, k_len = expected.dimension(2); k < k_len; ++k) {
                for(size_t s = 0, s_len = expected.dimension(3); s < s_len; ++s) {
                    for(size_t r = 0, r_len = expected.dimension(4); r < r_len; ++r) {
                        auto expect_val = expected(i, j, k, s, r);
                        auto real_val = real(i, j, k, s, r);
                        BOOST_REQUIRE_CLOSE(expect_val, real_val, epsilon);
                    }
                }
            }
        }
    }
}

inline void pretty_print_tensor(std::ostream& os, const algo::nn::tensor_4& tensor) {
    auto flags = os.flags();;
    os << std::setprecision(4) << std::fixed;
    os << "[\n";
    for(size_t i = 0, i_len = tensor.dimension(0); i < i_len; ++i) {
        os << "{\n";
        for (size_t j = 0, j_len = tensor.dimension(1); j < j_len; ++j) {
            for(size_t k = 0, k_len = tensor.dimension(2); k < k_len; ++k) {
                for(size_t s = 0, s_len = tensor.dimension(3); s < s_len; ++s) {
                    os << std::setw(8) << tensor(i, j, k, s);
                }
                os << "\n";
            }
            os << "\n";
        }
        os << "}\n";
    }
    os << "]\n";
    os.setf(flags);
}

SP_TESTING_NAMESPACE_END

#endif	/* SP_ALGO_TESTING_ASSERT_MATRIX_HPP */


// Author(s): Wieger Wesselink
// Copyright: see the accompanying file COPYING or copy at
// https://svn.win.tue.nl/trac/MCRL2/browser/trunk/COPYING
//
// 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)
//
/// \file typecheck_test.cpp
/// \brief Add your file description here.

#include "mcrl2/process/parse.h"
#include "mcrl2/process/typecheck.h"
#include <boost/test/included/unit_test_framework.hpp>
#include <iostream>

using namespace mcrl2;

void test_typechecker_case(std::string const& spec, bool const expected_result)
{
  std::clog << std::endl
            << "<---- testing specification: ---->" << std::endl
            << spec << std::endl;
  if (expected_result)
  {
    std::clog << "expected result: success" << std::endl;
    try
    {
      process::parse_process_specification(spec);
    }
    catch (mcrl2::runtime_error& e) // Catch errors and print them, such that all cases are treated.
    {
      std::clog << "type checking failed with error: " << std::endl
                << e.what() << std::endl;
      BOOST_CHECK(false);
    }
  }
  else
  {
    std::clog << "expected result: failure" << std::endl;
    BOOST_CHECK_THROW(process::parse_process_specification(spec), mcrl2::runtime_error);
  }
}

void test_process_specification(const std::string& ps_in, bool const expected_result = true, bool const test_type_checker = true)
{
  process::process_specification p = process::detail::parse_process_specification_new(ps_in);
  std::string ps_out;
  if (test_type_checker)
  {
    process::process_specification ps = p;
    if (expected_result)
    {
      process::typecheck_process_specification(ps);
      ps_out = process::pp(ps);
      if (ps_in != ps_out)
      {
        std::cerr << "--- failed test ---" << std::endl << "ps_in =\n" << ps_in << std::endl << std::endl << "ps_out =\n" <<  ps_out << std::endl;
        BOOST_CHECK_EQUAL(ps_in, ps_out);
      }
    }
    else
    {
      BOOST_CHECK_THROW(process::typecheck_process_specification(ps), mcrl2::runtime_error);
    }
  }
}

BOOST_AUTO_TEST_CASE(test_concat_element)
{
  // Example provided by Tim Willemse, 7 Oct 2009
  test_typechecker_case(
    "map place : List(Nat) -> List(Nat); \n"
    "                                    \n"
    "var l : List(Nat);                  \n"
    "                                    \n"
    "eqn place (l) = head(l) ++ tail(l); \n"
    "                                    \n"
    "init delta;                         \n",
    false
  );
}

BOOST_AUTO_TEST_CASE(test_concat_lists)
{
  test_typechecker_case(
    "map place : List(Nat) -> List(Nat); \n"
    "                                    \n"
    "var l : List(Nat);                  \n"
    "                                    \n"
    "eqn place (l) = l ++ tail(l);       \n"
    "                                    \n"
    "init delta;                         \n",
    true
  );
}

BOOST_AUTO_TEST_CASE(test_bug_528a)
{
  test_typechecker_case(
    "sort S = struct c;                  \n"
    "map succ_: S -> S;                   \n"
    "eqn succ_(c) = c;                    \n"
    "init delta;                         \n",
    true
  );
}

BOOST_AUTO_TEST_CASE(test_bug_528b)
{
  test_typechecker_case(
    "sort S,T;                           \n"
    "map  count_: S # T -> Nat;           \n"
    "var  x:S;                           \n"
    "     y:T;                           \n"
    "eqn  count_(x, y) = 0;               \n"
    "init delta;                         \n",
    true
  );
}

BOOST_AUTO_TEST_CASE(test_bug_528c)
{
  test_typechecker_case(
    "sort S;                             \n"
    "map  count_: S -> Nat;               \n"
    "var  x:S;                           \n"
    "eqn  count_(x) = 0;                  \n"
    "init delta;                         \n",
    true
  );
}

BOOST_AUTO_TEST_CASE(test_bug_528d)
{
  test_typechecker_case(
    "map  count: Pos#Bag(Pos)->Nat;      \n"
    "     f:Nat->Pos;                    \n"
    "                                    \n"
    "act  a:Nat;                         \n"
    "                                    \n"
    "proc P1(i: Nat) = a(i);             \n"
    "                                    \n"
    "init P1(count(3,{3:4}));            \n",
    false
  );
}

BOOST_AUTO_TEST_CASE(test_bug_663a)
{
  test_typechecker_case(
    "map const: Pos;                  \n"
    "eqn const = 10;                  \n"
    "                                 \n"
    "proc P1(i: Nat) = delta;         \n"
    "                                 \n"
    "init P1(const);                  \n",
    true
  );
}

BOOST_AUTO_TEST_CASE(test_bug_663b)
{
  test_typechecker_case(
    "map const: Pos;                  \n"
    "eqn const = 10;                  \n"
    "                                 \n"
    "proc P1(i: Nat) = delta;         \n"
    "                                 \n"
    "init P1(Nat2Pos(Pos2Nat(const)));\n",
    true
  );
}

BOOST_AUTO_TEST_CASE(test_bug_663c)
{
  test_typechecker_case(
    "map const: Pos;                  \n"
    "eqn const = 10;                  \n"
    "                                 \n"
    "proc P1(i: Nat) = delta;         \n"
    "                                 \n"
    "init P1(Pos2Nat(const));\n",
    true
  );
}

BOOST_AUTO_TEST_CASE(test_bug_644)
{
  test_typechecker_case(
    "cons maybe: Bool;                \n"
    "init delta;                      \n",
    false
  );
}

BOOST_AUTO_TEST_CASE(test_bug_626a)
{
  test_typechecker_case(
    "sort S = struct c( proj: Int );     \n"
    "                                 \n"
    "map f :(S -> Bool)#S -> S;       \n"
    "var pre: S -> Bool;             \n"
    "    s: S;                        \n"
    "eqn f (pre, s) = s;             \n"
    "                                 \n"
    "act a: (S);                      \n"
    "                                 \n"
    "init a( f( lambda x:S. proj(x) < 0 , c( 0 ) ) );\n",
    true
  );
}

BOOST_AUTO_TEST_CASE(test_bug_626b)
{
  test_typechecker_case(
    "sort S = struct c( x: Int );     \n"
    "                                 \n"
    "map f :(S -> Bool)#S -> S;       \n"
    "var pre: S -> Bool;             \n"
    "    s: S;                        \n"
    "eqn f (pre, s) = s;             \n"
    "                                 \n"
    "act a: (S);                      \n"
    "                                 \n"
    "init a( f( lambda i:S. x(i) < 0 , c( 0 ) ) );\n",
    true
  );
}

// First test case for issue #629, constructor domain is empty.
BOOST_AUTO_TEST_CASE(test_bug_629a)
{
  test_typechecker_case(
    "sort L_1=struct insert(L_1) ;    \n"
    "init delta;                      \n",
    false
  );
}

// Second test case for issue #629, constructor domain is empty.
BOOST_AUTO_TEST_CASE(test_bug_629b)
{
  test_typechecker_case(
    "sort L_2=struct insert(L_3);     \n"
    "     L_3=struct insert(L_2);     \n"
    "init delta;                      \n",
    false
  );
}

// Third test case for issue #629, constructor domain is empty.
BOOST_AUTO_TEST_CASE(test_bug_629c)
{
  test_typechecker_case(
    "sort D;                          \n"
    "cons f:D->D;                     \n"
    "init delta;                      \n",
    false
  );
}

// fourth test case for issue #629, constructor domain is empty.
BOOST_AUTO_TEST_CASE(test_bug_629d)
{
  test_typechecker_case(
    "sort D =struct f(struct g(D));"
    "init delta;",
    false
  );
}

// fifth test case for issue #629, constructor domain is NOT empty.
BOOST_AUTO_TEST_CASE(test_bug_629e)
{
  test_typechecker_case(
    "sort D =struct f(struct g(Nat));"
    "init delta;",
    true
  );
}

// Tricky test case that should succeed, as sorts are not recursively defined through sort containers.
BOOST_AUTO_TEST_CASE(test_recursive_a)
{
  test_typechecker_case(
    "sort MyRecType=struct f | g(MyRecType);"
    "     D=List(MyRecType);"
    "init delta;",
    true
  );
}

// Tricky test case that should succeed, as recursive sorts are are defined through the list containers
// are allowed.
BOOST_AUTO_TEST_CASE(test_recursive_b)
{
  test_typechecker_case(
    "sort MyRecType=struct f | g(List(MyRecType));"
    "init delta;",
    true
  );
}

// Tricky test case that should succeed, as sorts are not recursively defined through sort containers.
BOOST_AUTO_TEST_CASE(test_recursive_c)
{
  test_typechecker_case(
    "sort MyRecType=struct f | g(MyRecType);"
    "     D=List(MyRecType)->MyRecType;"
    "init delta;",
    true
  );
}

// Tricky test case that should fail, as recursive sorts are are defined through sort containers
// but in this case it is not that easy to see.
BOOST_AUTO_TEST_CASE(test_recursive_d)
{
  test_typechecker_case(
    "sort MyRecType=struct f | g(MyRecType->Nat);"
    "init delta;",
    false
  );
}

// Test case below went wrong, because sort expression was confused with a function symbol
BOOST_AUTO_TEST_CASE(test_sort_expression_vs_function_symbol)
{
  test_typechecker_case(
    "map  const: Pos;                 \n"
    "     f:Nat->Pos;                 \n"
    "eqn  const  =  10;               \n"
    "proc P1(i: Nat) = delta;         \n"
    "init P1(f(const));               \n",
    true
  );
}

BOOST_AUTO_TEST_CASE(test_real_zero)
{
  test_typechecker_case(
    "sort T = Real;\n"
    "map  x: List(T) -> List(T);\n"
    "var  l: List(T);\n"
    "     r: T;\n"
    "eqn  x(r |> l) = (r+0) |> l;\n"
    "act  a: List(T);\n"
    "init a(x([0]));\n",
    true
  );
}

// The following example tests whether a double assignment in a
// process is properly caught by the typechecker.
BOOST_AUTO_TEST_CASE(test_double_variable_assignment_in_process)
{
  test_typechecker_case(
    "proc X( v :Bool  ) = tau.  X( v = true, v = false );\n"
    "init X(true);",
    false);
}

BOOST_AUTO_TEST_CASE(test_typecheck)
{
  std::string text =
    "act  a;\n"
    "glob d,c,b: Bool;\n"
         "n,m: Nat;\n"
         "p: Pos;\n"
    "proc P(b,c: Bool) = a . P(c = true);\n"
    "init delta;\n"
    ;
  process::process_specification procspec = process::detail::parse_process_specification_new(text);
  process::typecheck_process_specification(procspec);
}

BOOST_AUTO_TEST_CASE(test_process_reference_assignment)
{
  //test process specification involving process reference assignments
  test_process_specification(
    "proc P(b: Bool) = tau . P() + tau . P(b = false);\n"
    "\n"
    "init P(b = true);\n"
  );
}

BOOST_AUTO_TEST_CASE(test_global_variables)
{
  //test process specification involving global variables
  test_process_specification(
    "glob dc: Bool;\n"
    "\n"
    "proc P(b: Bool) = tau . P(dc);\n"
    "\n"
    "init P(dc);\n"
  );
}

// For bug #732
BOOST_AUTO_TEST_CASE(test_function_condition)
{
  test_process_specification(
    "map  b: Nat -> Nat;\n\n"
    "init b -> tau;\n",
    false
  );
}

// For bug #732
BOOST_AUTO_TEST_CASE(test_function_as_set_descriptor)
{
  test_process_specification(
    "map  b: Bool # Pos -> Nat;\n"
    "     s: Set(Nat);\n\n"
    "eqn  s  =  { n: Nat | b };\n\n"
    "init b -> tau;\n",
    false
  );
}

// For bug #732
BOOST_AUTO_TEST_CASE(test_function_as_equation_condition)
{
  test_process_specification(
    "map  b: Bool # Pos -> Nat;\n"
    "     n: Nat;\n\n"
    "eqn  b  ->  n  =  0;\n\n"
    "act  a: Nat;\n\n"
    "init a(n);\n",
    false
  );
}

boost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[])
{
  return nullptr;
}


#include "utils.hh"

#include <boost/program_options.hpp>

#include <vtkCell.h>
#include <vtkCellIterator.h>
#include <vtkDoubleArray.h>
#include <vtkIdList.h>
#include <vtkPointData.h>
#include <vtkPoints.h>
#include <vtkSmartPointer.h>
#include <vtkSubdivideTetra.h>
#include <vtkUnstructuredGrid.h>
#include <vtkUnstructuredGridWriter.h>

#include <iostream>
#include <random>
#include <string>
#include <stdexcept>

using namespace cpp_utils;

namespace po = boost::program_options;

using Mat3d = Eigen::Matrix<double, 3, 3, Eigen::RowMajor>;

Mat3d inputMatrix(const std::string& name)
{
    std::cout << "Enter values for 3x3 matrix " << name << std::endl;
    auto result = Mat3d{};
    std::cin >> result(0, 0) >> result(0, 1) >> result(0, 2) >> result(1, 0)
             >> result(1, 1) >> result(1, 2) >> result(2, 0) >> result(2, 1)
             >> result(2, 2);
    return result;
}

template <typename R, typename G>
Mat3d randMatrix(R& rnd, G& gen, bool symmetric = false)
{
    auto result = Mat3d{};
    result << rnd(gen), rnd(gen), rnd(gen),
              rnd(gen), rnd(gen), rnd(gen),
              rnd(gen), rnd(gen), rnd(gen);
    if(symmetric)
    {
        result = 0.5 * (result + result.transpose().eval());
    }
    return result;
}

int main(int argc, char const* argv[])
{
    using cpp_utils::range;
    using cpp_utils::make_string;

    auto random_seed = uint32_t{42};
    auto num_subdivisions = int32_t{8};
    auto out_name = std::string{"Grid.vtk"};
    auto symmetric = false;
    auto interactive = false;
    auto gen_derivatives = false;

    try
    {
        po::options_description desc("Allowed options");
        desc.add_options()
            ("help,h", "produce help message")
            ("random,r",
             po::value<uint32_t>(&random_seed),
             "Random seed for tensor generation")
            ("symmetric,s",
             po::bool_switch(&symmetric),
             "Generate symmetric tensor. Only works in conjunction with "
             "--random")
            ("interactive,i",
             po::bool_switch(&interactive),
             "Interactive mode (read matrices from stdin)")
            ("derivatives,d",
             po::bool_switch(&gen_derivatives),
             "Also generate derivatives Sx, Sy, and Sz")
            ("subdivision-level,l",
             po::value<int32_t>(&num_subdivisions)
                     ->required()
                     ->default_value(num_subdivisions),
             "Number of times the tetrahedron is subdivided")
            ("output,o",
             po::value<std::string>(&out_name)
                     ->required()->default_value(out_name),
             "Name of the output file");

        auto vm = po::variables_map{};
        po::store(po::parse_command_line(argc, argv, desc), vm);

        if(vm.empty() || vm.count("help"))
        {
            std::cout << "Generate a linear tensor field on a tetrahedral grid "
                         "with variable subdivision level.\n\n";
            std::cout << desc << "\n";
            return 0;
        }

        po::notify(vm);

        if(vm.count("random") > 0 && interactive)
        {
            throw po::error(
                    "Conflicting options --random and --interactive");
        }
        if(symmetric && interactive)
        {
            throw po::error(
                    "Conflicting options --symmetric and --interactive");
        }
    }
    catch(std::exception& e)
    {
        std::cerr << "Error: " << e.what() << "\n";
        return 1;
    }
    catch(...)
    {
        std::cerr << "Exception of unknown type!\n";
    }

    auto gen = std::mt19937{random_seed};
    auto rnd = std::uniform_real_distribution<>{-1.0, 1.0};

    // Build vtkUnstructuredGrid with tetrahedral cells
    auto grid = vtkSmartPointer<vtkUnstructuredGrid>::New();
    auto points = vtkSmartPointer<vtkPoints>::New();
    points->InsertNextPoint( 1.01979867,  1.00974785,  0.96975252);
    points->InsertNextPoint(-1.01979867,  0.99015215, -0.98975218);
    points->InsertNextPoint( 0.97980134, -1.01014782, -1.00974785);
    points->InsertNextPoint(-0.97980134, -0.98975218,  1.02974752);
    grid->SetPoints(points);

    auto cell = vtkSmartPointer<vtkIdList>::New();
    cell->InsertNextId(0);
    cell->InsertNextId(1);
    cell->InsertNextId(2);
    cell->InsertNextId(3);
    grid->InsertNextCell(VTK_TETRA, cell);

    if(!gen_derivatives)
    {
        auto s_field = vtkSmartPointer<vtkDoubleArray>::New();
        s_field->SetName("S");
        s_field->SetNumberOfComponents(9);
        s_field->SetNumberOfTuples(points->GetNumberOfPoints());
        auto t_field = vtkSmartPointer<vtkDoubleArray>::New();
        t_field->SetName("T");
        t_field->SetNumberOfComponents(9);
        t_field->SetNumberOfTuples(points->GetNumberOfPoints());

        for(auto i : range(points->GetNumberOfPoints()))
        {
            if(interactive)
            {
                s_field->SetTuple(
                        i, inputMatrix(make_string() << "S" << (i + 1)).data());
                t_field->SetTuple(
                        i, inputMatrix(make_string() << "T" << (i + 1)).data());
            }
            else
            {
                s_field->SetTuple(i, randMatrix(rnd, gen, symmetric).data());
                t_field->SetTuple(i, randMatrix(rnd, gen, symmetric).data());
            }
        }
        grid->GetPointData()->SetTensors(s_field);
        grid->GetPointData()->AddArray(t_field);
    }
    else
    {
        auto sx_field = vtkSmartPointer<vtkDoubleArray>::New();
        sx_field->SetName("Sx");
        sx_field->SetNumberOfComponents(9);
        sx_field->SetNumberOfTuples(points->GetNumberOfPoints());
        auto sy_field = vtkSmartPointer<vtkDoubleArray>::New();
        sy_field->SetName("Sy");
        sy_field->SetNumberOfComponents(9);
        sy_field->SetNumberOfTuples(points->GetNumberOfPoints());
        auto sz_field = vtkSmartPointer<vtkDoubleArray>::New();
        sz_field->SetName("Sz");
        sz_field->SetNumberOfComponents(9);
        sz_field->SetNumberOfTuples(points->GetNumberOfPoints());
        auto s_field = vtkSmartPointer<vtkDoubleArray>::New();
        s_field->SetName("S");
        s_field->SetNumberOfComponents(9);
        s_field->SetNumberOfTuples(points->GetNumberOfPoints());

        for(auto i : range(points->GetNumberOfPoints()))
        {
            if(interactive)
            {
                s_field->SetTuple(
                        i, inputMatrix(make_string() << "S" << (i + 1)).data());
                sx_field->SetTuple(
                        i, inputMatrix(make_string() << "Sx" << (i + 1)).data());
                sy_field->SetTuple(
                        i, inputMatrix(make_string() << "Sy" << (i + 1)).data());
                sz_field->SetTuple(
                        i, inputMatrix(make_string() << "Sz" << (i + 1)).data());
            }
            else
            {
                s_field->SetTuple(i, randMatrix(rnd, gen, symmetric).data());
                sx_field->SetTuple(i, randMatrix(rnd, gen, false).data());
                sy_field->SetTuple(i, randMatrix(rnd, gen, false).data());
                sz_field->SetTuple(i, randMatrix(rnd, gen, false).data());
            }
        }
        grid->GetPointData()->AddArray(sx_field);
        grid->GetPointData()->AddArray(sy_field);
        grid->GetPointData()->AddArray(sz_field);
        grid->GetPointData()->AddArray(s_field);
    }

    auto sub_filter = vtkSmartPointer<vtkSubdivideTetra>::New();

    for(auto _ : range(num_subdivisions))
    {
        std::ignore = _; // suppress warning about unused loop variable

        sub_filter->SetInputData(grid);
        auto new_grid = vtkSmartPointer<vtkUnstructuredGrid>(
                vtkUnstructuredGrid::SafeDownCast(
                        sub_filter->GetOutputDataObject(0)));
        sub_filter->Update();
        grid->DeepCopy(new_grid);
    }

    auto inwriter = vtkSmartPointer<vtkUnstructuredGridWriter>::New();
    inwriter->SetInputData(grid);
    inwriter->SetFileName(out_name.c_str());
    inwriter->SetFileTypeToBinary();
    inwriter->Write();

    return 0;
}


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

#include <armnn/Descriptors.hpp>
#include <armnn/IRuntime.hpp>
#include <armnn/INetwork.hpp>
#include <Half.hpp>

#include <Graph.hpp>
#include <Optimizer.hpp>
#include <backendsCommon/CpuTensorHandle.hpp>

#include <boost/core/ignore_unused.hpp>
#include <boost/test/unit_test.hpp>

#include <set>

using namespace armnn;

BOOST_AUTO_TEST_SUITE(Fp16Support)

BOOST_AUTO_TEST_CASE(Fp16DataTypeSupport)
{
    Graph graph;

    Layer* const inputLayer1 = graph.AddLayer<InputLayer>(1, "input1");
    Layer* const inputLayer2 = graph.AddLayer<InputLayer>(2, "input2");

    Layer* const additionLayer = graph.AddLayer<AdditionLayer>("addition");
    Layer* const outputLayer = graph.AddLayer<armnn::OutputLayer>(0, "output");

    TensorInfo fp16TensorInfo({1, 2, 3, 5}, armnn::DataType::Float16);
    inputLayer1->GetOutputSlot(0).Connect(additionLayer->GetInputSlot(0));
    inputLayer2->GetOutputSlot(0).Connect(additionLayer->GetInputSlot(1));
    additionLayer->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0));

    inputLayer1->GetOutputSlot().SetTensorInfo(fp16TensorInfo);
    inputLayer2->GetOutputSlot().SetTensorInfo(fp16TensorInfo);
    additionLayer->GetOutputSlot().SetTensorInfo(fp16TensorInfo);

    BOOST_CHECK(inputLayer1->GetOutputSlot(0).GetTensorInfo().GetDataType() == armnn::DataType::Float16);
    BOOST_CHECK(inputLayer2->GetOutputSlot(0).GetTensorInfo().GetDataType() == armnn::DataType::Float16);
    BOOST_CHECK(additionLayer->GetOutputSlot(0).GetTensorInfo().GetDataType() == armnn::DataType::Float16);
}

BOOST_AUTO_TEST_CASE(Fp16AdditionTest)
{
   using namespace half_float::literal;
   // Create runtime in which test will run
   IRuntime::CreationOptions options;
   IRuntimePtr runtime(IRuntime::Create(options));

   // Builds up the structure of the network.
   INetworkPtr net(INetwork::Create());

   IConnectableLayer* inputLayer1 = net->AddInputLayer(0);
   IConnectableLayer* inputLayer2 = net->AddInputLayer(1);
   IConnectableLayer* additionLayer = net->AddAdditionLayer();
   IConnectableLayer* outputLayer = net->AddOutputLayer(0);

   inputLayer1->GetOutputSlot(0).Connect(additionLayer->GetInputSlot(0));
   inputLayer2->GetOutputSlot(0).Connect(additionLayer->GetInputSlot(1));
   additionLayer->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0));

   //change to float16
   TensorInfo fp16TensorInfo(TensorShape({4}), DataType::Float16);
   inputLayer1->GetOutputSlot(0).SetTensorInfo(fp16TensorInfo);
   inputLayer2->GetOutputSlot(0).SetTensorInfo(fp16TensorInfo);
   additionLayer->GetOutputSlot(0).SetTensorInfo(fp16TensorInfo);

   // optimize the network
   std::vector<BackendId> backends = {Compute::GpuAcc};
   IOptimizedNetworkPtr optNet = Optimize(*net, backends, runtime->GetDeviceSpec());

   // Loads it into the runtime.
   NetworkId netId;
   runtime->LoadNetwork(netId, std::move(optNet));

   std::vector<Half> input1Data
   {
       1.0_h, 2.0_h, 3.0_h, 4.0_h
   };

   std::vector<Half> input2Data
   {
       100.0_h, 200.0_h, 300.0_h, 400.0_h
   };

   InputTensors inputTensors
   {
       {0,ConstTensor(runtime->GetInputTensorInfo(netId, 0), input1Data.data())},
       {1,ConstTensor(runtime->GetInputTensorInfo(netId, 0), input2Data.data())}
   };

   std::vector<Half> outputData(input1Data.size());
   OutputTensors outputTensors
   {
       {0,Tensor(runtime->GetOutputTensorInfo(netId, 0), outputData.data())}
   };

   // Does the inference.
   runtime->EnqueueWorkload(netId, inputTensors, outputTensors);

   // Checks the results.
   BOOST_TEST(outputData == std::vector<Half>({ 101.0_h, 202.0_h, 303.0_h, 404.0_h})); // Add
}

BOOST_AUTO_TEST_SUITE_END()


/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */

/*
 Copyright (C) 2012, 2013 Klaus Spanderen
 Copyright (C) 2014 Johannes Göttker-Schnetmann

 This file is part of QuantLib, a free-software/open-source library
 for financial quantitative analysts and developers - http://quantlib.org/

 QuantLib is free software: you can redistribute it and/or modify it
 under the terms of the QuantLib license.  You should have received a
 copy of the license along with this program; if not, please email
 <quantlib-dev@lists.sf.net>. The license is also available online at
 <http://quantlib.org/license.shtml>.

 This program is distributed in the hope that it will be useful, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 FOR A PARTICULAR PURPOSE.  See the license for more details.
*/

/*! \file fdmsquarerootfwdop.cpp
    \brief Fokker-Planck forward operator for an square root process
*/

#include <ql/math/functional.hpp>
#include <ql/methods/finitedifferences/meshers/fdmmesher.hpp>
#include <ql/methods/finitedifferences/operators/fdmlinearoplayout.hpp>
#include <ql/methods/finitedifferences/operators/firstderivativeop.hpp>
#include <ql/methods/finitedifferences/operators/secondderivativeop.hpp>

#include <ql/experimental/finitedifferences/fdmsquarerootfwdop.hpp>
#include <ql/experimental/finitedifferences/modtriplebandlinearop.hpp>

#include <boost/math/special_functions/gamma.hpp>
#include <boost/math/distributions/non_central_chi_squared.hpp>

namespace QuantLib {

    FdmSquareRootFwdOp::FdmSquareRootFwdOp(
        const std::shared_ptr<FdmMesher>& mesher,
        Real kappa, Real theta, Real sigma,
        Size direction, TransformationType transform)
    : direction_(direction),
      kappa_(kappa),
      theta_(theta),
      sigma_(sigma),
      transform_(transform),
      mapX_(transform == Plain ?
          new ModTripleBandLinearOp(FirstDerivativeOp(direction_, mesher)
              .mult(kappa*(mesher->locations(direction_)-theta) + sigma*sigma)
              .add(SecondDerivativeOp(direction_, mesher)
                   .mult(0.5*sigma*sigma*mesher->locations(direction_)))
                .add(Array(mesher->layout()->size(), kappa)))

        : transform == Power ? new ModTripleBandLinearOp(
            SecondDerivativeOp(direction_, mesher)
               .mult(0.5*sigma*sigma*mesher->locations(direction_))
               .add(FirstDerivativeOp(direction_, mesher)
                     .mult(kappa*(mesher->locations(direction_)+theta)))
               .add(Array(mesher->layout()->size(),
                          2*kappa*kappa*theta/(sigma*sigma))))

            : new ModTripleBandLinearOp(FirstDerivativeOp(direction_, mesher)
                    .mult(Exp(-mesher->locations(direction))
                        *( -0.5*sigma*sigma - kappa*theta) + kappa)
                    .add(SecondDerivativeOp(direction_, mesher)
                    .mult(0.5*sigma*sigma*Exp(-mesher->locations(direction))))
                    .add(kappa*theta*Exp(-mesher->locations(direction))))
            ),
      v_  (mesher->layout()->dim()[direction_]) {

        const FdmLinearOpIterator endIter = mesher->layout()->end();
        for (FdmLinearOpIterator iter = mesher->layout()->begin();
            iter != endIter; ++iter) {
            const Real v = mesher->location(iter, direction_);
            v_[iter.coordinates()[direction_]] = v;
        }

        // zero flux boundary condition
        setLowerBC(mesher);
        setUpperBC(mesher);
    }

    void FdmSquareRootFwdOp::setLowerBC(
        const std::shared_ptr<FdmMesher>& mesher) {
        const Size n = 1;
        Real alpha, beta, gamma;

        getCoeff(alpha, beta, gamma, n);
        const Real f = lowerBoundaryFactor(transform_);

        const Real b = -(h(n-1)+h(n))/zeta(n);
        const Real c =  h(n-1)/zetap(n);

        const FdmLinearOpIterator endIter = mesher->layout()->end();
        for (FdmLinearOpIterator iter = mesher->layout()->begin();
            iter != endIter; ++iter) {
            if (iter.coordinates()[direction_] == 0) {
                const Size idx = iter.index();
                mapX_->diag()[idx]  = beta  + f*b; //*v(n-1);
                mapX_->upper()[idx] = gamma + f*c; //*v(n-1);
            }
        }
    }

    void FdmSquareRootFwdOp::setUpperBC(
        const std::shared_ptr<FdmMesher>& mesher) {
        const Size n = v_.size();
        Real alpha, beta, gamma;

        getCoeff(alpha, beta, gamma, n);
        const Real f = upperBoundaryFactor(transform_);

        const Real b = (h(n)+h(n-1))/zeta(n);
        const Real c = -h(n)/zetam(n);

        const FdmLinearOpIterator endIter = mesher->layout()->end();
        for (FdmLinearOpIterator iter = mesher->layout()->begin();
            iter != endIter; ++iter) {
            if (iter.coordinates()[direction_] == n-1) {
                const Size idx = iter.index();
                mapX_->diag()[idx] = beta   + f*b; //*v(n+1);
                mapX_->lower()[idx] = alpha + f*c; //*v(n+1);
            }
        }
    }

    Real FdmSquareRootFwdOp::lowerBoundaryFactor(TransformationType transform) const {
        if (transform == Plain) {
            return f0Plain();
        }
        else if (transform == Power) {
            return f0Power();
        }
        else if (transform == Log) {
            return f0Log();
        }
        else
            QL_FAIL("unknown transform");
    }

    Real FdmSquareRootFwdOp::upperBoundaryFactor(TransformationType transform) const {
        if (transform == Plain) {
            return f1Plain();
        }
        else if (transform == Power) {
            return f1Power();
        }
        else if (transform == Log) {
            return f1Log();
        }
        else
            QL_FAIL("unknown transform");
    }

    Real FdmSquareRootFwdOp::f0Plain() const {
        const Size n = 1;
        const Real a = -(2*h(n-1)+h(n))/zetam(n);
        const Real alpha = sigma_*sigma_*v(n)/zetam(n) - mu(n)*h(n)/zetam(n);
        const Real nu = a*v(n-1) + (2*kappa_*(v(n-1)-theta_) + sigma_*sigma_)
                                        /(sigma_*sigma_);

        return alpha/nu*v(n-1);
    }

    Real FdmSquareRootFwdOp::f1Plain() const {
        const Size n = v_.size();
        const Real a =  (2*h(n)+h(n-1))/zetap(n);
        const Real gamma = sigma_*sigma_*v(n)/zetap(n) + mu(n)*h(n-1)/zetap(n);
        const Real nu = a*v(n+1) + (2*kappa_*(v(n+1)-theta_) + sigma_*sigma_)
                        /(sigma_*sigma_);

        return gamma/nu*v(n+1);
    }

    Real FdmSquareRootFwdOp::f0Power() const {
        const Size n = 1;
        const Real mu = kappa_*(v(n)+theta_);
        const Real a = -(2*h(n-1)+h(n))/zetam(n);
        const Real alpha = sigma_*sigma_*v(n)/zetam(n) - mu*h(n)/zetam(n);
        const Real nu  = a*v(n-1) +2*(kappa_*v(n-1)/(sigma_*sigma_));

        return alpha/nu*v(n-1);
    }

    Real FdmSquareRootFwdOp::f1Power() const {
        const Size n = v_.size();
        const Real mu = kappa_*(v(n)+theta_);
        const Real a =  (2*h(n)+h(n-1))/zetap(n);
        const Real gamma = sigma_*sigma_*v(n)/zetap(n) + mu*h(n-1)/zetap(n);
        const Real nu = a*v(n+1) +2*(kappa_*v(n+1)/(sigma_*sigma_));

        return gamma/nu*v(n+1);
    }

    Real FdmSquareRootFwdOp::f0Log() const {
        const Size n = 1;
        const Real mu = ((-kappa_*theta_-sigma_*sigma_/2.0)*exp(-v(1))+kappa_);
        const Real a = -(2*h(n-1)+h(n))/zetam(n);
        const Real alpha = sigma_*sigma_*exp(-v(n))/zetam(n) - mu*h(n)/zetam(n);
        const Real nu = a*exp(-v(n-1)) + 2*kappa_*(1-theta_*exp(-v(n-1)))
                        /(sigma_*sigma_);

        return alpha/nu*exp(-v(n-1));
    }

    Real FdmSquareRootFwdOp::f1Log() const {
        const Size n = v_.size();
        const Real mu = ((-kappa_*theta_-sigma_*sigma_/2.0)*exp(-v(n))+kappa_);
        const Real a =  (2*h(n)+h(n-1))/zetap(n);
        const Real gamma = sigma_*sigma_*exp(-v(n))/zetap(n) + mu*h(n-1)/zetap(n);
        const Real nu = a*exp(-v(n+1)) + 2*kappa_*(1-theta_*exp(-v(n+1)))
                        /(sigma_*sigma_);

        return gamma/nu*exp(-v(n+1));
    }

    Real FdmSquareRootFwdOp::v(Size i) const {
        if (i > 0 && i <= v_.size()) {
            return v_[i-1];
        }
        else if (i == 0) {
            if (transform_ == Log) {
                return 2*v_[0] - v_[1];
//              log(std::max(0.5*exp(v_[0]), exp(v_[0] - 0.01 * (v_[1] - v_[0]))));
            } else {
                return std::max(0.5*v_[0], v_[0] - 0.01 * (v_[1] - v_[0]));
            }
        }
        else if (i == v_.size()+1) {
            return v_.back() + (v_.back() - *(v_.end()-2));
        }
        else {
            QL_FAIL("unknown index");
        }
    }

    Real FdmSquareRootFwdOp::h(Size i) const {
        return v(i+1) - v(i);
    }
    Real FdmSquareRootFwdOp::mu(Size i) const {
        return kappa_*(v(i) - theta_) + sigma_*sigma_;
    }
    Real FdmSquareRootFwdOp::zetam(Size i) const {
        return h(i-1)*(h(i-1)+h(i));
    }
    Real FdmSquareRootFwdOp::zeta(Size i) const {
        return h(i-1)*h(i);
    }
    Real FdmSquareRootFwdOp::zetap(Size i) const {
        return h(i)*(h(i-1)+h(i));
    }

    Size FdmSquareRootFwdOp::size() const {
        return 1;
    }
    void FdmSquareRootFwdOp::setTime(Time, Time) {
    }

    void FdmSquareRootFwdOp::getCoeff(Real& alpha, Real& beta,
                                               Real& gamma, Size n) const {
        if (transform_ == Plain) {
            getCoeffPlain(alpha, beta, gamma, n);
        }
        else if (transform_ == Power) {
            getCoeffPower(alpha, beta, gamma, n);
        }
        else if (transform_ == Log) {
            getCoeffLog(alpha, beta, gamma, n);
        }
    }

    void FdmSquareRootFwdOp::getCoeffPlain(Real& alpha, Real& beta,
                                               Real& gamma, Size n) const {
        alpha =   sigma_*sigma_*v(n)/zetam(n) - mu(n)*h(n)/zetam(n);
        beta  = - sigma_*sigma_*v(n)/zeta(n)
                    + mu(n)*(h(n)-h(n-1))/zeta(n) + kappa_;
        gamma =   sigma_*sigma_*v(n)/zetap(n) + mu(n)*h(n-1)/zetap(n);

    }

    void FdmSquareRootFwdOp::getCoeffLog(Real& alpha, Real& beta,
                                               Real& gamma, Size n) const {
        const Real mu = ((-kappa_*theta_-sigma_*sigma_/2.0)*exp(-v(n))+kappa_);
        alpha =   sigma_*sigma_*exp(-v(n))/zetam(n) - mu*h(n)/zetam(n);
        beta  = - sigma_*sigma_*exp(-v(n))/zeta(n)
                          + mu*(h(n)-h(n-1))/zeta(n) + kappa_*theta_*exp(-v(n));
        gamma =   sigma_*sigma_*exp(-v(n))/zetap(n) + mu*h(n-1)/zetap(n);
    }

    void FdmSquareRootFwdOp::getCoeffPower(Real& alpha, Real& beta,
                                               Real& gamma, Size n) const {
        const Real mu = kappa_*(theta_+v(n));
        alpha = (sigma_*sigma_*v(n) - mu*h(n))/zetam(n);
        beta = (-sigma_*sigma_*v(n) + mu*(h(n)-h(n-1)))/zeta(n)
                                + 2*kappa_*kappa_*theta_/(sigma_*sigma_);
        gamma=  (sigma_*sigma_*v(n) + mu*h(n-1))/zetap(n);
    }

    Disposable<Array> FdmSquareRootFwdOp::apply(const Array& p) const {
        return mapX_->apply(p);
    }

    Disposable<Array> FdmSquareRootFwdOp::apply_mixed(const Array& r) const {
        Array retVal(r.size(), 0.0);
        return retVal;
    }
    Disposable<Array> FdmSquareRootFwdOp::apply_direction(
        Size direction, const Array& r) const {
        if (direction == direction_) {
            return mapX_->apply(r);
        }
        else {
            Array retVal(r.size(), 0.0);
            return retVal;
        }
    }
    Disposable<Array> FdmSquareRootFwdOp::solve_splitting(
        Size direction, const Array& r, Real dt) const {
        if (direction == direction_) {
            return mapX_->solve_splitting(r, dt, 1.0);
        }
        else {
            Array retVal(r);
            return retVal;
        }
    }

    Disposable<Array> FdmSquareRootFwdOp::preconditioner(
        const Array& r, Real dt) const {
        return solve_splitting(direction_, r, dt);
    }

    #if !defined(QL_NO_UBLAS_SUPPORT)
    Disposable<std::vector<SparseMatrix> >
    FdmSquareRootFwdOp::toMatrixDecomp() const {
        std::vector<SparseMatrix> retVal(1, mapX_->toMatrix());
        return retVal;
    }
    #endif
}


//==============================================================================
//         Copyright 2003 - 2011 LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2011 LRI    UMR 8623 CNRS/Univ Paris Sud XI
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
#ifndef NT2_ELLIPTIC_FUNCTIONS_SCALAR_AM_HPP_INCLUDED
#define NT2_ELLIPTIC_FUNCTIONS_SCALAR_AM_HPP_INCLUDED

#include <nt2/elliptic/functions/am.hpp>
#include <boost/dispatch/attributes.hpp>
#include <nt2/elliptic/functions/generic/details/am_kernel.hpp>
#include <nt2/include/constants/one.hpp>
#include <nt2/include/constants/pio_2.hpp>
#include <nt2/include/constants/two.hpp>
#include <nt2/include/functions/scalar/abs.hpp>
#include <nt2/include/functions/scalar/atan.hpp>
#include <nt2/include/functions/scalar/exp.hpp>
#include <nt2/include/functions/scalar/is_equal.hpp>
#include <nt2/include/functions/scalar/is_eqz.hpp>

namespace nt2 { namespace ext
{

   BOOST_DISPATCH_IMPLEMENT  (am_, tag::cpu_,
                              (A0)(A1)(A2),
                              (scalar_<floating_<A0> >)
                              (scalar_<floating_<A1> >)
                              (scalar_<floating_<A2> >)
                             )
  {
    typedef A0 result_type;
    BOOST_FORCEINLINE result_type operator()(const A0 & u, const A1 & x,
                                             const A2 & tol) const
    {
      if(is_eqz(x)) return u;
      result_type k = nt2::abs(x);
      if(eq(k, One<A0>())) return Two<A0>()*atan(exp(u))-Pio_2<A0>();
      return details::am_kernel(u, k, tol);
    }
  };

} }
#endif


/*
 * Copyright 2009-2020 The VOTCA Development Team (http://www.votca.org)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 *
 *     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.
 *
 */

#define BOOST_TEST_MAIN
#define BOOST_TEST_MODULE dipoledipoleinteraction_test

// Standard includes
#include <iostream>

// Third party includes
#include <boost/test/unit_test.hpp>

// Local VOTCA includes
#include "votca/xtp/dipoledipoleinteraction.h"
#include "votca/xtp/eigen.h"

using namespace votca::xtp;
using namespace votca;

BOOST_AUTO_TEST_SUITE(dipoledipoleinteraction_test)

BOOST_AUTO_TEST_CASE(dipoledipoleinteraction_test) {

  PolarSite zero(0, "H", Eigen::Vector3d::UnitX());
  PolarSegment seg0("zero", 0);
  seg0.push_back(zero);
  PolarSite one(1, "C", Eigen::Vector3d::UnitZ());
  PolarSegment seg1("one", 1);
  seg1.push_back(one);

  std::vector<PolarSegment> segs;
  segs.push_back(seg0);
  segs.push_back(seg1);
  double tholedamp = 0.39;
  eeInteractor interactor(tholedamp);

  DipoleDipoleInteraction dipdip(interactor, segs);

  // building reference
  Eigen::MatrixXd ref = Eigen::MatrixXd::Zero(6, 6);
  PolarSegment seg_ref("ref", 100);
  seg_ref.push_back(zero);
  seg_ref.push_back(one);
  for (Index i = 1; i < seg_ref.size(); i++) {
    for (Index j = 0; j < i; j++) {
      ref.block<3, 3>(3 * i, 3 * j) =
          interactor.FillTholeInteraction(seg_ref[i], seg_ref[j]);
      ref.block<3, 3>(3 * j, 3 * i) =
          interactor.FillTholeInteraction(seg_ref[j], seg_ref[i]);
    }
  }

  for (Index i = 0; i < seg_ref.size(); i++) {
    ref.block<3, 3>(3 * i, 3 * i) = seg_ref[i].getPInv();
  }
  // building matrix via (i,j) operator
  Eigen::MatrixXd elementwise = Eigen::MatrixXd::Zero(6, 6);
  for (Index i = 0; i < 6; i++) {
    for (Index j = 0; j < 6; j++) {
      double value = dipdip(i, j);  // do not remove gcc debug needs this
      elementwise(i, j) = value;
    }
  }
  bool op_check = elementwise.isApprox(ref, 1e-6);
  BOOST_CHECK_EQUAL(op_check, 1);
  if (!op_check) {
    std::cout << "ref" << std::endl;
    std::cout << ref << std::endl;
    std::cout << "operator" << std::endl;
    std::cout << elementwise << std::endl;
  }

  // building matrix via matrix vector product
  Eigen::MatrixXd gemv = Eigen::MatrixXd::Zero(6, 6);
  Eigen::MatrixXd ident = Eigen::MatrixXd::Identity(6, 6);
  for (Index i = 0; i < 6; i++) {
    gemv.col(i) = dipdip * ident.col(i);
  }
  bool gemv_check = gemv.isApprox(ref, 1e-6);
  BOOST_CHECK_EQUAL(gemv_check, 1);
  if (!gemv_check) {
    std::cout << "ref" << std::endl;
    std::cout << ref << std::endl;
    std::cout << "gemv" << std::endl;
    std::cout << gemv << std::endl;
  }
  // building matrix via iterator product
  Eigen::MatrixXd iterator = Eigen::MatrixXd::Zero(6, 6);
  for (Index k = 0; k < dipdip.outerSize(); ++k) {
    for (DipoleDipoleInteraction::InnerIterator it(dipdip, k); it; ++it) {
      iterator(it.row(), k) = it.value();
    }
  }
  bool iterator_check = iterator.isApprox(ref, 1e-6);
  BOOST_CHECK_EQUAL(iterator_check, 1);
  if (!iterator_check) {
    std::cout << "ref" << std::endl;
    std::cout << ref << std::endl;
    std::cout << "iterator" << std::endl;
    std::cout << gemv << std::endl;
  }
}

BOOST_AUTO_TEST_SUITE_END()


//
// Created by phil on 1/7/17.
//

#include "MovementPattern.h"
#include "Game.h"
#include "Player.h"
#include <cmath>

#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/strategies/transform/matrix_transformers.hpp>

typedef boost::geometry::model::d2::point_xy<double> BoostPoint2d;
using boost::geometry::strategy::transform::translate_transformer;
using boost::geometry::strategy::transform::rotate_transformer;
using boost::geometry::strategy::transform::ublas_transformer;

/*--------------------------------------------------------------------------------------------------------------------*/
MovementPattern::MovementPattern(std::string name) : name_(name)
{
}

/*--------------------------------------------------------------------------------------------------------------------*/
IdleMovementPattern::IdleMovementPattern(void) : MovementPattern("idle")
{
}

/*--------------------------------------------------------------------------------------------------------------------*/
Vec2i IdleMovementPattern::move(Position &current_position)
{
  return {0,0};
}

/*--------------------------------------------------------------------------------------------------------------------*/
LinearMovementPattern::LinearMovementPattern(Direction direction) : MovementPattern("linear"), direction_(direction)
{
}

/*--------------------------------------------------------------------------------------------------------------------*/
Vec2i LinearMovementPattern::move(Position &current_position)
{
  if(direction_ == 0) return {0,0};

  Position old_position = current_position;

  current_position += direction_;

  current_position %= Game::BOARD_SIZE;
  if(current_position.x() < 0) current_position.x() += Game::BOARD_SIZE.x();
  if(current_position.y() < 0) current_position.y() += Game::BOARD_SIZE.y();

  return current_position - old_position;
}

/*--------------------------------------------------------------------------------------------------------------------*/
HarmonicMovementPattern::HarmonicMovementPattern(Direction direction, size_t amplitude, size_t period)
    : MovementPattern("harmonic"), direction_(direction), amplitude_(amplitude), period_(period), phi_(0.0), t_(0.0)
{
}

/*--------------------------------------------------------------------------------------------------------------------*/
Vec2i HarmonicMovementPattern::move(Position &current_position)
{
  t_ += direction_.length();

  if (t_ >= Game::BOARD_SIZE.x() * Game::BOARD_SIZE.y())
    t_ -= period_;

  const double frequency = 1.0 / period_;
  const double omega = 2 * M_PI * frequency;
  BoostPoint2d sine_point(t_, -amplitude_ * std::sin(omega * t_ + phi_)); // negative value to invert y-axis

  translate_transformer<double, 2, 2> sine_translation(sine_point.x(), sine_point.y());
  rotate_transformer<boost::geometry::radian, double, 2, 2> rotate(direction_.radian());

  ublas_transformer<double, 2, 2> translateRotate(prod(rotate.matrix(), sine_translation.matrix()));
  BoostPoint2d translate_rotate_position;
  translateRotate.apply(BoostPoint2d(0.0, 0.0), translate_rotate_position);

  Vec2i new_position(static_cast<int>(translate_rotate_position.x() + start_position_.x()),
                     static_cast<int>(translate_rotate_position.y() + start_position_.y()));

  Vec2i delta = current_position - new_position;
  current_position = new_position;

  current_position %= Game::BOARD_SIZE;
  if (current_position.x() < 0) current_position.x() += Game::BOARD_SIZE.x();
  if (current_position.y() < 0) current_position.y() += Game::BOARD_SIZE.y();

  return delta;
}

/*--------------------------------------------------------------------------------------------------------------------*/
CircularMovementPattern::CircularMovementPattern(void)
    : MovementPattern("circular"), phi_(0)
{
  radius_ = RandomNumberGenerator::instance().getRandomInt(50, 300);

  if(RandomNumberGenerator::instance().getRandomInt(0, 1))
    rotation_ = RotationDirection::CW;
  else
    rotation_ = RotationDirection::CCW;
}

/*--------------------------------------------------------------------------------------------------------------------*/
Vec2i CircularMovementPattern::move(Position & current_position)
{

  if (!centered_)
  {
    int tmp_center = start_position_.x() - radius_;
    if (tmp_center < 0)
      tmp_center += 2 * radius_;
    center_ = Position(tmp_center, start_position_.y());
    centered_ = true;
  }

  if(phi_ == 360)
  {
    phi_ = 0;
  }
  Position old_pos = current_position;

  double x_result, y_result;
  y_result = radius_ * sin((double)phi_*(double)M_PI / (double)180);
  x_result = radius_ * cos((double)phi_*(double)M_PI / (double)180);

  Position new_pos((int)x_result, (int)y_result);
  new_pos = center_ + new_pos;

  if(new_pos.x() < 0)
    new_pos.x() = Game::BOARD_SIZE.x() + new_pos.x();
  if(new_pos.x() > Game::BOARD_SIZE.x())
    new_pos.x() = new_pos.x() - Game::BOARD_SIZE.x();
  if(new_pos.y() < 0)
    new_pos.y() = Game::BOARD_SIZE.y() + new_pos.y();
  if(new_pos.y() > Game::BOARD_SIZE.y())
    new_pos.y() = new_pos.y() - Game::BOARD_SIZE.y();

  current_position = new_pos;
  phi_ += 1;
  return current_position - old_pos;
}


/***************************************************************************
 * Copyright 1998-2020 by authors (see AUTHORS.txt)                        *
 *                                                                         *
 *   This file is part of LuxCoreRender.                                   *
 *                                                                         *
 * 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.                                          *
 ***************************************************************************/

#include <iostream>
#include <fstream>
#include <cstring>

#include <boost/format.hpp>
#include <boost/filesystem.hpp>

#include "luxrays/core/exttrianglemesh.h"
#include "luxrays/utils/ply/rply.h"
#include "luxrays/utils/serializationutils.h"
#include "luxrays/utils/strutils.h"

using namespace std;
using namespace luxrays;

//------------------------------------------------------------------------------
// ExtMesh
//------------------------------------------------------------------------------

BOOST_CLASS_EXPORT_IMPLEMENT(luxrays::ExtMesh)

void ExtMesh::GetDifferentials(const Transform &local2World,
		const u_int triIndex, const Normal &shadeNormal, const u_int dataIndex,
        Vector *dpdu, Vector *dpdv,
        Normal *dndu, Normal *dndv) const {
    // Compute triangle partial derivatives
    const Triangle &tri = GetTriangles()[triIndex];
	const u_int v0Index = tri.v[0];
	const u_int v1Index = tri.v[1];
	const u_int v2Index = tri.v[2];

    UV uv0, uv1, uv2;
    if (HasUVs(dataIndex)) {
        uv0 = GetUV(v0Index, dataIndex);
        uv1 = GetUV(v1Index, dataIndex);
        uv2 = GetUV(v2Index, dataIndex);
    } else {
		uv0 = UV(.5f, .5f);
		uv1 = UV(.5f, .5f);
		uv2 = UV(.5f, .5f);
	}

    // Compute deltas for triangle partial derivatives
	const float du1 = uv0.u - uv2.u;
	const float du2 = uv1.u - uv2.u;
	const float dv1 = uv0.v - uv2.v;
	const float dv2 = uv1.v - uv2.v;
	const float determinant = du1 * dv2 - dv1 * du2;

	if (determinant == 0.f) {
		// Handle 0 determinant for triangle partial derivative matrix
		CoordinateSystem(Vector(shadeNormal), dpdu, dpdv);
		*dndu = Normal();
		*dndv = Normal();
	} else {
		const float invdet = 1.f / determinant;

		// Using localToWorld in order to do all computation relative to
		// the global coordinate system
		const Point p0 = GetVertex(local2World, v0Index);
		const Point p1 = GetVertex(local2World, v1Index);
		const Point p2 = GetVertex(local2World, v2Index);

		const Vector dp1 = p0 - p2;
		const Vector dp2 = p1 - p2;

		const Vector geometryDpDu = ( dv2 * dp1 - dv1 * dp2) * invdet;
		const Vector geometryDpDv = (-du2 * dp1 + du1 * dp2) * invdet;

		*dpdu = Cross(shadeNormal, Cross(geometryDpDu, shadeNormal));
		*dpdv = Cross(shadeNormal, Cross(geometryDpDv, shadeNormal));

		if (HasNormals()) {
			// Using localToWorld in order to do all computation relative to
			// the global coordinate system
			const Normal n0 = Normalize(GetShadeNormal(local2World, v0Index));
			const Normal n1 = Normalize(GetShadeNormal(local2World, v1Index));
			const Normal n2 = Normalize(GetShadeNormal(local2World, v2Index));

			const Normal dn1 = n0 - n2;
			const Normal dn2 = n1 - n2;
			*dndu = ( dv2 * dn1 - dv1 * dn2) * invdet;
			*dndv = (-du2 * dn1 + du1 * dn2) * invdet;
		} else {
			*dndu = Normal();
			*dndv = Normal();
		}
	}
}

//------------------------------------------------------------------------------
// ExtTriangleMesh
//------------------------------------------------------------------------------

// This is a workaround to a GCC bug described here:
//  https://svn.boost.org/trac10/ticket/3730
//  https://marc.info/?l=boost&m=126496738227673&w=2
namespace boost{
template<>
struct is_virtual_base_of<luxrays::TriangleMesh, luxrays::ExtTriangleMesh>: public mpl::true_ {};
}

BOOST_CLASS_EXPORT_IMPLEMENT(luxrays::ExtTriangleMesh)

ExtTriangleMesh::ExtTriangleMesh(const u_int meshVertCount, const u_int meshTriCount,
		Point *meshVertices, Triangle *meshTris, Normal *meshNormals,
		UV *mUVs, Spectrum *mCols, float *mAlphas) :
		TriangleMesh(meshVertCount, meshTriCount, meshVertices, meshTris) {
	fill(uvs.begin(), uvs.end(), nullptr);
	fill(cols.begin(), cols.end(), nullptr);
	fill(alphas.begin(), alphas.end(), nullptr);
	fill(vertAOV.begin(), vertAOV.end(), nullptr);
	fill(triAOV.begin(), triAOV.end(), nullptr);

	array<UV *, EXTMESH_MAX_DATA_COUNT> meshUVs;
	fill(meshUVs.begin(), meshUVs.end(), nullptr);
	if (mUVs)
		meshUVs[0] = mUVs;

	array<Spectrum *, EXTMESH_MAX_DATA_COUNT> meshCols;
	fill(meshCols.begin(), meshCols.end(), nullptr);
	if (mCols)
		meshCols[0] = mCols;

	array<float *, EXTMESH_MAX_DATA_COUNT> meshAlphas;
	fill(meshAlphas.begin(), meshAlphas.end(), nullptr);
	if (mAlphas)
		meshAlphas[0] = mAlphas;

	Init(meshNormals, &meshUVs, &meshCols, &meshAlphas);
}

ExtTriangleMesh::ExtTriangleMesh(const u_int meshVertCount, const u_int meshTriCount,
		Point *meshVertices, Triangle *meshTris, Normal *meshNormals,
		array<UV *, EXTMESH_MAX_DATA_COUNT> *meshUVs,
		array<Spectrum *, EXTMESH_MAX_DATA_COUNT> *meshCols,
		array<float *, EXTMESH_MAX_DATA_COUNT> *meshAlphas) :
		TriangleMesh(meshVertCount, meshTriCount, meshVertices, meshTris) {
	fill(uvs.begin(), uvs.end(), nullptr);
	fill(cols.begin(), cols.end(), nullptr);
	fill(alphas.begin(), alphas.end(), nullptr);
	fill(vertAOV.begin(), vertAOV.end(), nullptr);
	fill(triAOV.begin(), triAOV.end(), nullptr);

	Init(meshNormals, meshUVs, meshCols, meshAlphas);
}

void ExtTriangleMesh::Init(Normal *meshNormals,
		array<UV *, EXTMESH_MAX_DATA_COUNT> *meshUVs,
		array<Spectrum *, EXTMESH_MAX_DATA_COUNT> *meshCols,
		array<float *, EXTMESH_MAX_DATA_COUNT> *meshAlphas) {
	if (meshUVs && (meshUVs->size() > EXTMESH_MAX_DATA_COUNT)) {
		throw runtime_error("Error in ExtTriangleMesh::ExtTriangleMesh(): trying to define more (" +
				ToString(meshUVs->size()) + ") UV sets than EXTMESH_MAX_DATA_COUNT");
	}
	if (meshCols && (meshCols->size() > EXTMESH_MAX_DATA_COUNT)) {
		throw runtime_error("Error in ExtTriangleMesh::ExtTriangleMesh(): trying to define more (" +
				ToString(meshCols->size()) + ") Color sets than EXTMESH_MAX_DATA_COUNT");
	}
	if (meshAlphas && (meshAlphas->size() > EXTMESH_MAX_DATA_COUNT)) {
		throw runtime_error("Error in ExtTriangleMesh::ExtTriangleMesh(): trying to define more (" +
				ToString(meshAlphas->size()) + ") Alpha sets than EXTMESH_MAX_DATA_COUNT");
	}

	normals = meshNormals;
	triNormals = new Normal[triCount];

	if (meshUVs)
		uvs = *meshUVs;
	if (meshCols)
		cols = *meshCols;
	if (meshAlphas)
		alphas = *meshAlphas;

	Preprocess();
}

void ExtTriangleMesh::Preprocess() {
	// Compute all triangle normals
	for (u_int i = 0; i < triCount; ++i)
		triNormals[i] = tris[i].GetGeometryNormal(vertices);
}

void ExtTriangleMesh::Delete() {
	delete[] vertices;
	delete[] tris;

	delete[] normals;
	delete[] triNormals;

	for (UV *uv : uvs)
		delete[] uv;
	for (Spectrum *c : cols)
		delete[] c;
	for (float *a : alphas)
		delete[] a;
	for (float *v : vertAOV)
		delete[] v;
	for (float *t : triAOV)
		delete[] t;
}

Normal *ExtTriangleMesh::ComputeNormals() {
	bool allocated;
	if (!normals) {
		allocated = true;
		normals = new Normal[vertCount];
	} else
		allocated = false;

	for (u_int i = 0; i < vertCount; ++i)
		normals[i] = Normal(0.f, 0.f, 0.f);
	for (u_int i = 0; i < triCount; ++i) {
		const Vector e1 = vertices[tris[i].v[1]] - vertices[tris[i].v[0]];
		const Vector e2 = vertices[tris[i].v[2]] - vertices[tris[i].v[0]];
		const Normal N = Normal(Normalize(Cross(e1, e2)));
		normals[tris[i].v[0]] += N;
		normals[tris[i].v[1]] += N;
		normals[tris[i].v[2]] += N;
	}
	//int printedWarning = 0;
	for (u_int i = 0; i < vertCount; ++i) {
		normals[i] = Normalize(normals[i]);
		// Check for degenerate triangles/normals, they can freeze the GPU
		if (isnan(normals[i].x) || isnan(normals[i].y) || isnan(normals[i].z)) {
			/*if (printedWarning < 15) {
				SDL_LOG("The model contains a degenerate normal (index " << i << ")");
				++printedWarning;
			} else if (printedWarning == 15) {
				SDL_LOG("The model contains more degenerate normals");
				++printedWarning;
			}*/
			normals[i] = Normal(0.f, 0.f, 1.f);
		}
	}

	return allocated ? normals : NULL;
}

void ExtTriangleMesh::ApplyTransform(const Transform &trans) {
	TriangleMesh::ApplyTransform(trans);

	if (normals) {
		for (u_int i = 0; i < vertCount; ++i) {
			normals[i] *= trans;
			normals[i] = Normalize(normals[i]);
		}
	}

	Preprocess();
}

void ExtTriangleMesh::CopyAOV(ExtTriangleMesh *destMesh) const {
	for (u_int i = 0; i < EXTMESH_MAX_DATA_COUNT; ++i) {
		if (HasVertexAOV(i)) {
			float *v = new float[vertCount];
			copy(&vertAOV[i][0], &vertAOV[i][0] + vertCount, v);

			destMesh->DeleteVertexAOV(i);
			destMesh->SetVertexAOV(i, v);
		}

		if (HasTriAOV(i)) {
			float *t = new float[triCount];
			copy(&triAOV[i][0], &triAOV[i][0] + triCount, t);

			destMesh->DeleteTriAOV(i);
			destMesh->SetTriAOV(i, t);
		}
	}
}

ExtTriangleMesh *ExtTriangleMesh::CopyExt(Point *meshVertices, Triangle *meshTris, Normal *meshNormals,
		array<UV *, EXTMESH_MAX_DATA_COUNT> *meshUVs,
		array<Spectrum *, EXTMESH_MAX_DATA_COUNT> *meshCols,
		array<float *, EXTMESH_MAX_DATA_COUNT> *meshAlphas) const {
	Point *vs = meshVertices;
	if (!vs) {
		vs = AllocVerticesBuffer(vertCount);
		copy(vertices, vertices + vertCount, vs);
	}

	Triangle *ts = meshTris;
	if (!ts) {
		ts = AllocTrianglesBuffer(triCount);
		copy(tris, tris + triCount, ts);
	}

	Normal *ns = meshNormals;
	if (!ns && HasNormals()) {
		ns = new Normal[vertCount];
		copy(normals, normals + vertCount, ns);
	}

	array<UV *, EXTMESH_MAX_DATA_COUNT> us;
	array<Spectrum *, EXTMESH_MAX_DATA_COUNT> cs;
	array<float *, EXTMESH_MAX_DATA_COUNT> as;
	for (u_int i = 0; i < EXTMESH_MAX_DATA_COUNT; ++i) {
		if (HasUVs(i) && (!meshUVs || !(*meshUVs)[i])) {
			us[i] = new UV[vertCount];
			copy(uvs[i], uvs[i] + vertCount, us[i]);
		} else
			us[i] = meshUVs ? (*meshUVs)[i] : nullptr;

		if (HasColors(i) && (!meshCols || !(*meshCols)[i])) {
			cs[i] = new Spectrum[vertCount];
			copy(cols[i], cols[i] + vertCount, cs[i]);
		} else
			cs[i] = meshCols ? (*meshCols)[i] : nullptr;

		if (HasAlphas(i) && (!meshAlphas || !(*meshAlphas)[i])) {
			as[i] = new float[vertCount];
			copy(alphas[i], alphas[i] + vertCount, as[i]);
		} else
			as[i] = meshAlphas ? (*meshAlphas)[i] : nullptr;
	}

	ExtTriangleMesh *m = new ExtTriangleMesh(vertCount, triCount, vs, ts, ns, &us, &cs, &as);
	m->SetLocal2World(appliedTrans);

	// Copy AOV too
	CopyAOV(m);

	return m;
}

ExtTriangleMesh *ExtTriangleMesh::Copy(Point *meshVertices, Triangle *meshTris, Normal *meshNormals,
		UV *mUVs, Spectrum *mCols, float *mAlphas) const {
	array<UV *, EXTMESH_MAX_DATA_COUNT> meshUVs;
	fill(meshUVs.begin(), meshUVs.end(), nullptr);
	if (mUVs)
		meshUVs[0] = mUVs;

	array<Spectrum *, EXTMESH_MAX_DATA_COUNT> meshCols;
	fill(meshCols.begin(), meshCols.end(), nullptr);
	if (mCols)
		meshCols[0] = mCols;

	array<float *, EXTMESH_MAX_DATA_COUNT> meshAlphas;
	fill(meshAlphas.begin(), meshAlphas.end(), nullptr);
	if (mAlphas)
		meshAlphas[0] = mAlphas;

	return CopyExt(meshVertices, meshTris, meshNormals, &meshUVs, &meshCols, &meshAlphas);
}

ExtTriangleMesh *ExtTriangleMesh::Merge(const vector<const ExtTriangleMesh *> &meshes,
		const vector<Transform> *trans) {
	u_int totalVertexCount = 0;
	u_int totalTriangleCount = 0;

	for (auto mesh : meshes) {
		totalVertexCount += mesh->GetTotalVertexCount();
		totalTriangleCount += mesh->GetTotalTriangleCount();
	}

	assert (totalVertexCount > 0);
	assert (totalTriangleCount > 0);
	assert (meshes.size() > 0);

	Point *meshVertices = AllocVerticesBuffer(totalVertexCount);
	Triangle *meshTris = AllocTrianglesBuffer(totalTriangleCount);

	Normal *meshNormals = nullptr;
	array<UV *, EXTMESH_MAX_DATA_COUNT> meshUVs;
	array<Spectrum *, EXTMESH_MAX_DATA_COUNT> meshCols;
	array<float *, EXTMESH_MAX_DATA_COUNT> meshAlphas;
	array<float *, EXTMESH_MAX_DATA_COUNT> meshVertAOV;
	array<float *, EXTMESH_MAX_DATA_COUNT> meshTriAOV;

	if (meshes[0]->HasNormals())
		meshNormals = new Normal[totalVertexCount];

	for (u_int i = 0; i < EXTMESH_MAX_DATA_COUNT; i++) {
		if (meshes[0]->HasUVs(i))
			meshUVs[i] = new UV[totalVertexCount];
		else
			meshUVs[i] = nullptr;

		if (meshes[0]->HasColors(i))
			meshCols[i] = new Spectrum[totalVertexCount];
		else
			meshCols[i] = nullptr;

		if (meshes[0]->HasAlphas(i))
			meshAlphas[i] = new float[totalVertexCount];
		else
			meshAlphas[i] = nullptr;

		if (meshes[0]->HasVertexAOV(i))
			meshVertAOV[i] = new float[totalVertexCount];
		else
			meshVertAOV[i] = nullptr;

		if (meshes[0]->HasTriAOV(i))
			meshTriAOV[i] = new float[totalTriangleCount];
		else
			meshTriAOV[i] = nullptr;
	}

	u_int vIndex = 0;
	u_int iIndex = 0;
	for (u_int meshIndex = 0; meshIndex < meshes.size(); ++meshIndex) {
		const ExtTriangleMesh *mesh = meshes[meshIndex];
		const Transform *transformation = trans ? &((*trans)[meshIndex]) : nullptr;

		// It is a ExtTriangleMesh so I can use Transform::TRANS_IDENTITY everywhere
		// in the following code instead of local2World

		// Copy the mesh vertices
		if (transformation) {
			for (u_int i = 0; i < mesh->GetTotalVertexCount(); ++i)
				meshVertices[i + vIndex] = (*transformation) * mesh->GetVertex(Transform::TRANS_IDENTITY, i);
		} else {
			for (u_int i = 0; i < mesh->GetTotalVertexCount(); ++i)
				meshVertices[i + vIndex] = mesh->GetVertex(Transform::TRANS_IDENTITY, i);
		}

		// Copy the mesh normals
		if (meshes[0]->HasNormals() != mesh->HasNormals())
			throw runtime_error("Error in ExtTriangleMesh::Merge(): trying to merge meshes with different type of normal definitions");
		if (meshes[0]->HasNormals()) {
			if (transformation) {
				for (u_int i = 0; i < mesh->GetTotalVertexCount(); ++i)
					meshNormals[i + vIndex] = Normalize((*transformation) * mesh->GetShadeNormal(Transform::TRANS_IDENTITY, i));
			} else {
				for (u_int i = 0; i < mesh->GetTotalVertexCount(); ++i)
					meshNormals[i + vIndex] = mesh->GetShadeNormal(Transform::TRANS_IDENTITY, i);
			}
		}

		for (u_int dataIndex = 0; dataIndex < EXTMESH_MAX_DATA_COUNT; dataIndex++) {
			// Copy the mesh uvs
			if (meshes[0]->HasUVs(dataIndex) != mesh->HasUVs(dataIndex))
				throw runtime_error("Error in ExtTriangleMesh::Merge(): trying to merge meshes with different type of UV definitions");
			if (meshes[0]->HasUVs(dataIndex)) {
				for (u_int i = 0; i < mesh->GetTotalVertexCount(); ++i)
					meshUVs[dataIndex][i + vIndex] = mesh->GetUV(i, dataIndex);
			}

			// Copy the mesh colors
			if (meshes[0]->HasColors(dataIndex) != mesh->HasColors(dataIndex))
				throw runtime_error("Error in ExtTriangleMesh::Merge(): trying to merge meshes with different type of color definitions");
			if (meshes[0]->HasColors(dataIndex)) {
				for (u_int i = 0; i < mesh->GetTotalVertexCount(); ++i)
					meshCols[dataIndex][i + vIndex] = mesh->GetColor(i, dataIndex);
			}

			// Copy the mesh alphas
			if (meshes[0]->HasAlphas(dataIndex) != mesh->HasAlphas(dataIndex))
				throw runtime_error("Error in ExtTriangleMesh::Merge(): trying to merge meshes with different type of alpha definitions");
			if (meshes[0]->HasAlphas(dataIndex)) {
				for (u_int i = 0; i < mesh->GetTotalVertexCount(); ++i)
					meshAlphas[dataIndex][i + vIndex] = mesh->GetAlpha(i, dataIndex);
			}

			// Copy the mesh vertex AOV
			if (meshes[0]->HasVertexAOV(dataIndex) != mesh->HasVertexAOV(dataIndex))
				throw runtime_error("Error in ExtTriangleMesh::Merge(): trying to merge meshes with different type of vertex AOV definitions");
			if (meshes[0]->HasVertexAOV(dataIndex)) {
				for (u_int i = 0; i < mesh->GetTotalVertexCount(); ++i)
					meshVertAOV[dataIndex][i + vIndex] = mesh->GetVertexAOV(i, dataIndex);
			}

			// Copy the mesh triangle AOV
			if (meshes[0]->HasTriAOV(dataIndex) != mesh->HasTriAOV(dataIndex))
				throw runtime_error("Error in ExtTriangleMesh::Merge(): trying to merge meshes with different type of triangle AOV definitions");
			if (meshes[0]->HasTriAOV(dataIndex)) {
				for (u_int i = 0; i < mesh->GetTotalTriangleCount(); ++i)
					meshTriAOV[dataIndex][i + vIndex] = mesh->GetTriAOV(i, dataIndex);
			}
		}

		// Translate mesh indices
		const Triangle *tris = mesh->GetTriangles();
		for (u_int j = 0; j < mesh->GetTotalTriangleCount(); j++) {
			meshTris[iIndex].v[0] = tris[j].v[0] + vIndex;
			meshTris[iIndex].v[1] = tris[j].v[1] + vIndex;
			meshTris[iIndex].v[2] = tris[j].v[2] + vIndex;

			++iIndex;
		}

		vIndex += mesh->GetTotalVertexCount();
	}

	ExtTriangleMesh *newMesh = new ExtTriangleMesh(totalVertexCount, totalTriangleCount,
			meshVertices, meshTris, meshNormals, &meshUVs, &meshCols, &meshAlphas);
	for (u_int dataIndex = 0; dataIndex < EXTMESH_MAX_DATA_COUNT; dataIndex++) {
		newMesh->SetVertexAOV(dataIndex, meshVertAOV[dataIndex]);
		newMesh->SetTriAOV(dataIndex, meshTriAOV[dataIndex]);
	}

	return newMesh;
}

// For some reason, LoadSerialized() and SaveSerialized() must be in the same
// file of BOOST_CLASS_EXPORT_IMPLEMENT()

ExtTriangleMesh *ExtTriangleMesh::LoadSerialized(const string &fileName) {
	SerializationInputFile sif(fileName);

	ExtTriangleMesh *mesh;
	sif.GetArchive() >> mesh;

	if (!sif.IsGood())
		throw runtime_error("Error while loading serialized scene: " + fileName);

	return mesh;
}

void ExtTriangleMesh::SaveSerialized(const string &fileName) const {
	SerializationOutputFile sof(fileName);

	const ExtTriangleMesh *mesh = this;
	sof.GetArchive() << mesh;

	if (!sof.IsGood())
		throw runtime_error("Error while saving serialized mesh: " + fileName);

	sof.Flush();
}

//------------------------------------------------------------------------------
// ExtInstanceTriangleMesh
//------------------------------------------------------------------------------

// This is a workaround to a GCC bug described here:
//  https://svn.boost.org/trac10/ticket/3730
//  https://marc.info/?l=boost&m=126496738227673&w=2
namespace boost{
template<>
struct is_virtual_base_of<luxrays::InstanceTriangleMesh, luxrays::ExtInstanceTriangleMesh>: public mpl::true_ {};
}

BOOST_CLASS_EXPORT_IMPLEMENT(luxrays::ExtInstanceTriangleMesh)

void ExtInstanceTriangleMesh::UpdateMeshReferences(ExtTriangleMesh *oldMesh, ExtTriangleMesh *newMesh) {
	if (static_cast<ExtTriangleMesh *>(mesh) == oldMesh) {
		mesh = newMesh;
		cachedArea = false;
	}
}

//------------------------------------------------------------------------------
// ExtMotionTriangleMesh
//------------------------------------------------------------------------------

// This is a workaround to a GCC bug described here:
//  https://svn.boost.org/trac10/ticket/3730
//  https://marc.info/?l=boost&m=126496738227673&w=2
namespace boost{
template<>
struct is_virtual_base_of<luxrays::MotionTriangleMesh, luxrays::ExtMotionTriangleMesh>: public mpl::true_ {};
}

BOOST_CLASS_EXPORT_IMPLEMENT(luxrays::ExtMotionTriangleMesh)

void ExtMotionTriangleMesh::UpdateMeshReferences(ExtTriangleMesh *oldMesh, ExtTriangleMesh *newMesh) {
	if (static_cast<ExtTriangleMesh *>(mesh) == oldMesh) {
		mesh = oldMesh;
		cachedArea = false;
	}
}


// Boost.Geometry

// Copyright (c) 2020, Oracle and/or its affiliates.

// Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle

// Licensed under the Boost Software License version 1.0.
// http://www.boost.org/users/license.html

#ifndef BOOST_GEOMETRY_STRATEGY_CARTESIAN_SIDE_NON_ROBUST_HPP
#define BOOST_GEOMETRY_STRATEGY_CARTESIAN_SIDE_NON_ROBUST_HPP

#include <boost/geometry/util/select_most_precise.hpp>
#include <boost/geometry/util/select_calculation_type.hpp>
#include <boost/geometry/util/precise_math.hpp>

namespace boost { namespace geometry
{

namespace strategy { namespace side
{

/*!
\brief Adaptive precision predicate to check at which side of a segment a point lies:
    left of segment (>0), right of segment (< 0), on segment (0).
\ingroup strategies
\tparam CalculationType \tparam_calculation
\details This predicate determines at which side of a segment a point lies
*/
template
<
    typename CalculationType = void
>
struct side_non_robust
{
public:
    //! \brief Computes double the signed area of the CCW triangle p1, p2, p

#ifndef DOXYGEN_SHOULD_SKIP_THIS
    template
    <
        typename P1,
        typename P2,
        typename P
    >
    static inline int apply(P1 const& p1, P2 const& p2, P const& p)
    {
        typedef typename select_calculation_type_alt
            <
                CalculationType,
                P1,
                P2,
                P
            >::type coordinate_type;
        typedef typename select_most_precise
            <
                coordinate_type,
                double
            >::type promoted_type;

        auto detleft = (promoted_type(get<0>(p1)) - promoted_type(get<0>(p)))
                * (promoted_type(get<1>(p2)) - promoted_type(get<1>(p)));
        auto detright = (promoted_type(get<1>(p1)) - promoted_type(get<1>(p)))
                * (promoted_type(get<0>(p2)) - promoted_type(get<0>(p)));
        return detleft > detright ? 1 : (detleft < detright ? -1 : 0 );

    }
#endif

};

}} // namespace strategy::side

}} // namespace boost::geometry

#endif // BOOST_GEOMETRY_STRATEGY_CARTESIAN_SIDE_NON_ROBUST_HPP


#ifndef TESTER_UTILS_HPP
#define TESTER_UTILS_HPP

#include <Eigen/Core>

#include <atomic>
#include <chrono>
#include <future>
#include <string>
#include <thread>
#include <tuple>
#include <utility>

#include "common.hpp"

using namespace std::chrono_literals;

template<typename Tester1, typename Tester2, typename Test>
bool test_correctness()
{
  Tester1 tester1;
  Tester2 tester2;
  Test test;

  Eigen::Matrix<double, Test::InputSize, 1> x = Eigen::Matrix<double, Test::InputSize, 1>::Ones();
  typename EigenFunctor<Test, decltype(x)>::JacobianType J1, J2;
  try {
    // setup
    tester1.setup([&test](const auto & x) {return test(x);}, x);
    tester2.setup([&test](const auto & x) {return test(x);}, x);

    // test
    for (size_t i = 0; i < 5; i++) {
      x = Eigen::Matrix<double, Test::InputSize, 1>::Random();

      tester1.run([&test](const auto & x) {return test(x);}, x, J1);
      tester2.run([&test](const auto & x) {return test(x);}, x, J2);

      if (!J1.isApprox(J2, 1e-2)) {
        std::cerr << "Different jacobians detected on " << Test::name << std::endl;
        std::cerr << "Jacobian from " << Tester1::name << std::endl;
        std::cerr << J1 << std::endl;
        std::cerr << "Jacobian from " << Tester2::name << std::endl;
        std::cerr << J2 << std::endl;
        return false;
      }
    }
  } catch (const std::exception & e) {
    std::cerr << "Exception thrown during correctness test: " << e.what() << '\n';
    return false;
  }

  return true;
}

struct SpeedResult
{
  std::string exception{""};
  bool setup_timeout{false}, calc_timeout{false};
  uint64_t setup_iter{}, calc_iter{};
  std::chrono::nanoseconds setup_time{}, calc_time{};
};

template<typename Tester, typename Test>
SpeedResult test_speed()
{
  SpeedResult res{};

  Tester tester;
  Test test;

  std::atomic<bool> canceled = false;
  std::promise<std::tuple<std::string, std::size_t, std::chrono::nanoseconds>> setup_promise;
  auto setup_ftr = setup_promise.get_future();

  std::thread setup_thr(
    [&tester, &test, &canceled, &setup_promise]() {
      Eigen::Matrix<double, Test::InputSize, 1> x;
      x.setOnes();
      try {
        std::size_t cntr = 0;
        const auto beg = std::chrono::high_resolution_clock::now();
        while (!canceled) {
          tester.template setup([&test](const auto & x) {return test(x);}, x);
          ++cntr;
        }
        const auto end = std::chrono::high_resolution_clock::now();

        setup_promise.set_value(std::make_tuple(std::string{}, cntr, end - beg));
      } catch (const std::exception & e) {
        setup_promise.set_value(std::make_tuple(e.what(), 0, 0ns));
      }
    });

  std::this_thread::sleep_for(500ms);
  canceled.store(true);

  if (setup_ftr.wait_for(20s) == std::future_status::ready) {
    setup_thr.join();
    std::tie(res.exception, res.setup_iter, res.setup_time) = setup_ftr.get();
  } else {
    setup_thr.detach();
    res.setup_timeout = true;
    return res;
  }

  if (!res.exception.empty()) {
    return res;
  }

  std::promise<std::tuple<std::string, std::size_t, std::chrono::nanoseconds>> calc_promise;
  auto calc_ftr = calc_promise.get_future();
  canceled.store(false);
  std::thread calc_thr([&test, &tester, &canceled, &calc_promise]() {
      Eigen::Matrix<double, Test::InputSize, 1> x;
      x.setOnes();
      typename EigenFunctor<Test, decltype(x)>::JacobianType J;

      try {
        std::size_t cntr = 0;
        const auto beg = std::chrono::high_resolution_clock::now();
        while (!canceled) {
          tester.template run([&test](const auto & x) {return test(x);}, x, J);
          ++cntr;
        }
        const auto end = std::chrono::high_resolution_clock::now();
        calc_promise.set_value(std::make_tuple(std::string{}, cntr, end - beg));
      } catch (const std::exception & e) {
        calc_promise.set_value(std::make_tuple(e.what(), 0, 0ns));
      }
    });

  std::this_thread::sleep_for(3s);
  canceled.store(true);

  if (calc_ftr.wait_for(20s) == std::future_status::ready) {
    calc_thr.join();
    std::tie(res.exception, res.calc_iter, res.calc_time) = calc_ftr.get();
  } else {
    calc_thr.detach();    // forceful termination
    res.calc_timeout = true;
  }

  return res;
}

#endif  // TESTER_UTILS_HPP


// RMS
//
// Copyright (C) 2009
// Center for Perceptual Systems
// University of Texas at Austin
//
// jsp Thu Feb 26 11:01:00 CST 2009

#include "argv.h"
#include "pnm.h"
#include "raster.h"
#include "raster_utils.h"
#include "subscript_function.h"
#include <boost/lambda/lambda.hpp>
#include <fstream>
#include <iostream>
#include <stdexcept>

using namespace boost::lambda;
using namespace horny_toad;
using namespace jack_rabbit;
using namespace std;

int main (int argc, char **argv)
{
    try
    {
        bool help = false;
        bool verbose = false;
        string fn;
        unsigned wsize = 33;
        bool mean = false;

        CommandLine cl;
        cl.AddSpec ("help", 'h', help, "Print help message");
        cl.AddSpec ("verbose", 'v', verbose, "Be verbose");
        cl.AddSpec ("wsize", 'w', wsize, "Weighting function size[33]", "#", "#");
        cl.AddSpec ("mean", 'm', mean, "Only output mean");
        cl.AddSpec ("", '\0', fn, "Input filename", "[FN]");
        // Group argv's into option groups
        cl.GroupArgs (argc, argv, 1);
        // Convert from strings to their proper type
        cl.ExtractBegin ();
        cl.Extract (help);
        cl.Extract (verbose);
        cl.Extract (wsize);
        cl.Extract (mean);
        cl.Extract (fn);
        cl.ExtractEnd ();

        if (help)
        {
            cerr << argv[0] << " " << cl.Usage ();
            cerr << std::endl;
            cerr << cl.Help ();
            return 0;
        }
        if (!cl.GetLeftOverArgs ().empty ())
            throw runtime_error ("usage: " + string (argv[0]) + " " + cl.Usage ());
        raster<unsigned char> img;
        if (fn.empty ())
        {
            cerr << "Reading from stdin" << endl;
            size_t bpp, rows, cols;
            read_pnm_header (cin, bpp, cols, rows);
            if (verbose)
                clog << cols << "x" << rows << "x" << bpp << endl;
            if (bpp != 1)
                throw runtime_error ("The image must be 8 bits/pixel");
            if (verbose)
                clog << "Reading PNM pixels..." << endl;
            img.resize (rows, cols);
            read_pnm_pixels (cin, img);
        }
        else
        {
            cerr << "Reading " << fn << endl;
            if (verbose)
                clog << "Opening " << fn << endl;
            ifstream ifs (fn.c_str ());
            if (!ifs)
                throw runtime_error ("Could not open file for reading");
            if (verbose)
                clog << "Reading PNM header..." << endl;
            size_t bpp, rows, cols;
            read_pnm_header (ifs, bpp, cols, rows);
            if (verbose)
                clog << cols << "x" << rows << "x" << bpp << endl;
            if (bpp != 1)
                throw runtime_error ("The image must be 8 bits/pixel");
            if (verbose)
                clog << "Reading PNM pixels..." << endl;
            img.resize (rows, cols);
            read_pnm_pixels (ifs, img);
        }

        // Create a weighting function
        if (verbose)
        clog << "Computing weighting function..." << endl;
        raster<double> w (wsize, wsize);
        subscript_generator<double,raised_cos> f (w.rows (), w.cols ());
        generate (w.begin (), w.end (), f);
        double w_sum = std::accumulate (w.begin (), w.end (), 0.0);
        for_each (w.begin (), w.end (),  _1 = _1 / w_sum);
        if (verbose)
        {
            clog << "Weighting function:" << endl;
            print2d (clog, w);
        }
        raster<double> r;
        if (mean)
        {
            clog << "Computing mean..." << endl;
            r = convolve<subregion> (img, w);
        }
        else
        {
            clog << "Computing RMS contrast..." << endl;
            r = rms_contrast<subregion> (img, w, 1.0);
        }

        print2d (cout, r);

        return 0;
    }
    catch (const exception &e)
    {
        cerr << e.what () << endl;
        return -1;
    }
}