<commit_before>  /******************************************************************
 *
 * Round for C
 *
 * Copyright (C) Satoshi Konno 2015
 *
 * This is licensed under BSD-style license, see file COPYING.
 *
 ******************************************************************/

#include <boost/test/unit_test.hpp>
#include <string>
#include <round/script/lua.h>

#include "ScriptTestController.h"

#if defined(ROUND_SUPPORT_LUA)

BOOST_AUTO_TEST_SUITE(script)
BOOST_AUTO_TEST_SUITE(lua)

static const char *LUA_ECHO_CODE = "function " RPC_HELLO_METHOD_NAME "(params)\n"
  "return params\n"
  "end";

static const char* LUA_SETKEY_CODE = \
  "json = require(\"json\") -- JSON4Lua\n" \
  "function " RPC_SET_KEY_METHOD_NAME "(jsonParams)" \
  "  params = json.decode(jsonParams)\n" \
  "  return " ROUND_SYSTEM_METHOD_SET_REGISTRY "(params[\"" ROUND_SYSTEM_METHOD_PARAM_KEY "\"], params[\"" ROUND_SYSTEM_METHOD_PARAM_VALUE "\"])\n" \
  "end\n";
static const char* LUA_GETKEY_CODE = \
  "json = require(\"json\") -- JSON4Lua\n" \
  "function " RPC_GET_KEY_METHOD_NAME "(jsonParams)" \
  "  params = json.decode(jsonParams)\n" \
  "  return " ROUND_SYSTEM_METHOD_REMOVE_REGISTRY "(params[\"" ROUND_SYSTEM_METHOD_PARAM_KEY "\"])\n" \
  "end\n";
static const char* LUA_REMOVEKEY_CODE = \
  "json = require(\"json\") -- JSON4Lua\n" \
  "function " RPC_REMOVE_KEY_METHOD_NAME "(jsonParams):\n" \
  "  params = json.decode(jsonParams)\n" \
  "  return " ROUND_SYSTEM_METHOD_REMOVE_REGISTRY "(params[\"" ROUND_SYSTEM_METHOD_PARAM_KEY "\"])\n" \
  "end\n";

BOOST_AUTO_TEST_CASE(LuaEngineTest)
{
#define SCRIPT_ECHO_LOOP 10
#define LUA_ECHO_PARAM "hello"

  RoundLuaEngine* luaEngine = round_lua_engine_new();

  RoundMethod* method = round_method_new();
  round_method_setname(method, RPC_HELLO_METHOD_NAME);
  round_method_setstringcode(method, LUA_ECHO_CODE);

  RoundJSONObject* resObj;
  const char *resStr;
  RoundError* err = round_error_new();

  BOOST_CHECK(luaEngine);

  for (int n = 0; n < SCRIPT_ECHO_LOOP; n++) {
    BOOST_CHECK(round_lua_engine_run(luaEngine, method, LUA_ECHO_PARAM, &resObj, err));
    BOOST_CHECK(resObj);
    BOOST_CHECK(round_json_object_tostring(resObj, &resStr));
    BOOST_CHECK(round_streq(LUA_ECHO_PARAM, resStr));
    if (resObj) {
      round_json_object_delete(resObj);
    }
  }

  BOOST_CHECK(round_method_delete(method));
  BOOST_CHECK(round_error_delete(err));
  BOOST_CHECK(round_lua_engine_delete(luaEngine));
}

BOOST_AUTO_TEST_CASE(LuaRegistryMethods)
{
  RoundLocalNode* node = round_local_node_new();
  BOOST_CHECK(round_local_node_start(node));

  RoundError* err = round_error_new();

  // TODO : Install JSON4Lua

  // Post Node Message (Set 'hello' method)

  BOOST_CHECK(round_node_setmethod((RoundNode*)node, ROUND_SCRIPT_LANGUAGE_LUA, RPC_HELLO_METHOD_NAME, LUA_ECHO_CODE, err));

  // Post Node Message (Set '*_key' method)

  BOOST_CHECK(round_node_setmethod((RoundNode*)node, ROUND_SCRIPT_LANGUAGE_LUA, RPC_SET_KEY_METHOD_NAME, LUA_SETKEY_CODE, err));
  BOOST_CHECK(round_node_setmethod((RoundNode*)node, ROUND_SCRIPT_LANGUAGE_LUA, RPC_GET_KEY_METHOD_NAME, LUA_GETKEY_CODE, err));
  BOOST_CHECK(round_node_setmethod((RoundNode*)node, ROUND_SCRIPT_LANGUAGE_LUA, RPC_REMOVE_KEY_METHOD_NAME, LUA_REMOVEKEY_CODE, err));

  // Run Methods

  Round::Test::ScriptTestController scriptTestCtrl;
  scriptTestCtrl.runScriptMethodTest(node);

  // Clean up

  BOOST_CHECK(round_error_delete(err));

  BOOST_CHECK(round_local_node_stop(node));
  BOOST_CHECK(round_local_node_delete(node));
}

BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE_END()

#endif
<commit_msg>* Updated LUA functions.<commit_after>  /******************************************************************
 *
 * Round for C
 *
 * Copyright (C) Satoshi Konno 2015
 *
 * This is licensed under BSD-style license, see file COPYING.
 *
 ******************************************************************/

#include <boost/test/unit_test.hpp>
#include <string>
#include <round/script/lua.h>

#include "ScriptTestController.h"

#if defined(ROUND_SUPPORT_LUA)

BOOST_AUTO_TEST_SUITE(script)
BOOST_AUTO_TEST_SUITE(lua)

static const char *LUA_ECHO_CODE = "function " RPC_HELLO_METHOD_NAME "(params)\n"
  "return params\n"
  "end";

static const char* LUA_SETKEY_CODE = \
  "json = require(\"json\") -- JSON4Lua\n" \
  "function " RPC_SET_KEY_METHOD_NAME "(jsonParams)" \
  "  params = json.decode(jsonParams)\n" \
  "  return " ROUND_SYSTEM_METHOD_SET_REGISTRY "(params[\"" ROUND_SYSTEM_METHOD_PARAM_KEY "\"], params[\"" ROUND_SYSTEM_METHOD_PARAM_VALUE "\"])\n" \
  "end\n";
static const char* LUA_GETKEY_CODE = \
  "json = require(\"json\") -- JSON4Lua\n" \
  "function " RPC_GET_KEY_METHOD_NAME "(jsonParams)" \
  "  params = json.decode(jsonParams)\n" \
  "  success, value = " ROUND_SYSTEM_METHOD_GET_REGISTRY "(params[\"" ROUND_SYSTEM_METHOD_PARAM_KEY "\"])\n" \
  "  return value\n" \
  "end\n";
static const char* LUA_REMOVEKEY_CODE = \
  "json = require(\"json\") -- JSON4Lua\n" \
  "function " RPC_REMOVE_KEY_METHOD_NAME "(jsonParams)\n" \
  "  params = json.decode(jsonParams)\n" \
  "  return " ROUND_SYSTEM_METHOD_REMOVE_REGISTRY "(params[\"" ROUND_SYSTEM_METHOD_PARAM_KEY "\"])\n" \
  "end\n";

BOOST_AUTO_TEST_CASE(LuaEngineTest)
{
#define SCRIPT_ECHO_LOOP 10
#define LUA_ECHO_PARAM "hello"

  RoundLuaEngine* luaEngine = round_lua_engine_new();

  RoundMethod* method = round_method_new();
  round_method_setname(method, RPC_HELLO_METHOD_NAME);
  round_method_setstringcode(method, LUA_ECHO_CODE);

  RoundJSONObject* resObj;
  const char *resStr;
  RoundError* err = round_error_new();

  BOOST_CHECK(luaEngine);

  for (int n = 0; n < SCRIPT_ECHO_LOOP; n++) {
    BOOST_CHECK(round_lua_engine_run(luaEngine, method, LUA_ECHO_PARAM, &resObj, err));
    BOOST_CHECK(resObj);
    BOOST_CHECK(round_json_object_tostring(resObj, &resStr));
    BOOST_CHECK(round_streq(LUA_ECHO_PARAM, resStr));
    if (resObj) {
      round_json_object_delete(resObj);
    }
  }

  BOOST_CHECK(round_method_delete(method));
  BOOST_CHECK(round_error_delete(err));
  BOOST_CHECK(round_lua_engine_delete(luaEngine));
}

BOOST_AUTO_TEST_CASE(LuaRegistryMethods)
{
  RoundLocalNode* node = round_local_node_new();
  BOOST_CHECK(round_local_node_start(node));

  RoundError* err = round_error_new();

  // TODO : Install JSON4Lua

  // Post Node Message (Set 'hello' method)

  BOOST_CHECK(round_node_setmethod((RoundNode*)node, ROUND_SCRIPT_LANGUAGE_LUA, RPC_HELLO_METHOD_NAME, LUA_ECHO_CODE, err));

  // Post Node Message (Set '*_key' method)

  BOOST_CHECK(round_node_setmethod((RoundNode*)node, ROUND_SCRIPT_LANGUAGE_LUA, RPC_SET_KEY_METHOD_NAME, LUA_SETKEY_CODE, err));
  BOOST_CHECK(round_node_setmethod((RoundNode*)node, ROUND_SCRIPT_LANGUAGE_LUA, RPC_GET_KEY_METHOD_NAME, LUA_GETKEY_CODE, err));
  BOOST_CHECK(round_node_setmethod((RoundNode*)node, ROUND_SCRIPT_LANGUAGE_LUA, RPC_REMOVE_KEY_METHOD_NAME, LUA_REMOVEKEY_CODE, err));

  // Run Methods

  Round::Test::ScriptTestController scriptTestCtrl;
  scriptTestCtrl.runScriptMethodTest(node);

  // Clean up

  BOOST_CHECK(round_error_delete(err));

  BOOST_CHECK(round_local_node_stop(node));
  BOOST_CHECK(round_local_node_delete(node));
}

BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE_END()

#endif
<|endoftext|>


<commit_before>#include <mutex>
#include <tuple>
#include <utility>
#include <vector>

#include "gungnir/gungnir.hpp"

#include "catch.hpp"

SCENARIO("dispatchSerial maintains order of tasks", "[serial]") {

    GIVEN("an ordered sequence of tasks") {

        std::vector<int> v1, v2, v3, v4;
        std::mutex m1, m2, m3, m4;

        std::vector<gungnir::Task<void>> tasks1, tasks2, tasks3, tasks4;
        for (int i = 0; i < 1000; ++i) {
            tasks1.emplace_back([i, &v1, &m1] {
                std::unique_lock<std::mutex> lk{m1};
                v1.emplace_back(i);
            });
        }
        for (int i = 0; i < 1000; ++i) {
            tasks2.emplace_back([i, &v2, &m2] {
                std::unique_lock<std::mutex> lk{m2};
                v2.emplace_back(i);
            });
        }
        for (int i = 0; i < 1000; ++i) {
            tasks3.emplace_back([i, &v3, &m3] {
                std::unique_lock<std::mutex> lk{m3};
                v3.emplace_back(i);
            });
        }
        for (int i = 0; i < 1000; ++i) {
            tasks4.emplace_back([i, &v4, &m4] {
                std::unique_lock<std::mutex> lk{m4};
                v4.emplace_back(i);
            });
        }

        WHEN("passed to dispatchSerial") {

            {
                gungnir::TaskPool tp{8};

                std::thread t1{[&tp, &tasks1] {
                    tp.dispatchSerial(tasks1.cbegin(), tasks1.cend());
                }};
                tp.dispatchSerial(tasks2.cbegin(), tasks2.cend());
                std::thread t2{[&tp, &tasks3] {
                    tp.dispatchSerial(tasks3.cbegin(), tasks3.cend());
                }};
                tp.dispatchSerial(tasks4.cbegin(), tasks4.cend());

                t1.join();
                t2.join();
            }

            THEN("they are executed in the identical order") {

                for (const auto &v: {v1, v2, v3, v4}) {
                    REQUIRE(v.size() == 1000);

                    bool matched = true;
                    for (int i = 0; matched && i < 1000; ++i) {
                        if (v[i] != i) {
                            matched = false;
                        }
                    }
                    REQUIRE(matched);
                }
            }
        }
    }

    GIVEN("an ordered sequence of tasks that return values") {

        std::vector<int> v1, v2, v3, v4;
        std::mutex m1, m2, m3, m4;

        std::vector<gungnir::Task<int>> tasks1, tasks2, tasks3, tasks4;
        for (int i = 0; i < 1000; ++i) {
            tasks1.emplace_back([i, &v1, &m1] {
                std::unique_lock<std::mutex> lk{m1};
                v1.emplace_back(i);
                return i;
            });
        }
        for (int i = 0; i < 1000; ++i) {
            tasks2.emplace_back([i, &v2, &m2] {
                std::unique_lock<std::mutex> lk{m2};
                v2.emplace_back(i);
                return i;
            });
        }
        for (int i = 0; i < 1000; ++i) {
            tasks3.emplace_back([i, &v3, &m3] {
                std::unique_lock<std::mutex> lk{m3};
                v3.emplace_back(i);
                return i;
            });
        }
        for (int i = 0; i < 1000; ++i) {
            tasks4.emplace_back([i, &v4, &m4] {
                std::unique_lock<std::mutex> lk{m4};
                v4.emplace_back(i);
                return i;
            });
        }

        WHEN("passed to dispatchSerial") {

            std::vector<std::future<int>> f1, f2, f3, f4;

            {
                gungnir::TaskPool tp{8};

                std::thread t1{[&tp, &tasks1, &f1] {
                    f1 = tp.dispatchSerial<int>(
                            tasks1.cbegin(), tasks1.cend());
                }};
                f2 = tp.dispatchSerial<int>(tasks2.cbegin(), tasks2.cend());
                std::thread t2{[&tp, &tasks3, &f3] {
                    f3 = tp.dispatchSerial<int>(
                            tasks3.cbegin(), tasks3.cend());
                }};
                f4 = tp.dispatchSerial<int>(tasks4.cbegin(), tasks4.cend());

                t1.join();
                t2.join();
            }

            THEN("they are executed in the identical order") {

                auto fvs = {
                    std::make_pair(std::ref(f1), std::ref(v1)),
                    std::make_pair(std::ref(f2), std::ref(v2)),
                    std::make_pair(std::ref(f3), std::ref(v3)),
                    std::make_pair(std::ref(f4), std::ref(v4)),
                };
                for (auto &fv: fvs) {
                    auto &f = fv.first;
                    auto &v = fv.second;

                    REQUIRE(f.size() == 1000);
                    REQUIRE(v.size() == 1000);

                    bool matched = true;
                    for (int i = 0; matched && i < 1000; ++i) {
                        if (v[i] != i || f[i].get() != i) {
                            matched = false;
                        }
                    }
                    REQUIRE(matched);
                }
            }
        }
    }
}

<commit_msg>added more exception tests<commit_after>#include <cstring>
#include <exception>
#include <mutex>
#include <utility>
#include <vector>

#include "gungnir/gungnir.hpp"

#include "catch.hpp"

SCENARIO("dispatchSerial maintains order of tasks", "[serial]") {

    GIVEN("an ordered sequence of tasks") {

        std::vector<int> v1, v2, v3, v4;
        std::mutex m1, m2, m3, m4;

        std::vector<gungnir::Task<void>> tasks1, tasks2, tasks3, tasks4;
        for (int i = 0; i < 1000; ++i) {
            tasks1.emplace_back([i, &v1, &m1] {
                std::unique_lock<std::mutex> lk{m1};
                v1.emplace_back(i);
            });
            tasks2.emplace_back([i, &v2, &m2] {
                std::unique_lock<std::mutex> lk{m2};
                v2.emplace_back(i);
            });
            tasks3.emplace_back([i, &v3, &m3] {
                std::unique_lock<std::mutex> lk{m3};
                v3.emplace_back(i);
            });
            tasks4.emplace_back([i, &v4, &m4] {
                std::unique_lock<std::mutex> lk{m4};
                v4.emplace_back(i);
            });
        }

        WHEN("passed to dispatchSerial") {

            {
                gungnir::TaskPool tp{8};

                std::thread t1{[&tp, &tasks1] {
                    tp.dispatchSerial(tasks1.cbegin(), tasks1.cend());
                }};
                tp.dispatchSerial(tasks2.cbegin(), tasks2.cend());
                std::thread t2{[&tp, &tasks3] {
                    tp.dispatchSerial(tasks3.cbegin(), tasks3.cend());
                }};
                tp.dispatchSerial(tasks4.cbegin(), tasks4.cend());

                t1.join();
                t2.join();
            }

            THEN("they are executed in the identical order") {

                for (const auto &v: {v1, v2, v3, v4}) {
                    REQUIRE(v.size() == 1000);

                    bool matched = true;
                    for (int i = 0; matched && i < 1000; ++i) {
                        if (v[i] != i) {
                            matched = false;
                        }
                    }
                    REQUIRE(matched);
                }
            }
        }
    }

    GIVEN("an ordered sequence of tasks that return values") {

        std::vector<int> v1, v2, v3, v4;
        std::mutex m1, m2, m3, m4;

        std::vector<gungnir::Task<int>> tasks1, tasks2, tasks3, tasks4;
        for (int i = 0; i < 10; ++i) {
            tasks1.emplace_back([i, &v1, &m1] {
                throw std::runtime_error{{static_cast<char>(i + '0'), '\0'}};
                std::unique_lock<std::mutex> lk{m1};
                v1.emplace_back(i);
                return i;
            });
            tasks2.emplace_back([i, &v2, &m2] {
                throw std::runtime_error{{static_cast<char>(i + '0'), '\0'}};
                std::unique_lock<std::mutex> lk{m2};
                v2.emplace_back(i);
                return i;
            });
            tasks3.emplace_back([i, &v3, &m3] {
                throw std::runtime_error{{static_cast<char>(i + '0'), '\0'}};
                std::unique_lock<std::mutex> lk{m3};
                v3.emplace_back(i);
                return i;
            });
            tasks4.emplace_back([i, &v4, &m4] {
                throw std::runtime_error{{static_cast<char>(i + '0'), '\0'}};
                std::unique_lock<std::mutex> lk{m4};
                v4.emplace_back(i);
                return i;
            });
        }
        for (int i = 0; i < 1000; ++i) {
            tasks1.emplace_back([i, &v1, &m1] {
                std::unique_lock<std::mutex> lk{m1};
                v1.emplace_back(i);
                return i;
            });
            tasks2.emplace_back([i, &v2, &m2] {
                std::unique_lock<std::mutex> lk{m2};
                v2.emplace_back(i);
                return i;
            });
            tasks3.emplace_back([i, &v3, &m3] {
                std::unique_lock<std::mutex> lk{m3};
                v3.emplace_back(i);
                return i;
            });
            tasks4.emplace_back([i, &v4, &m4] {
                std::unique_lock<std::mutex> lk{m4};
                v4.emplace_back(i);
                return i;
            });
        }

        WHEN("passed to dispatchSerial") {

            std::vector<std::future<int>> f1, f2, f3, f4;

            {
                gungnir::TaskPool tp{8};

                std::thread t1{[&tp, &tasks1, &f1] {
                    f1 = tp.dispatchSerial<int>(
                            tasks1.cbegin(), tasks1.cend());
                }};
                f2 = tp.dispatchSerial<int>(tasks2.cbegin(), tasks2.cend());
                std::thread t2{[&tp, &tasks3, &f3] {
                    f3 = tp.dispatchSerial<int>(
                            tasks3.cbegin(), tasks3.cend());
                }};
                f4 = tp.dispatchSerial<int>(tasks4.cbegin(), tasks4.cend());

                t1.join();
                t2.join();
            }

            THEN("they are executed in the identical order") {

                auto fvs = {
                    std::make_pair(std::ref(f1), std::ref(v1)),
                    std::make_pair(std::ref(f2), std::ref(v2)),
                    std::make_pair(std::ref(f3), std::ref(v3)),
                    std::make_pair(std::ref(f4), std::ref(v4)),
                };
                for (auto &fv: fvs) {
                    auto &f = fv.first;
                    auto &v = fv.second;

                    REQUIRE(f.size() == 1010);
                    REQUIRE(v.size() == 1000);

                    bool matched = true;
                    for (int i = 0; matched && i < 10; ++i) {
                        try {
                            f[i].get();
                            matched = false;
                        } catch (const std::runtime_error &e) {
                            const char s[] = {static_cast<char>(i + '0'), '\0'};
                            matched = !std::strcmp(e.what(), s);
                        } catch (...) {
                            matched = false;
                        }
                    }
                    for (int i = 0; matched && i < 1000; ++i) {
                        if (v[i] != i || f[i + 10].get() != i) {
                            matched = false;
                        }
                    }
                    REQUIRE(matched);
                }
            }
        }
    }
}

<|endoftext|>


<commit_before>/*=========================================================================
 *
 *  Copyright Insight Software Consortium
 *
 *  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.txt
 *
 *  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 "itkRegularStepGradientDescentOptimizerv4.h"
#include "itkMath.h"

/**
 *  The objectif function is the quadratic form:
 *
 *  1/2 x^T A x - b^T x
 *
 *  Where A is a matrix and b is a vector
 *  The system in this example is:
 *
 *     | 3  2 ||x|   | 2|   |0|
 *     | 2  6 ||y| + |-8| = |0|
 *
 *
 *   the solution is the vector | 2 -2 |
 *
 * \class RSGv4TestMetric
 */
class RSGv4TestMetric : public itk::ObjectToObjectMetricBase
{
public:

  typedef RSGv4TestMetric               Self;
  typedef itk::ObjectToObjectMetricBase Superclass;
  typedef itk::SmartPointer<Self>       Pointer;
  typedef itk::SmartPointer<const Self> ConstPointer;
  itkNewMacro( Self );

  enum { SpaceDimension=2 };

  typedef Superclass::ParametersType      ParametersType;
  typedef Superclass::DerivativeType      DerivativeType;
  typedef Superclass::ParametersValueType ParametersValueType;
  typedef Superclass::MeasureType         MeasureType;

  RSGv4TestMetric()
  {
    m_Parameters.SetSize( SpaceDimension );
    m_Parameters.Fill( 0 );
  }

  virtual void Initialize(void) throw ( itk::ExceptionObject ) ITK_OVERRIDE {}

  virtual void GetDerivative( DerivativeType & derivative ) const ITK_OVERRIDE
  {
    MeasureType value;
    GetValueAndDerivative( value, derivative );
  }

  void GetValueAndDerivative( MeasureType & value,
                              DerivativeType & derivative ) const ITK_OVERRIDE
  {
    if( derivative.Size() != 2 )
      {
      derivative.SetSize(2);
      }

    double x = m_Parameters[0];
    double y = m_Parameters[1];

    std::cout << "GetValueAndDerivative( ";
    std::cout << x << " ";
    std::cout << y << ") = ";

    value = 0.5*(3*x*x+4*x*y+6*y*y) - 2*x + 8*y;

    std::cout << "value: " << value << std::endl;

    /* The optimizer simply takes the derivative from the metric
     * and adds it to the transform after scaling. So instead of
     * setting a 'minimize' option in the gradient, we return
     * a minimizing derivative. */
    derivative[0] = -( 3 * x + 2 * y -2 );
    derivative[1] = -( 2 * x + 6 * y +8 );

    std::cout << "derivative: " << derivative << std::endl;
  }

  virtual MeasureType  GetValue() const ITK_OVERRIDE
  {
    return 0.0;
  }

  virtual void UpdateTransformParameters( const DerivativeType & update, ParametersValueType factor ) ITK_OVERRIDE
  {
    m_Parameters += update * factor;
  }

  virtual unsigned int GetNumberOfParameters(void) const ITK_OVERRIDE
  {
    return SpaceDimension;
  }

  virtual bool HasLocalSupport() const ITK_OVERRIDE
  {
    return false;
  }

  virtual unsigned int GetNumberOfLocalParameters() const ITK_OVERRIDE
  {
    return SpaceDimension;
  }

  /* These Set/Get methods are only needed for this test derivation that
   * isn't using a transform */
  virtual void SetParameters( ParametersType & parameters ) ITK_OVERRIDE
  {
    m_Parameters = parameters;
  }

  virtual const ParametersType & GetParameters() const ITK_OVERRIDE
  {
    return m_Parameters;
  }

private:
  ParametersType m_Parameters;
};

///////////////////////////////////////////////////////////
int itkRegularStepGradientDescentOptimizerv4Test(int, char* [] )
{
  std::cout << "RegularStepGradientDescentOptimizerv4 Test ";
  std::cout << std::endl << std::endl;

  typedef  itk::RegularStepGradientDescentOptimizerv4<double>  OptimizerType;

  typedef  OptimizerType::ScalesType  ScalesType;

  // Declaration of a itkOptimizer
  OptimizerType::Pointer  itkOptimizer = OptimizerType::New();

  // Declaration of the metric
  RSGv4TestMetric::Pointer metric = RSGv4TestMetric::New();

  itkOptimizer->SetMetric( metric );

  typedef RSGv4TestMetric::ParametersType    ParametersType;

  const unsigned int spaceDimension =
                      metric->GetNumberOfParameters();

  // We start not so far from  | 2 -2 |
  ParametersType  initialPosition( spaceDimension );
  initialPosition[0] =  100;
  initialPosition[1] = -100;
  metric->SetParameters( initialPosition );

  ScalesType    parametersScale( spaceDimension );
  parametersScale[0] = 1.0;
  parametersScale[1] = 1.0;

  itkOptimizer->SetScales( parametersScale );
  itkOptimizer->SetGradientMagnitudeTolerance( 1e-6 );
  itkOptimizer->SetLearningRate( 100 );
  itkOptimizer->SetMinimumStepLength( 1e-6 );
  itkOptimizer->SetNumberOfIterations( 900 );

  try
    {
    std::cout << "currentPosition before optimization: " << itkOptimizer->GetMetric()->GetParameters() << std::endl;
    itkOptimizer->StartOptimization();
    std::cout << "currentPosition after optimization: " << itkOptimizer->GetMetric()->GetParameters() << std::endl;
    std::cout << " Stop Condition  = " << itkOptimizer->GetStopConditionDescription() << std::endl;
    }
  catch( itk::ExceptionObject & e )
    {
    std::cout << "Exception thrown ! " << std::endl;
    std::cout << "An error occurred during Optimization" << std::endl;
    std::cout << "Location    = " << e.GetLocation()    << std::endl;
    std::cout << "Description = " << e.GetDescription() << std::endl;
    return EXIT_FAILURE;
    }

  ParametersType finalPosition = itkOptimizer->GetMetric()->GetParameters();
  std::cout << "Solution        = (";
  std::cout << finalPosition[0] << ",";
  std::cout << finalPosition[1] << ")" << std::endl;

  //
  // check results to see if it is within range
  //
  bool pass = true;
  double trueParameters[2] = { 2, -2 };
  for( unsigned int j = 0; j < 2; j++ )
    {
    if( itk::Math::FloatAlmostEqual( finalPosition[j], trueParameters[j] ) )
      {
      pass = false;
      }
    }

  if( !pass )
    {
    std::cout << "Test failed." << std::endl;
    return EXIT_FAILURE;
    }

  // Run now with a different relaxation factor
  std::cout << "\nRun test with a different relaxation factor: 0.8, instead of default value: 0.5." << std::endl;
  {
  metric->SetParameters( initialPosition );

  itkOptimizer->SetRelaxationFactor( 0.8 );
  try
    {
    std::cout << "currentPosition before optimization: " << itkOptimizer->GetMetric()->GetParameters() << std::endl;
    itkOptimizer->StartOptimization();
    std::cout << "currentPosition after optimization: " << itkOptimizer->GetMetric()->GetParameters() << std::endl;
    std::cout << " Stop Condition  = " << itkOptimizer->GetStopConditionDescription() << std::endl;
    }
  catch( itk::ExceptionObject & e )
    {
    std::cout << "Exception thrown ! " << std::endl;
    std::cout << "An error occurred during Optimization" << std::endl;
    std::cout << "Location    = " << e.GetLocation()    << std::endl;
    std::cout << "Description = " << e.GetDescription() << std::endl;
    return EXIT_FAILURE;
    }

  finalPosition = itkOptimizer->GetMetric()->GetParameters();
  std::cout << "Solution        = (";
  std::cout << finalPosition[0] << ",";
  std::cout << finalPosition[1] << ")" << std::endl;

  //
  // check results to see if it is within range
  //
  pass = true;
  for( unsigned int j = 0; j < 2; j++ )
    {
    if( itk::Math::FloatAlmostEqual( finalPosition[j], trueParameters[j] ) )
      {
      pass = false;
      }
    }

  if( !pass )
    {
    std::cout << "Test failed." << std::endl;
    return EXIT_FAILURE;
    }

  }

  //
  // Verify that the optimizer doesn't run if the
  // number of iterations is set to zero.
  //
  std::cout << "\nCheck the optimizer when number of iterations is set to zero:" << std::endl;
  {
  itkOptimizer->SetNumberOfIterations( 0 );
  metric->SetParameters( initialPosition );

  try
    {
    std::cout << "currentPosition before optimization: " << itkOptimizer->GetMetric()->GetParameters() << std::endl;
    itkOptimizer->StartOptimization();
    std::cout << "currentPosition after optimization: " << itkOptimizer->GetMetric()->GetParameters() << std::endl;
    std::cout << " Stop Condition  = " << itkOptimizer->GetStopConditionDescription() << std::endl;
    }
  catch( itk::ExceptionObject & excp )
    {
    std::cout << excp << std::endl;
    return EXIT_FAILURE;
    }

  if( itkOptimizer->GetCurrentIteration() > 0 )
    {
    std::cerr << "The optimizer is running iterations despite of ";
    std::cerr << "having a maximum number of iterations set to zero" << std::endl;
    return EXIT_FAILURE;
    }
  }

  //
  // Test the Exception if the GradientMagnitudeTolerance is set to a negative value
  //
  std::cout << "\nTest the Exception if the GradientMagnitudeTolerance is set to a negative value:" << std::endl;
  itkOptimizer->SetGradientMagnitudeTolerance( -1.0 );
  bool expectedExceptionReceived = false;
  try
    {
    std::cout << "currentPosition before optimization: " << itkOptimizer->GetMetric()->GetParameters() << std::endl;
    itkOptimizer->StartOptimization();
    std::cout << "currentPosition after optimization: " << itkOptimizer->GetMetric()->GetParameters() << std::endl;
    std::cout << " Stop Condition  = " << itkOptimizer->GetStopConditionDescription() << std::endl;
    }
  catch( itk::ExceptionObject & excp )
    {
    expectedExceptionReceived = true;
    std::cout << "Expected Exception " << std::endl;
    std::cout << excp << std::endl;
    }

  if( !expectedExceptionReceived )
    {
    std::cerr << "Failure to produce an exception when";
    std::cerr << "the GradientMagnitudeTolerance is negative " << std::endl;
    std::cerr << "TEST FAILED !" << std::endl;
    return EXIT_FAILURE;
    }

  //
  // Test the Exception if the RelaxationFactor is set to a value more than one.
  //
  std::cout << "\nTest the Exception if the RelaxationFactor is set to a value more than one:" << std::endl;
  itkOptimizer->SetNumberOfIterations( 100 );
  itkOptimizer->SetGradientMagnitudeTolerance( 0.01 );
  itkOptimizer->SetRelaxationFactor( 1.1 );
  expectedExceptionReceived = false;
  try
  {
  std::cout << "currentPosition before optimization: " << itkOptimizer->GetMetric()->GetParameters() << std::endl;
  itkOptimizer->StartOptimization();
  std::cout << "currentPosition after optimization: " << itkOptimizer->GetMetric()->GetParameters() << std::endl;
  std::cout << " Stop Condition  = " << itkOptimizer->GetStopConditionDescription() << std::endl;
  }
  catch( itk::ExceptionObject & excp )
  {
  expectedExceptionReceived = true;
  std::cout << "Expected Exception " << std::endl;
  std::cout << excp << std::endl;
  }

  if( !expectedExceptionReceived )
    {
    std::cerr << "Failure to produce an exception when";
    std::cerr << "the RelaxationFactor is more than one " << std::endl;
    std::cerr << "TEST FAILED !" << std::endl;
    return EXIT_FAILURE;
    }

  std::cout << "TEST PASSED !" << std::endl;
  return EXIT_SUCCESS;

}
<commit_msg>ENH: Improve coverage for itkRegularStepGradientDescentOptimizerv4.<commit_after>/*=========================================================================
 *
 *  Copyright Insight Software Consortium
 *
 *  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.txt
 *
 *  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 "itkRegularStepGradientDescentOptimizerv4.h"
#include "itkGradientDescentOptimizerv4.h"
#include "itkMath.h"
#include "itkTestingMacros.h"

/**
 *  The objective function is the quadratic form:
 *
 *  1/2 x^T A x - b^T x
 *
 *  Where A is a matrix and b is a vector
 *  The system in this example is:
 *
 *     | 3  2 ||x|   | 2|   |0|
 *     | 2  6 ||y| + |-8| = |0|
 *
 *
 *   the solution is the vector | 2 -2 |
 *
 * \class RSGv4TestMetric
 */
class RSGv4TestMetric : public itk::ObjectToObjectMetricBase
{
public:

  typedef RSGv4TestMetric               Self;
  typedef itk::ObjectToObjectMetricBase Superclass;
  typedef itk::SmartPointer<Self>       Pointer;
  typedef itk::SmartPointer<const Self> ConstPointer;
  itkNewMacro( Self );

  enum { SpaceDimension = 2 };

  typedef Superclass::ParametersType      ParametersType;
  typedef Superclass::DerivativeType      DerivativeType;
  typedef Superclass::ParametersValueType ParametersValueType;
  typedef Superclass::MeasureType         MeasureType;

  RSGv4TestMetric()
  {
    m_Parameters.SetSize( SpaceDimension );
    m_Parameters.Fill( 0 );
  }

  virtual void Initialize(void) throw ( itk::ExceptionObject ) ITK_OVERRIDE {}

  virtual void GetDerivative( DerivativeType & derivative ) const ITK_OVERRIDE
  {
    MeasureType value;
    GetValueAndDerivative( value, derivative );
  }

  void GetValueAndDerivative( MeasureType & value,
                              DerivativeType & derivative ) const ITK_OVERRIDE
  {
    if( derivative.Size() != 2 )
      {
      derivative.SetSize(2);
      }

    double x = m_Parameters[0];
    double y = m_Parameters[1];

    std::cout << "GetValueAndDerivative( ";
    std::cout << x << " ";
    std::cout << y << ") = ";

    value = 0.5*(3*x*x+4*x*y+6*y*y) - 2*x + 8*y;

    std::cout << "value: " << value << std::endl;

    //
    // The optimizer simply takes the derivative from the metric
    // and adds it to the transform after scaling. So instead of
    // setting a 'minimize' option in the gradient, we return
    // a minimizing derivative.
    //
    derivative[0] = -( 3 * x + 2 * y -2 );
    derivative[1] = -( 2 * x + 6 * y +8 );

    std::cout << "derivative: " << derivative << std::endl;
  }

  virtual MeasureType  GetValue() const ITK_OVERRIDE
  {
    return 0.0;
  }

  virtual void UpdateTransformParameters( const DerivativeType & update, ParametersValueType factor ) ITK_OVERRIDE
  {
    m_Parameters += update * factor;
  }

  virtual unsigned int GetNumberOfParameters(void) const ITK_OVERRIDE
  {
    return SpaceDimension;
  }

  virtual bool HasLocalSupport() const ITK_OVERRIDE
  {
    return false;
  }

  virtual unsigned int GetNumberOfLocalParameters() const ITK_OVERRIDE
  {
    return SpaceDimension;
  }

  // These Set/Get methods are only needed for this test derivation that
  // isn't using a transform.
  virtual void SetParameters( ParametersType & parameters ) ITK_OVERRIDE
  {
    m_Parameters = parameters;
  }

  virtual const ParametersType & GetParameters() const ITK_OVERRIDE
  {
    return m_Parameters;
  }

private:
  ParametersType m_Parameters;
};

template< typename OptimizerType >
int RegularStepGradientDescentOptimizerv4TestHelper(
  itk::SizeValueType numberOfIterations,
  bool doEstimateLearningRateAtEachIteration,
  bool doEstimateLearningRateOnce,
  typename OptimizerType::InternalComputationValueType relaxationFactor,
  typename OptimizerType::InternalComputationValueType minimumStepLength,
  typename OptimizerType::InternalComputationValueType gradientMagnitudeTolerance,
  typename OptimizerType::MeasureType currentLearningRateRelaxation )
{
  typedef typename OptimizerType::ScalesType ScalesType;

  typename OptimizerType::Pointer optimizer = OptimizerType::New();

  // Declaration of the metric
  RSGv4TestMetric::Pointer metric = RSGv4TestMetric::New();

  optimizer->SetMetric( metric );

  typedef RSGv4TestMetric::ParametersType ParametersType;

  const unsigned int spaceDimension =
    metric->GetNumberOfParameters();

  // We start not so far from | 2 -2 |
  ParametersType  initialPosition( spaceDimension );
  initialPosition[0] =  100;
  initialPosition[1] = -100;
  metric->SetParameters( initialPosition );

  ScalesType parametersScale( spaceDimension );
  parametersScale[0] = 1.0;
  parametersScale[1] = 1.0;

  optimizer->SetScales( parametersScale );

  typename OptimizerType::InternalComputationValueType learningRate = 100;
  optimizer->SetLearningRate( learningRate );

  optimizer->SetNumberOfIterations( numberOfIterations );

  optimizer->SetDoEstimateLearningRateAtEachIteration( doEstimateLearningRateAtEachIteration );
  optimizer->SetDoEstimateLearningRateOnce( doEstimateLearningRateOnce );

  TEST_SET_GET_VALUE( doEstimateLearningRateAtEachIteration, optimizer->GetDoEstimateLearningRateAtEachIteration() );
  TEST_SET_GET_VALUE( doEstimateLearningRateOnce, optimizer->GetDoEstimateLearningRateOnce() );

  optimizer->SetRelaxationFactor( relaxationFactor );

  TEST_SET_GET_VALUE( relaxationFactor, optimizer->GetRelaxationFactor() );

  optimizer->SetMinimumStepLength( minimumStepLength );

  TEST_SET_GET_VALUE( minimumStepLength, optimizer->GetMinimumStepLength() );

  optimizer->SetGradientMagnitudeTolerance( gradientMagnitudeTolerance );

  TEST_SET_GET_VALUE( gradientMagnitudeTolerance,
    optimizer->GetGradientMagnitudeTolerance() );

  optimizer->SetCurrentLearningRateRelaxation( currentLearningRateRelaxation );

  TEST_SET_GET_VALUE( currentLearningRateRelaxation,
    optimizer->GetCurrentLearningRateRelaxation() );

  try
    {
    std::cout << "currentPosition before optimization: " << optimizer->GetMetric()->GetParameters() << std::endl;
    optimizer->StartOptimization();
    std::cout << "currentPosition after optimization: " << optimizer->GetMetric()->GetParameters() << std::endl;
    std::cout << " Stop Condition  = " << optimizer->GetStopConditionDescription() << std::endl;
    }
  catch( itk::ExceptionObject & e )
    {
    std::cout << "Exception thrown ! " << std::endl;
    std::cout << "An error occurred during Optimization" << std::endl;
    std::cout << "Location    = " << e.GetLocation() << std::endl;
    std::cout << "Description = " << e.GetDescription() << std::endl;
    return EXIT_FAILURE;
    }

  if( optimizer->GetCurrentIteration() > 0 )
    {
    std::cerr << "The optimizer is running iterations despite of ";
    std::cerr << "having a maximum number of iterations set to zero" << std::endl;
    return EXIT_FAILURE;
    }

  ParametersType finalPosition = optimizer->GetMetric()->GetParameters();
  std::cout << "Solution        = (";
  std::cout << finalPosition[0] << ",";
  std::cout << finalPosition[1] << ")" << std::endl;

  //
  // Check results to see if it is within range
  //
  bool pass = true;
  double trueParameters[2] = { 2, -2 };
  for( unsigned int j = 0; j < 2; ++j )
    {
    if( itk::Math::FloatAlmostEqual( finalPosition[j], trueParameters[j] ) )
      {
      pass = false;
      }
    }

  if( !pass )
    {
    std::cout << "Test failed." << std::endl;
    return EXIT_FAILURE;
    }

  return EXIT_SUCCESS;
}

int itkRegularStepGradientDescentOptimizerv4Test( int, char* [] )
{
  std::cout << "RegularStepGradientDescentOptimizerv4 Test ";
  std::cout << std::endl << std::endl;

  typedef itk::RegularStepGradientDescentOptimizerv4< double > OptimizerType;

  OptimizerType::Pointer itkOptimizer = OptimizerType::New();

  EXERCISE_BASIC_OBJECT_METHODS( itkOptimizer, RegularStepGradientDescentOptimizerv4,
    GradientDescentOptimizerv4Template );

  bool testStatus = EXIT_SUCCESS;

  bool doEstimateLearningRateAtEachIteration = false;
  bool doEstimateLearningRateOnce = false;

  itk::SizeValueType numberOfIterations = 900;

  OptimizerType::InternalComputationValueType relaxationFactor = 0.5;
  OptimizerType::InternalComputationValueType minimumStepLength = 1e-6;
  OptimizerType::InternalComputationValueType gradientMagnitudeTolerance = 1e-6;
  OptimizerType::MeasureType currentLearningRateRelaxation = 0;

  testStatus = RegularStepGradientDescentOptimizerv4TestHelper< OptimizerType >(
     numberOfIterations,
     doEstimateLearningRateAtEachIteration,
     doEstimateLearningRateOnce,
     relaxationFactor,
     minimumStepLength,
     gradientMagnitudeTolerance,
     currentLearningRateRelaxation );

  // Run now with different learning rate estimation frequencies
  std::cout << "\nRun test with a different learning rate estimation frequencies:"
    " estimate learning rate at each iteration: true; "
    " estimate learning rate once: false." << std::endl;
  {
  bool doEstimateLearningRateAtEachIteration2 = true;
  bool doEstimateLearningRateOnce2 = false;
  testStatus = RegularStepGradientDescentOptimizerv4TestHelper< OptimizerType >(
     numberOfIterations,
     doEstimateLearningRateAtEachIteration2,
     doEstimateLearningRateOnce2,
     relaxationFactor,
     minimumStepLength,
     gradientMagnitudeTolerance,
     currentLearningRateRelaxation );
  }

  // Run now with different learning rate estimation frequencies
  std::cout << "\nRun test with a different learning rate estimation frequencies:"
    " estimate learning rate at each iteration: false; "
    " estimate learning rate once: true." << std::endl;
  {
  bool doEstimateLearningRateAtEachIteration3 = false;
  bool doEstimateLearningRateOnce3 = true;

  testStatus = RegularStepGradientDescentOptimizerv4TestHelper< OptimizerType >(
     numberOfIterations,
     doEstimateLearningRateAtEachIteration3,
     doEstimateLearningRateOnce3,
     relaxationFactor,
     minimumStepLength,
     gradientMagnitudeTolerance,
     currentLearningRateRelaxation );
  }

  // Run now with different learning rate estimation frequencies
  std::cout << "\nRun test with a different learning rate estimation frequencies:"
    " estimate learning rate at each iteration: true; "
    " estimate learning rate once: true." << std::endl;
  {
  bool doEstimateLearningRateAtEachIteration4 = true;
  bool doEstimateLearningRateOnce4 = true;

  testStatus = RegularStepGradientDescentOptimizerv4TestHelper< OptimizerType >(
     numberOfIterations,
     doEstimateLearningRateAtEachIteration4,
     doEstimateLearningRateOnce4,
     relaxationFactor,
     minimumStepLength,
     gradientMagnitudeTolerance,
     currentLearningRateRelaxation );
  }

  // Run now with a different relaxation factor
  std::cout << "\nRun test with a different relaxation factor: 0.8, instead of default value: 0.5." << std::endl;
  {
  OptimizerType::InternalComputationValueType relaxationFactor2 = 0.8;

  testStatus = RegularStepGradientDescentOptimizerv4TestHelper< OptimizerType >(
     numberOfIterations,
     doEstimateLearningRateAtEachIteration,
     doEstimateLearningRateOnce,
     relaxationFactor2,
     minimumStepLength,
     gradientMagnitudeTolerance,
     currentLearningRateRelaxation );
  }

  // Verify that the optimizer doesn't run if the number of iterations is set to zero.
  std::cout << "\nCheck the optimizer when number of iterations is set to zero:" << std::endl;
  {
  itk::SizeValueType numberOfIterations2 = 0;

  testStatus = RegularStepGradientDescentOptimizerv4TestHelper< OptimizerType >(
     numberOfIterations2,
     doEstimateLearningRateAtEachIteration,
     doEstimateLearningRateOnce,
     relaxationFactor,
     minimumStepLength,
     gradientMagnitudeTolerance,
     currentLearningRateRelaxation );
  }

  //
  // Test the Exception if the GradientMagnitudeTolerance is set to a negative value
  //
  std::cout << "\nTest the Exception if the GradientMagnitudeTolerance is set to a negative value:" << std::endl;
  {
  OptimizerType::InternalComputationValueType gradientMagnitudeTolerance2 = -1.0;
  bool expectedExceptionReceived = RegularStepGradientDescentOptimizerv4TestHelper< OptimizerType >(
     numberOfIterations,
     doEstimateLearningRateAtEachIteration,
     doEstimateLearningRateOnce,
     relaxationFactor,
     minimumStepLength,
     gradientMagnitudeTolerance2,
     currentLearningRateRelaxation );

  if( !expectedExceptionReceived )
    {
    std::cerr << "Failure to produce an exception when";
    std::cerr << " the GradientMagnitudeTolerance is negative " << std::endl;
    std::cerr << "TEST FAILED !" << std::endl;
    testStatus = EXIT_FAILURE;
    }
  }

  //
  // Test the Exception if the RelaxationFactor is set to a value more than one.
  //
  std::cout << "\nTest the Exception if the RelaxationFactor is set to a value larger than one:" << std::endl;
  {
  itk::SizeValueType numberOfIterations3 = 100;
  OptimizerType::InternalComputationValueType relaxationFactor3 = 1.1;
  OptimizerType::InternalComputationValueType gradientMagnitudeTolerance3 = 0.01;
  bool expectedExceptionReceived = RegularStepGradientDescentOptimizerv4TestHelper< OptimizerType >(
     numberOfIterations3,
     doEstimateLearningRateAtEachIteration,
     doEstimateLearningRateOnce,
     relaxationFactor3,
     minimumStepLength,
     gradientMagnitudeTolerance3,
     currentLearningRateRelaxation );

  if( !expectedExceptionReceived )
    {
    std::cerr << "Failure to produce an exception when";
    std::cerr << " the RelaxationFactor is larger than one " << std::endl;
    std::cerr << "TEST FAILED !" << std::endl;
    testStatus = EXIT_FAILURE;
    }
  }

  if( !testStatus )
    {
    std::cout << "TEST FINISHED SUCCESSFULLY!" << std::endl;
    }
  else
    {
    std::cout << "TEST FAILED!" << std::endl;
    }

  return testStatus;

}
<|endoftext|>


<commit_before>// 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

#ifndef __PROCESS_PROTOBUF_HPP__
#define __PROCESS_PROTOBUF_HPP__

#include <glog/logging.h>

#include <google/protobuf/arena.h>
#include <google/protobuf/message.h>
#include <google/protobuf/repeated_field.h>

#include <set>
#include <vector>

#include <process/defer.hpp>
#include <process/dispatch.hpp>
#include <process/id.hpp>
#include <process/process.hpp>

#include <stout/hashmap.hpp>
#include <stout/lambda.hpp>

// Provides an implementation of process::post that for a protobuf.
namespace process {

inline void post(const process::UPID& to,
                 const google::protobuf::Message& message)
{
  std::string data;
  message.SerializeToString(&data);
  post(to, message.GetTypeName(), data.data(), data.size());
}

inline void post(const process::UPID& from,
                 const process::UPID& to,
                 const google::protobuf::Message& message)
{
  std::string data;
  message.SerializeToString(&data);
  post(from, to, message.GetTypeName(), data.data(), data.size());
}

} // namespace process {

// The rest of this file provides libprocess "support" for using
// protocol buffers. In particular, this file defines a subclass of
// Process (ProtobufProcess) that allows you to install protocol
// buffer handlers in addition to normal message and HTTP
// handlers. Install handlers can optionally take the sender's UPID
// as their first argument.
// Note that this header file assumes you will be linking
// against BOTH libprotobuf and libglog.

namespace google {
namespace protobuf {

// Type conversions helpful for changing between protocol buffer types
// and standard C++ types (for parameters).
template <typename T>
const T& convert(const T& t)
{
  return t;
}

template <typename T>
std::vector<T> convert(const google::protobuf::RepeatedPtrField<T>& items)
{
  return std::vector<T>(items.begin(), items.end());
}

} // namespace protobuf {
} // namespace google {

template <typename T>
class ProtobufProcess : public process::Process<T>
{
public:
  virtual ~ProtobufProcess() {}

protected:
  virtual void visit(const process::MessageEvent& event)
  {
    if (protobufHandlers.count(event.message.name) > 0) {
      from = event.message.from; // For 'reply'.
      protobufHandlers[event.message.name](
          event.message.from, event.message.body);
      from = process::UPID();
    } else {
      process::Process<T>::visit(event);
    }
  }

  void send(const process::UPID& to,
            const google::protobuf::Message& message)
  {
    std::string data;
    message.SerializeToString(&data);
    process::Process<T>::send(to, message.GetTypeName(), std::move(data));
  }

  using process::Process<T>::send;

  void reply(const google::protobuf::Message& message)
  {
    CHECK(from) << "Attempting to reply without a sender";
    send(from, message);
  }

  // Installs that take the sender as the first argument.
  template <typename M>
  void install(void (T::*method)(const process::UPID&, const M&))
  {
    google::protobuf::Message* m = new M();
    T* t = static_cast<T*>(this);
    protobufHandlers[m->GetTypeName()] =
      lambda::bind(&handlerM<M>,
                   t, method,
                   lambda::_1, lambda::_2);
    delete m;
  }

  template <typename M, typename P>
  using MessageProperty = P(M::*)() const;

  template <typename M>
  void install(void (T::*method)(const process::UPID&))
  {
    google::protobuf::Message* m = new M();
    T* t = static_cast<T*>(this);
    protobufHandlers[m->GetTypeName()] =
      lambda::bind(&handler0,
                   t, method,
                   lambda::_1, lambda::_2);
    delete m;
  }

  template <typename M,
            typename ...P, typename ...PC>
  void install(
      void (T::*method)(const process::UPID&, PC...),
      MessageProperty<M, P>... param)
  {
    google::protobuf::Message* m = new M();
    T* t = static_cast<T*>(this);
    protobufHandlers[m->GetTypeName()] =
      lambda::bind(static_cast<void(&)(
                       T*,
                       void (T::*)(const process::UPID&, PC...),
                       const process::UPID&,
                       const std::string&,
                       MessageProperty<M, P>...)>(handlerN),
                   t, method,
                   lambda::_1, lambda::_2, param...);
    delete m;
  }

  // Installs that do not take the sender.
  template <typename M>
  void install(void (T::*method)(const M&))
  {
    google::protobuf::Message* m = new M();
    T* t = static_cast<T*>(this);
    protobufHandlers[m->GetTypeName()] =
      lambda::bind(&_handlerM<M>,
                   t, method,
                   lambda::_1, lambda::_2);
    delete m;
  }

  template <typename M>
  void install(void (T::*method)())
  {
    google::protobuf::Message* m = new M();
    T* t = static_cast<T*>(this);
    protobufHandlers[m->GetTypeName()] =
      lambda::bind(&_handler0,
                   t, method,
                   lambda::_1, lambda::_2);
    delete m;
  }

  template <typename M,
            typename ...P, typename ...PC>
  void install(
      void (T::*method)(PC...),
      MessageProperty<M, P>... param)
  {
    google::protobuf::Message* m = new M();
    T* t = static_cast<T*>(this);
    protobufHandlers[m->GetTypeName()] =
      lambda::bind(static_cast<void(&)(
                       T*,
                       void (T::*)(PC...),
                       const process::UPID&,
                       const std::string&,
                       MessageProperty<M, P>...)>(_handlerN),
                   t, method,
                   lambda::_1, lambda::_2, param...);
    delete m;
  }

  using process::Process<T>::install;

private:
  // Handlers that take the sender as the first argument.
  template <typename M>
  static void handlerM(
      T* t,
      void (T::*method)(const process::UPID&, const M&),
      const process::UPID& sender,
      const std::string& data)
  {
    google::protobuf::Arena arena;
    M* m = CHECK_NOTNULL(google::protobuf::Arena::CreateMessage<M>(&arena));
    m->ParseFromString(data);

    if (m->IsInitialized()) {
      (t->*method)(sender, *m);
    } else {
      LOG(WARNING) << "Initialization errors: "
                   << m->InitializationErrorString();
    }
  }

  static void handler0(
      T* t,
      void (T::*method)(const process::UPID&),
      const process::UPID& sender,
      const std::string& data)
  {
    (t->*method)(sender);
  }

  template <typename M,
            typename ...P, typename ...PC>
  static void handlerN(
      T* t,
      void (T::*method)(const process::UPID&, PC...),
      const process::UPID& sender,
      const std::string& data,
      MessageProperty<M, P>... p)
  {
    google::protobuf::Arena arena;
    M* m = CHECK_NOTNULL(google::protobuf::Arena::CreateMessage<M>(&arena));
    m->ParseFromString(data);

    if (m->IsInitialized()) {
      (t->*method)(sender, google::protobuf::convert((m->*p)())...);
    } else {
      LOG(WARNING) << "Initialization errors: "
                   << m->InitializationErrorString();
    }
  }

  // Handlers that ignore the sender.
  template <typename M>
  static void _handlerM(
      T* t,
      void (T::*method)(const M&),
      const process::UPID&,
      const std::string& data)
  {
    google::protobuf::Arena arena;
    M* m = CHECK_NOTNULL(google::protobuf::Arena::CreateMessage<M>(&arena));
    m->ParseFromString(data);

    if (m->IsInitialized()) {
      (t->*method)(*m);
    } else {
      LOG(WARNING) << "Initialization errors: "
                   << m->InitializationErrorString();
    }
  }

  static void _handler0(
      T* t,
      void (T::*method)(),
      const process::UPID&,
      const std::string& data)
  {
    (t->*method)();
  }

  template <typename M,
            typename ...P, typename ...PC>
  static void _handlerN(
      T* t,
      void (T::*method)(PC...),
      const process::UPID&,
      const std::string& data,
      MessageProperty<M, P>... p)
  {
    google::protobuf::Arena arena;
    M* m = CHECK_NOTNULL(google::protobuf::Arena::CreateMessage<M>(&arena));
    m->ParseFromString(data);

    if (m->IsInitialized()) {
      (t->*method)(google::protobuf::convert((m->*p)())...);
    } else {
      LOG(WARNING) << "Initialization errors: "
                   << m->InitializationErrorString();
    }
  }

  typedef lambda::function<
      void(const process::UPID&, const std::string&)> handler;
  hashmap<std::string, handler> protobufHandlers;

  // Sender of "current" message, inaccessible by subclasses.
  // This is only used for reply().
  process::UPID from;
};

// Implements a process for sending protobuf "requests" to a process
// and waiting for a protobuf "response", but uses futures so that
// this can be done without needing to implement a process.
template <typename Req, typename Res>
class ReqResProcess : public ProtobufProcess<ReqResProcess<Req, Res>>
{
public:
  ReqResProcess(const process::UPID& _pid, const Req& _req)
    : process::ProcessBase(process::ID::generate("__req_res__")),
      pid(_pid),
      req(_req)
  {
    ProtobufProcess<ReqResProcess<Req, Res>>::template
      install<Res>(&ReqResProcess<Req, Res>::response);
  }

  virtual ~ReqResProcess()
  {
    // Discard the promise.
    promise.discard();
  }

  process::Future<Res> run()
  {
    promise.future().onDiscard(defer(this, &ReqResProcess::discarded));

    ProtobufProcess<ReqResProcess<Req, Res>>::send(pid, req);

    return promise.future();
  }

private:
  void discarded()
  {
    promise.discard();
    process::terminate(this);
  }

  void response(const Res& res)
  {
    promise.set(res);
    process::terminate(this);
  }

  const process::UPID pid;
  const Req req;
  process::Promise<Res> promise;
};

// Allows you to describe request/response protocols and then use
// those for sending requests and getting back responses.
template <typename Req, typename Res>
struct Protocol
{
  process::Future<Res> operator()(
      const process::UPID& pid,
      const Req& req) const
  {
    // Help debugging by adding some "type constraints".
    { Req* req = nullptr; google::protobuf::Message* m = req; (void)m; }
    { Res* res = nullptr; google::protobuf::Message* m = res; (void)m; }

    ReqResProcess<Req, Res>* process = new ReqResProcess<Req, Res>(pid, req);
    process::spawn(process, true);
    return process::dispatch(process, &ReqResProcess<Req, Res>::run);
  }
};

#endif // __PROCESS_PROTOBUF_HPP__
<commit_msg>Enabled rvalue reference parameters in protobuf handlers.<commit_after>// 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

#ifndef __PROCESS_PROTOBUF_HPP__
#define __PROCESS_PROTOBUF_HPP__

#include <glog/logging.h>

#include <google/protobuf/arena.h>
#include <google/protobuf/message.h>
#include <google/protobuf/repeated_field.h>

#include <set>
#include <vector>

#include <process/defer.hpp>
#include <process/dispatch.hpp>
#include <process/id.hpp>
#include <process/process.hpp>

#include <stout/hashmap.hpp>
#include <stout/lambda.hpp>

// Provides an implementation of process::post that for a protobuf.
namespace process {

inline void post(const process::UPID& to,
                 const google::protobuf::Message& message)
{
  std::string data;
  message.SerializeToString(&data);
  post(to, message.GetTypeName(), data.data(), data.size());
}

inline void post(const process::UPID& from,
                 const process::UPID& to,
                 const google::protobuf::Message& message)
{
  std::string data;
  message.SerializeToString(&data);
  post(from, to, message.GetTypeName(), data.data(), data.size());
}

} // namespace process {

// The rest of this file provides libprocess "support" for using
// protocol buffers. In particular, this file defines a subclass of
// Process (ProtobufProcess) that allows you to install protocol
// buffer handlers in addition to normal message and HTTP
// handlers. Install handlers can optionally take the sender's UPID
// as their first argument.
// Note that this header file assumes you will be linking
// against BOTH libprotobuf and libglog.

namespace google {
namespace protobuf {

// Type conversions helpful for changing between protocol buffer types
// and standard C++ types (for parameters).
template <typename T>
const T& convert(const T& t)
{
  return t;
}

template <typename T>
std::vector<T> convert(const google::protobuf::RepeatedPtrField<T>& items)
{
  return std::vector<T>(items.begin(), items.end());
}

} // namespace protobuf {
} // namespace google {

template <typename T>
class ProtobufProcess : public process::Process<T>
{
public:
  virtual ~ProtobufProcess() {}

protected:
  virtual void visit(const process::MessageEvent& event)
  {
    if (protobufHandlers.count(event.message.name) > 0) {
      from = event.message.from; // For 'reply'.
      protobufHandlers[event.message.name](
          event.message.from, event.message.body);
      from = process::UPID();
    } else {
      process::Process<T>::visit(event);
    }
  }

  void send(const process::UPID& to,
            const google::protobuf::Message& message)
  {
    std::string data;
    message.SerializeToString(&data);
    process::Process<T>::send(to, message.GetTypeName(), std::move(data));
  }

  using process::Process<T>::send;

  void reply(const google::protobuf::Message& message)
  {
    CHECK(from) << "Attempting to reply without a sender";
    send(from, message);
  }

  // Installs that take the sender as the first argument.
  template <typename M>
  void install(void (T::*method)(const process::UPID&, const M&))
  {
    google::protobuf::Message* m = new M();
    T* t = static_cast<T*>(this);
    protobufHandlers[m->GetTypeName()] =
      lambda::bind(&handlerM<M>,
                   t, method,
                   lambda::_1, lambda::_2);
    delete m;
  }

  template <typename M>
  void install(void (T::*method)(const process::UPID&, M&&))
  {
    google::protobuf::Message* m = new M();
    T* t = static_cast<T*>(this);
    protobufHandlers[m->GetTypeName()] =
      lambda::bind(&handlerMutM<M>,
                   t, method,
                   lambda::_1, lambda::_2);
    delete m;
  }

  template <typename M, typename P>
  using MessageProperty = P(M::*)() const;

  template <typename M>
  void install(void (T::*method)(const process::UPID&))
  {
    google::protobuf::Message* m = new M();
    T* t = static_cast<T*>(this);
    protobufHandlers[m->GetTypeName()] =
      lambda::bind(&handler0,
                   t, method,
                   lambda::_1, lambda::_2);
    delete m;
  }

  template <typename M,
            typename ...P, typename ...PC>
  void install(
      void (T::*method)(const process::UPID&, PC...),
      MessageProperty<M, P>... param)
  {
    google::protobuf::Message* m = new M();
    T* t = static_cast<T*>(this);
    protobufHandlers[m->GetTypeName()] =
      lambda::bind(static_cast<void(&)(
                       T*,
                       void (T::*)(const process::UPID&, PC...),
                       const process::UPID&,
                       const std::string&,
                       MessageProperty<M, P>...)>(handlerN),
                   t, method,
                   lambda::_1, lambda::_2, param...);
    delete m;
  }

  // Installs that do not take the sender.
  template <typename M>
  void install(void (T::*method)(const M&))
  {
    google::protobuf::Message* m = new M();
    T* t = static_cast<T*>(this);
    protobufHandlers[m->GetTypeName()] =
      lambda::bind(&_handlerM<M>,
                   t, method,
                   lambda::_1, lambda::_2);
    delete m;
  }

  template <typename M>
  void install(void (T::*method)(M&&))
  {
    google::protobuf::Message* m = new M();
    T* t = static_cast<T*>(this);
    protobufHandlers[m->GetTypeName()] =
      lambda::bind(&_handlerMutM<M>,
                   t, method,
                   lambda::_1, lambda::_2);
    delete m;
  }

  template <typename M>
  void install(void (T::*method)())
  {
    google::protobuf::Message* m = new M();
    T* t = static_cast<T*>(this);
    protobufHandlers[m->GetTypeName()] =
      lambda::bind(&_handler0,
                   t, method,
                   lambda::_1, lambda::_2);
    delete m;
  }

  template <typename M,
            typename ...P, typename ...PC>
  void install(
      void (T::*method)(PC...),
      MessageProperty<M, P>... param)
  {
    google::protobuf::Message* m = new M();
    T* t = static_cast<T*>(this);
    protobufHandlers[m->GetTypeName()] =
      lambda::bind(static_cast<void(&)(
                       T*,
                       void (T::*)(PC...),
                       const process::UPID&,
                       const std::string&,
                       MessageProperty<M, P>...)>(_handlerN),
                   t, method,
                   lambda::_1, lambda::_2, param...);
    delete m;
  }

  using process::Process<T>::install;

private:
  // Handlers that take the sender as the first argument.
  template <typename M>
  static void handlerM(
      T* t,
      void (T::*method)(const process::UPID&, const M&),
      const process::UPID& sender,
      const std::string& data)
  {
    google::protobuf::Arena arena;
    M* m = CHECK_NOTNULL(google::protobuf::Arena::CreateMessage<M>(&arena));
    m->ParseFromString(data);

    if (m->IsInitialized()) {
      (t->*method)(sender, *m);
    } else {
      LOG(WARNING) << "Initialization errors: "
                   << m->InitializationErrorString();
    }
  }

  template <typename M>
  static void handlerMutM(
      T* t,
      void (T::*method)(const process::UPID&, M&&),
      const process::UPID& sender,
      const std::string& data)
  {
    M m;
    m.ParseFromString(data);

    if (m.IsInitialized()) {
      (t->*method)(sender, std::move(m));
    } else {
      LOG(WARNING) << "Initialization errors: "
                   << m.InitializationErrorString();
    }
  }

  static void handler0(
      T* t,
      void (T::*method)(const process::UPID&),
      const process::UPID& sender,
      const std::string& data)
  {
    (t->*method)(sender);
  }

  template <typename M,
            typename ...P, typename ...PC>
  static void handlerN(
      T* t,
      void (T::*method)(const process::UPID&, PC...),
      const process::UPID& sender,
      const std::string& data,
      MessageProperty<M, P>... p)
  {
    google::protobuf::Arena arena;
    M* m = CHECK_NOTNULL(google::protobuf::Arena::CreateMessage<M>(&arena));
    m->ParseFromString(data);

    if (m->IsInitialized()) {
      (t->*method)(sender, google::protobuf::convert((m->*p)())...);
    } else {
      LOG(WARNING) << "Initialization errors: "
                   << m->InitializationErrorString();
    }
  }

  // Handlers that ignore the sender.
  template <typename M>
  static void _handlerM(
      T* t,
      void (T::*method)(const M&),
      const process::UPID&,
      const std::string& data)
  {
    google::protobuf::Arena arena;
    M* m = CHECK_NOTNULL(google::protobuf::Arena::CreateMessage<M>(&arena));
    m->ParseFromString(data);

    if (m->IsInitialized()) {
      (t->*method)(*m);
    } else {
      LOG(WARNING) << "Initialization errors: "
                   << m->InitializationErrorString();
    }
  }

  template <typename M>
  static void _handlerMutM(
      T* t,
      void (T::*method)(M&&),
      const process::UPID&,
      const std::string& data)
  {
    M m;
    m.ParseFromString(data);

    if (m.IsInitialized()) {
      (t->*method)(std::move(m));
    } else {
      LOG(WARNING) << "Initialization errors: "
                   << m.InitializationErrorString();
    }
  }

  static void _handler0(
      T* t,
      void (T::*method)(),
      const process::UPID&,
      const std::string& data)
  {
    (t->*method)();
  }

  template <typename M,
            typename ...P, typename ...PC>
  static void _handlerN(
      T* t,
      void (T::*method)(PC...),
      const process::UPID&,
      const std::string& data,
      MessageProperty<M, P>... p)
  {
    google::protobuf::Arena arena;
    M* m = CHECK_NOTNULL(google::protobuf::Arena::CreateMessage<M>(&arena));
    m->ParseFromString(data);

    if (m->IsInitialized()) {
      (t->*method)(google::protobuf::convert((m->*p)())...);
    } else {
      LOG(WARNING) << "Initialization errors: "
                   << m->InitializationErrorString();
    }
  }

  typedef lambda::function<
      void(const process::UPID&, const std::string&)> handler;
  hashmap<std::string, handler> protobufHandlers;

  // Sender of "current" message, inaccessible by subclasses.
  // This is only used for reply().
  process::UPID from;
};

// Implements a process for sending protobuf "requests" to a process
// and waiting for a protobuf "response", but uses futures so that
// this can be done without needing to implement a process.
template <typename Req, typename Res>
class ReqResProcess : public ProtobufProcess<ReqResProcess<Req, Res>>
{
public:
  ReqResProcess(const process::UPID& _pid, const Req& _req)
    : process::ProcessBase(process::ID::generate("__req_res__")),
      pid(_pid),
      req(_req)
  {
    ProtobufProcess<ReqResProcess<Req, Res>>::template
      install<Res>(&ReqResProcess<Req, Res>::response);
  }

  virtual ~ReqResProcess()
  {
    // Discard the promise.
    promise.discard();
  }

  process::Future<Res> run()
  {
    promise.future().onDiscard(defer(this, &ReqResProcess::discarded));

    ProtobufProcess<ReqResProcess<Req, Res>>::send(pid, req);

    return promise.future();
  }

private:
  void discarded()
  {
    promise.discard();
    process::terminate(this);
  }

  void response(const Res& res)
  {
    promise.set(res);
    process::terminate(this);
  }

  const process::UPID pid;
  const Req req;
  process::Promise<Res> promise;
};

// Allows you to describe request/response protocols and then use
// those for sending requests and getting back responses.
template <typename Req, typename Res>
struct Protocol
{
  process::Future<Res> operator()(
      const process::UPID& pid,
      const Req& req) const
  {
    // Help debugging by adding some "type constraints".
    { Req* req = nullptr; google::protobuf::Message* m = req; (void)m; }
    { Res* res = nullptr; google::protobuf::Message* m = res; (void)m; }

    ReqResProcess<Req, Res>* process = new ReqResProcess<Req, Res>(pid, req);
    process::spawn(process, true);
    return process::dispatch(process, &ReqResProcess<Req, Res>::run);
  }
};

#endif // __PROCESS_PROTOBUF_HPP__
<|endoftext|>


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

#include "compiler/cstring.h"
#include "compiler/compiler.h"
#include "compiler/scope.h"
#include "compiler/value.h"
#include "interpreter/location.hh"
#include "types/native_types.h"

namespace clever {

/**
 * Declaration of namespace global type ptrs for Clever native types
 */
DECLARE_CLEVER_NATIVE_TYPES();

/**
 * Compiler initialization phase
 */
void Compiler::init()
{
	m_scope = new Scope;

	m_scope_pool.reserve(10);
	m_value_pool.reserve(30);
	m_type_pool.reserve(15);

	m_scope_pool[m_scope_id++] = m_scope;
	m_value_pool[m_value_id++] = NULL;

	/**
	 * Native type allocation
	 */
	m_type_pool[m_type_id++] = CLEVER_INT_TYPE    = new IntType;
	m_type_pool[m_type_id++] = CLEVER_DOUBLE_TYPE = new DoubleType;
}

/**
 * Frees all resource used by the compiler
 */
void Compiler::shutdown()
{
	CLEVER_SAFE_DELETE(g_cstring_tbl);
	CLEVER_SAFE_DELETE(m_scope);

	ValuePool::const_iterator it = m_value_pool.begin(),
		end = m_value_pool.end();

	while (it != end) {
		delete *it;
		++it;
	}

	TypePool::const_iterator it2 = m_type_pool.begin(),
		end2 = m_type_pool.end();

	while (it2 != end2) {
		delete *it2;
		++it2;
	}
}

/**
 * Compiler termination phase
 */
void Compiler::end()
{
	m_ir.push_back(IR(OP_RETURN, 0));
}

/**
 * Displays an error message and exits
 */
void Compiler::error(const char* msg) const
{
	std::cerr << "Compile error: " << msg << std::endl;
	CLEVER_EXIT_FATAL();
}

/**
 * Displays an error message
 */
void Compiler::error(const std::string& message, const location& loc) const
{
	if (loc.begin.filename) {
		std::cerr << "Compile error: " << message << " on "
			<< *loc.begin.filename << " line " << loc.begin.line << "\n";
	} else {
		std::cerr << "Compile error: " << message << " on line "
			<< loc.begin.line << "\n";
	}
	CLEVER_EXIT_FATAL();
}

/**
 * Displays a formatted error message and abort the compilation
 */
void Compiler::errorf(const location& loc, const char* format, ...) const
{
	std::ostringstream out;
	va_list args;

	va_start(args, format);

	vsprintf(out, format, args);

	va_end(args);

	error(out.str(), loc);
}

/**
 * Compiles a variable declaration
 */
void Compiler::varDeclaration(const CString* var, Value* node)
{
	/**
	 * A null value is represented by m_value_id = 0 on value pool
	 */
	m_ir.push_back(IR(OP_VAR_DECL, m_scope->push(var, node ? m_value_id : 0)));

	if (node) {
		m_value_pool[m_value_id++] = node;
	}
}

/**
 * Compiles a variable assignment
 */
void Compiler::assignment(const CString* var, Value* value, const location& loc)
{
	Symbol* sym = m_scope->getSymbol(var);

	if (!sym) {
		errorf(loc, "Variable `%S' cannot be found!", var);
	}

	m_ir.push_back(IR(OP_ASSIGN, sym->getValueId(), m_value_id));

	m_value_pool[m_value_id++] = value;
}

/**
 * Creates a new lexical scope
 */
void Compiler::newScope()
{
	m_scope = m_scope->newLexicalScope();
	m_scope->setId(m_scope_id);

	m_ir.push_back(IR(OP_SCOPE, m_scope_id));

	m_scope_pool[m_scope_id++] = m_scope;
}

/**
 * Scope end marker to switch to parent scope
 */
void Compiler::endScope()
{
	m_scope = m_scope->getParent();

	m_ir.push_back(IR(OP_SCOPE, m_scope->getId()));
}

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

#include "compiler/cstring.h"
#include "compiler/compiler.h"
#include "compiler/scope.h"
#include "compiler/value.h"
#include "interpreter/location.hh"
#include "types/native_types.h"

namespace clever {

/**
 * Declaration of namespace global type ptrs for Clever native types
 */
DECLARE_CLEVER_NATIVE_TYPES();

/**
 * Compiler initialization phase
 */
void Compiler::init()
{
	m_scope = new Scope;

	m_ir.reserve(20);
	m_scope_pool.reserve(10);
	m_value_pool.reserve(30);
	m_type_pool.reserve(15);

	m_scope_pool[m_scope_id++] = m_scope;
	m_value_pool[m_value_id++] = NULL;

	/**
	 * Native type allocation
	 */
	m_type_pool[m_type_id++] = CLEVER_INT_TYPE    = new IntType;
	m_type_pool[m_type_id++] = CLEVER_DOUBLE_TYPE = new DoubleType;
}

/**
 * Frees all resource used by the compiler
 */
void Compiler::shutdown()
{
	CLEVER_SAFE_DELETE(g_cstring_tbl);
	CLEVER_SAFE_DELETE(m_scope);

	ValuePool::const_iterator it = m_value_pool.begin(),
		end = m_value_pool.end();

	while (it != end) {
		delete *it;
		++it;
	}

	TypePool::const_iterator it2 = m_type_pool.begin(),
		end2 = m_type_pool.end();

	while (it2 != end2) {
		delete *it2;
		++it2;
	}
}

/**
 * Compiler termination phase
 */
void Compiler::end()
{
	m_ir.push_back(IR(OP_RETURN, 0));
}

/**
 * Displays an error message and exits
 */
void Compiler::error(const char* msg) const
{
	std::cerr << "Compile error: " << msg << std::endl;
	CLEVER_EXIT_FATAL();
}

/**
 * Displays an error message
 */
void Compiler::error(const std::string& message, const location& loc) const
{
	if (loc.begin.filename) {
		std::cerr << "Compile error: " << message << " on "
			<< *loc.begin.filename << " line " << loc.begin.line << "\n";
	} else {
		std::cerr << "Compile error: " << message << " on line "
			<< loc.begin.line << "\n";
	}
	CLEVER_EXIT_FATAL();
}

/**
 * Displays a formatted error message and abort the compilation
 */
void Compiler::errorf(const location& loc, const char* format, ...) const
{
	std::ostringstream out;
	va_list args;

	va_start(args, format);

	vsprintf(out, format, args);

	va_end(args);

	error(out.str(), loc);
}

/**
 * Compiles a variable declaration
 */
void Compiler::varDeclaration(const CString* var, Value* node)
{
	/**
	 * A null value is represented by m_value_id = 0 on value pool
	 */
	m_ir.push_back(IR(OP_VAR_DECL, m_scope->push(var, node ? m_value_id : 0)));

	if (node) {
		m_value_pool[m_value_id++] = node;
	}
}

/**
 * Compiles a variable assignment
 */
void Compiler::assignment(const CString* var, Value* value, const location& loc)
{
	Symbol* sym = m_scope->getSymbol(var);

	if (!sym) {
		errorf(loc, "Variable `%S' cannot be found!", var);
	}

	m_ir.push_back(IR(OP_ASSIGN, sym->getValueId(), m_value_id));

	m_value_pool[m_value_id++] = value;
}

/**
 * Creates a new lexical scope
 */
void Compiler::newScope()
{
	m_scope = m_scope->newLexicalScope();
	m_scope->setId(m_scope_id);

	m_ir.push_back(IR(OP_SCOPE, m_scope_id));

	m_scope_pool[m_scope_id++] = m_scope;
}

/**
 * Scope end marker to switch to parent scope
 */
void Compiler::endScope()
{
	m_scope = m_scope->getParent();

	m_ir.push_back(IR(OP_SCOPE, m_scope->getId()));
}

} // clever
<|endoftext|>


<commit_before>/*
The MIT License (MIT)

Copyright (c) 2015 Julius Ikkala

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/* This program searches for all possible combinations of two words that can
 * be subjected to "sananmuunnos" https://en.wikipedia.org/wiki/Sananmuunnos
 * You can optionally define a word or a regex to search for. Requires C++11.
 * */
#include <cstdio>
#include <string>
#include <iostream>
#include <cassert>
#include <fstream>
#include <stdexcept>
#include <vector>
#include <map>
#include <regex>
#include <algorithm>
#include <cstring>
#include <thread>
#ifdef NDEBUG
    #define STATIC static
#else
    #define STATIC
#endif
static const char vowels[][4]={"a","e","i","o","u","y","å","ä","ö","é"};
struct word_data
{
    size_t mora_len;
    bool has_long_vowel;
    const char* vowel;
};
typedef std::map<std::string, word_data> dictionary;
STATIC bool word_in_dictionary(
    const std::string& word,
    const dictionary& dict
){
    return dict.count(word);
}
STATIC word_data analyze_word(const std::string& word)
{
    const char* word_str=nullptr;
    word_data data;
    data.mora_len=0;
    data.has_long_vowel=false;
    data.vowel=nullptr;
    for(word_str=word.c_str();!data.vowel&&*word_str!=0;word_str++)
    {
        // Check if the character is a vowel
        for(size_t i=0;i<sizeof(vowels)/sizeof(const char[4]);++i)
        {
            size_t vlen=strlen(vowels[i]);
            if(strncmp(word_str, vowels[i], vlen)==0)
            {
                data.vowel=vowels[i];
                word_str+=vlen;
                if(strncmp(word_str, data.vowel, vlen)==0)
                {
                    data.has_long_vowel=true;
                    word_str+=vlen;
                }
                break;
            }
        }
    }
    data.mora_len=word_str-word.c_str()-1;
    return data;
}
STATIC void dictionary_add(
    dictionary& dict,
    const std::string& word
){
    dict.insert(std::pair<std::string, word_data>(word, analyze_word(word)));
}
STATIC dictionary read_dictionary(const char* path)
{
    dictionary dict;
    std::ifstream file;
    file.open(path, std::ios::in|std::ios::binary);
    if(!file)
    {
        throw std::runtime_error(std::string(path)+": Unable to read file");
    }
    std::string str;
    while(std::getline(file, str, '\n'))
    {
        if(str.size()!=0)
        {
            std::transform(str.begin(), str.end(), str.begin(), ::tolower);
            dictionary_add(dict, str);
        }
    }
    file.close();
    return dict;
}
STATIC bool sananmuunnos(
    const std::string& word1,
    const word_data& word1_data,
    const std::string& word2,
    const word_data& word2_data,
    std::string& out_word1,
    std::string& out_word2
){
    out_word1.clear();
    out_word2.clear();

    size_t shortest_len=
        word1_data.mora_len<word2_data.mora_len?
        word1_data.mora_len:word2_data.mora_len;
    if(word1_data.vowel==nullptr||word2_data.vowel==nullptr
       ||strncmp(word1.c_str(), word2.c_str(), shortest_len)==0
       ||strcmp(
            word1.c_str()+word1_data.mora_len,
            word2.c_str()+word2_data.mora_len)==0
    ){
        return false;
    }
    if(word1_data.has_long_vowel==word2_data.has_long_vowel){
        out_word1.append(word2, 0, word2_data.mora_len);
        out_word2.append(word1, 0, word1_data.mora_len);
    }
    else if(word1_data.has_long_vowel==false&&word2_data.has_long_vowel==true)
    {
        out_word1.append(
            word2,
            0,
            word2_data.mora_len-strlen(word2_data.vowel)
        );
        out_word2.append(word1, 0, word1_data.mora_len);
        out_word2.append(word1_data.vowel);
    }
    else
    {
        out_word2.append(
            word1,
            0,
            word1_data.mora_len-strlen(word1_data.vowel)
        );
        out_word1.append(word2, 0, word2_data.mora_len);
        out_word1.append(word2_data.vowel);
    }
    out_word1.append(word1, word1_data.mora_len, std::string::npos);
    out_word2.append(word2, word2_data.mora_len, std::string::npos);
    return true;
}
STATIC size_t search_and_print(
    const dictionary& search_dict,
    const dictionary& dict
){
    std::string out_word1, out_word2;
    size_t n=0;
    for(const auto& search_it: search_dict)
    {
        for(const auto& it: dict)
        {
            sananmuunnos(
                search_it.first,
                search_it.second,
                it.first,
                it.second,
                out_word1,
                out_word2
            );
            if(word_in_dictionary(out_word1, dict)&&
               word_in_dictionary(out_word2, dict))
            {
                n++;
                std::cout<<search_it.first<<" "<<it.first<<" => "<<out_word1
                         <<" "<<out_word2<<std::endl;
            }
        }
    }
    return n;
}
enum search_type
{
    SEARCH_ALL,
    SEARCH_WORD,
    SEARCH_REGEX
};
struct arguments {
    std::string dictionary_path;
    search_type search;
    std::string search_word;
    unsigned threads;
};
bool parse_arguments(int argc, char** argv, arguments& args)
{
    args.search=SEARCH_ALL;
    args.search_word="";
    args.threads=0;
    if(argc<2)
    {
        return false;
    }
    args.dictionary_path=argv[1];
    for(int i=2;i<argc;++i)
    {
        if(strcmp("-r", argv[i])==0)
        {
            i++;
            if(i>=argc) return false;
            args.search=SEARCH_REGEX;
            args.search_word=argv[i];
        }
        else if(strcmp("-t", argv[i])==0)
        {
            i++;
            if(i>=argc) return false;
            char* endptr=nullptr;
            args.threads=strtoul(argv[i], &endptr, 10);
            if(endptr!=argv[i]+strlen(argv[i]))
            {
                return false;
            }
        }
        else
        {
            args.search=SEARCH_WORD;
            args.search_word=argv[i];
        }
    }
    return true;
}
int main(int argc, char** argv)
{
    arguments args;
    if(!parse_arguments(argc, argv, args))
    {
        std::cerr<<"Usage: "<<argv[0]
                 <<" dictionary [word|-r regex] [-t threads]\n";
        return 1;
    }
    try {
        dictionary dict(read_dictionary(args.dictionary_path.c_str()));
        size_t n=0;
        if(args.search==SEARCH_ALL)
        {
            n=search_and_print(dict, dict);
        }
        else
        {
            dictionary from;
            if(args.search==SEARCH_WORD)
            {
                dictionary_add(from, args.search_word);
            }
            else if(args.search==SEARCH_REGEX)
            {
                std::regex regex(
                    args.search_word,
                    std::regex_constants::grep|std::regex_constants::icase
                );
                std::cout<<"Searching for following regex matches: "
                         <<std::endl;
                for(const auto& it: dict)
                {
                    if(std::regex_match(it.first, regex))
                    {
                        dictionary_add(from, it.first);
                        std::cout<<"\t"<<it.first<<std::endl;
                    }
                }
            }
            else
            {
                assert(false);
            }
            n=search_and_print(from, dict);
        }
        std::cout<<n<<" found "<<std::endl;
    }
    catch(std::exception& e)
    {
        std::cerr<<e.what()<<std::endl;
        return 1;
    }
    return 0;
}
<commit_msg>Split dictionary to vectors and unordered_sets<commit_after>/*
The MIT License (MIT)

Copyright (c) 2015 Julius Ikkala

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/* This program searches for all possible combinations of two words that can
 * be subjected to "sananmuunnos" https://en.wikipedia.org/wiki/Sananmuunnos
 * You can optionally define a word or a regex to search for. Requires C++11.
 * */
#include <cstdio>
#include <string>
#include <iostream>
#include <cassert>
#include <fstream>
#include <stdexcept>
#include <vector>
#include <set>
#include <unordered_set>
#include <regex>
#include <algorithm>
#include <sstream>
#include <cstring>
#include <thread>
#ifdef NDEBUG
    #define STATIC static
#else
    #define STATIC
#endif
static const char vowels[][4]={"a","e","i","o","u","y","å","ä","ö","é"};
struct word_data
{
    size_t mora_len;
    bool has_long_vowel;
    const char* vowel;
};
struct dictionary_entry
{
    dictionary_entry(const std::string& word, const word_data& data)
    : word(word), data(data)
    {}
    std::string word;
    word_data data;
};
//Used for checking if the formed string is a proper word (O(1) lookup)
typedef std::unordered_set<std::string> search_dictionary;
//Used for actually iterating through words. (simple iteration, random access
//iterator, sorted)
typedef std::vector<dictionary_entry> dictionary;

STATIC bool word_in_dictionary(
    const std::string& word,
    const search_dictionary& sdict
){
    return sdict.count(word);
}
STATIC search_dictionary search_dictionary_from_dictionary(
    const dictionary& dict
){
    search_dictionary sdict;
    for(const dictionary_entry& entry: dict)
    {
        sdict.insert(entry.word);
    }
    return sdict;
}
STATIC word_data analyze_word(const std::string& word)
{
    const char* word_str=nullptr;
    word_data data;
    data.mora_len=0;
    data.has_long_vowel=false;
    data.vowel=nullptr;
    for(word_str=word.c_str();!data.vowel&&*word_str!=0;word_str++)
    {
        // Check if the character is a vowel
        for(size_t i=0;i<sizeof(vowels)/sizeof(const char[4]);++i)
        {
            size_t vlen=strlen(vowels[i]);
            if(strncmp(word_str, vowels[i], vlen)==0)
            {
                data.vowel=vowels[i];
                word_str+=vlen;
                if(strncmp(word_str, data.vowel, vlen)==0)
                {
                    data.has_long_vowel=true;
                    word_str+=vlen;
                }
                break;
            }
        }
    }
    data.mora_len=word_str-word.c_str()-1;
    return data;
}
STATIC void dictionary_add(
    dictionary& dict,
    const std::string& word
){
    dict.push_back(dictionary_entry(word, analyze_word(word)));
}
STATIC dictionary read_dictionary(const char* path)
{
    dictionary dict;
    std::ifstream file;
    file.open(path, std::ios::in|std::ios::binary);
    if(!file)
    {
        throw std::runtime_error(std::string(path)+": Unable to read file");
    }
    std::string str;
    std::set<std::string> unique_lines;
    while(std::getline(file, str, '\n'))
    {
        if(str.size()!=0)
        {
            std::transform(str.begin(), str.end(), str.begin(), ::tolower);
            unique_lines.insert(str);
        }
    }
    dict.reserve(unique_lines.size());
    for(const std::string& line: unique_lines)
    {
        dictionary_add(dict, line);
    }
    file.close();
    return dict;
}
STATIC bool sananmuunnos(
    const std::string& word1,
    const word_data& word1_data,
    const std::string& word2,
    const word_data& word2_data,
    std::string& out_word1,
    std::string& out_word2
){
    out_word1.clear();
    out_word2.clear();

    size_t shortest_len=
        word1_data.mora_len<word2_data.mora_len?
        word1_data.mora_len:word2_data.mora_len;
    if(word1_data.vowel==nullptr||word2_data.vowel==nullptr
       ||strncmp(word1.c_str(), word2.c_str(), shortest_len)==0
       ||strcmp(
            word1.c_str()+word1_data.mora_len,
            word2.c_str()+word2_data.mora_len)==0
    ){
        return false;
    }
    if(word1_data.has_long_vowel==word2_data.has_long_vowel){
        out_word1.append(word2, 0, word2_data.mora_len);
        out_word2.append(word1, 0, word1_data.mora_len);
    }
    else if(word1_data.has_long_vowel==false&&word2_data.has_long_vowel==true)
    {
        out_word1.append(
            word2,
            0,
            word2_data.mora_len-strlen(word2_data.vowel)
        );
        out_word2.append(word1, 0, word1_data.mora_len);
        out_word2.append(word1_data.vowel);
    }
    else
    {
        out_word2.append(
            word1,
            0,
            word1_data.mora_len-strlen(word1_data.vowel)
        );
        out_word1.append(word2, 0, word2_data.mora_len);
        out_word1.append(word2_data.vowel);
    }
    out_word1.append(word1, word1_data.mora_len, std::string::npos);
    out_word2.append(word2, word2_data.mora_len, std::string::npos);
    return true;
}
STATIC void search_and_format(
    const dictionary& dict1,
    const dictionary& dict2,
    const search_dictionary& sdict,
    std::vector<std::string>& formatted
){
    std::string out_word1, out_word2;
    for(const dictionary_entry& entry1: dict1)
    {
        for(const dictionary_entry& entry2: dict2)
        {
            sananmuunnos(
                entry1.word,
                entry1.data,
                entry2.word,
                entry2.data,
                out_word1,
                out_word2
            );
            if(word_in_dictionary(out_word1, sdict)&&
               word_in_dictionary(out_word2, sdict))
            {
                std::stringstream ss;
                ss<<entry1.word<<" "<<entry2.word<<" => "<<out_word1<<" "
                  <<out_word2;
                formatted.push_back(ss.str());
            }
        }
    }
}
enum search_type
{
    SEARCH_ALL,
    SEARCH_WORD,
    SEARCH_REGEX
};
struct arguments {
    std::string dictionary_path;
    search_type search;
    std::string search_word;
    unsigned threads;
};
bool parse_arguments(int argc, char** argv, arguments& args)
{
    args.search=SEARCH_ALL;
    args.search_word="";
    args.threads=0;
    if(argc<2)
    {
        return false;
    }
    args.dictionary_path=argv[1];
    for(int i=2;i<argc;++i)
    {
        if(strcmp("-r", argv[i])==0)
        {
            i++;
            if(i>=argc) return false;
            args.search=SEARCH_REGEX;
            args.search_word=argv[i];
        }
        else if(strcmp("-t", argv[i])==0)
        {
            i++;
            if(i>=argc) return false;
            char* endptr=nullptr;
            args.threads=strtoul(argv[i], &endptr, 10);
            if(endptr!=argv[i]+strlen(argv[i]))
            {
                return false;
            }
        }
        else
        {
            args.search=SEARCH_WORD;
            args.search_word=argv[i];
        }
    }
    return true;
}
int main(int argc, char** argv)
{
    arguments args;
    if(!parse_arguments(argc, argv, args))
    {
        std::cerr<<"Usage: "<<argv[0]
                 <<" dictionary [word|-r regex] [-t threads]\n";
        return 1;
    }
    try {
        dictionary dict(read_dictionary(args.dictionary_path.c_str()));
        search_dictionary sdict(search_dictionary_from_dictionary(dict));
        std::vector<std::string> formatted;
        if(args.search==SEARCH_ALL)
        {
            search_and_format(dict, dict, sdict, formatted);
        }
        else
        {
            dictionary from;
            if(args.search==SEARCH_WORD)
            {
                dictionary_add(from, args.search_word);
            }
            else if(args.search==SEARCH_REGEX)
            {
                std::regex regex(
                    args.search_word,
                    std::regex_constants::grep|std::regex_constants::icase
                );
                std::cout<<"Searching for following regex matches: "
                         <<std::endl;
                for(const dictionary_entry& entry: dict)
                {
                    if(std::regex_match(entry.word, regex))
                    {
                        dictionary_add(from, entry.word);
                        std::cout<<"\t"<<entry.word<<std::endl;
                    }
                }
            }
            else
            {
                assert(false);
            }
            search_and_format(from, dict, sdict, formatted);
        }
        for(const std::string& str: formatted)
        {
            std::cout<<str<<std::endl;
        }
        std::cout<<formatted.size()<<" found "<<std::endl;
    }
    catch(std::exception& e)
    {
        std::cerr<<e.what()<<std::endl;
        return 1;
    }
    return 0;
}
<|endoftext|>


<commit_before>// Petter Strandmark 2012.
//
// See http://doye.chem.ox.ac.uk/jon/structures/LJ/tables.150.html
// for best known minima for N <= 150.
//

#include <functional>
#include <iomanip>
#include <iostream>
#include <random>

#include <spii/auto_diff_term.h>
#include <spii/solver.h>

using namespace spii;

struct LennardJonesTerm
{
	template<typename R>
	R operator()(const R* const p1, const R* const p2) const
	{
		R dx = p1[0] - p2[0];
		R dy = p1[1] - p2[1];
		R dz = p1[2] - p2[2];
		R r2 = dx*dx + dy*dy + dz*dz;
		R r6  = r2*r2*r2;
		R r12 = r6*r6;
		return 1.0 / r12 - 2.0 / r6;
	}
};

int main()
{
	std::mt19937 prng(1);
	std::normal_distribution<double> normal;
	auto randn = std::bind(normal, prng);

	int N = -1;
	std::cout << "Enter N = ";
	std::cin >> N;

	Function potential;
	std::vector<Eigen::Vector3d> points(N);

	int n = int(std::ceil(std::pow(double(N), 1.0/3.0)));

	// Initial position is a cubic grid with random pertubations.
	for (int i = 0; i < N; ++i) {
		int x =  i % n;
		int y = (i / n) % n;
		int z = (i / n) / n;

		potential.add_variable(&points[i][0], 3);
		points[i][0] = x + 0.05 * randn();
		points[i][1] = y + 0.05 * randn();
		points[i][2] = z + 0.05 * randn();
	}

	for (int i = 0; i < N; ++i) {
		for (int j = i + 1; j < N; ++j) {
			potential.add_term(
				std::make_shared<AutoDiffTerm<LennardJonesTerm, 3, 3>>(),
				&points[i][0],
				&points[j][0]);
		}
	}

	LBFGSSolver solver;
	//solver.sparsity_mode = Solver::DENSE;  // For NewtonSolver.
	solver.maximum_iterations = 3000;
	SolverResults results;
	solver.solve(potential, &results);

	std::cerr << results;
	potential.print_timing_information(std::cout);

	std::cout << "Energy = " << std::setprecision(10) << potential.evaluate() << std::endl;
}<commit_msg>Change to new interface to add terms.<commit_after>// Petter Strandmark 2012.
//
// See http://doye.chem.ox.ac.uk/jon/structures/LJ/tables.150.html
// for best known minima for N <= 150.
//

#include <functional>
#include <iomanip>
#include <iostream>
#include <random>

#include <spii/auto_diff_term.h>
#include <spii/solver.h>

using namespace spii;

struct LennardJonesTerm
{
	template<typename R>
	R operator()(const R* const p1, const R* const p2) const
	{
		R dx = p1[0] - p2[0];
		R dy = p1[1] - p2[1];
		R dz = p1[2] - p2[2];
		R r2 = dx*dx + dy*dy + dz*dz;
		R r6  = r2*r2*r2;
		R r12 = r6*r6;
		return 1.0 / r12 - 2.0 / r6;
	}
};

int main()
{
	std::mt19937 prng(1);
	std::normal_distribution<double> normal;
	auto randn = std::bind(normal, prng);

	int N = -1;
	std::cout << "Enter N = ";
	std::cin >> N;

	Function potential;
	std::vector<Eigen::Vector3d> points(N);

	int n = int(std::ceil(std::pow(double(N), 1.0/3.0)));

	// Initial position is a cubic grid with random pertubations.
	for (int i = 0; i < N; ++i) {
		int x =  i % n;
		int y = (i / n) % n;
		int z = (i / n) / n;

		potential.add_variable(&points[i][0], 3);
		points[i][0] = x + 0.05 * randn();
		points[i][1] = y + 0.05 * randn();
		points[i][2] = z + 0.05 * randn();
	}

	for (int i = 0; i < N; ++i) {
		for (int j = i + 1; j < N; ++j) {
			potential.add_term<AutoDiffTerm<LennardJonesTerm, 3, 3>>(
				&points[i][0],
				&points[j][0]);
		}
	}

	LBFGSSolver solver;
	//solver.sparsity_mode = Solver::DENSE;  // For NewtonSolver.
	solver.maximum_iterations = 3000;
	SolverResults results;
	solver.solve(potential, &results);

	std::cerr << results;
	potential.print_timing_information(std::cout);

	std::cout << "Energy = " << std::setprecision(10) << potential.evaluate() << std::endl;
}<|endoftext|>