<commit_before>// This file is part of MLDB. Copyright 2015 Datacratic. All rights reserved.

/** metric_space.cc
    Jeremy Barnes, 25 April 2015
    Copyright (c) 2015 Datacratic Inc.  All rights reserved.

    Create a metric space.
*/

#include "metric_space.h"
#include "mldb/jml/stats/distribution_simd.h"
#include "ml/value_descriptions.h"
#include "mldb/types/basic_value_descriptions.h"
#include "mldb/types/distribution_description.h"
#include "mldb/http/http_exception.h"
#include "mldb/base/exc_assert.h"

using namespace std;

namespace Datacratic {
namespace MLDB {

DEFINE_ENUM_DESCRIPTION(MetricSpace);

MetricSpaceDescription::
MetricSpaceDescription()
{
    addValue("none", METRIC_NONE, "No metric is chosen.  This will cause an error.");
    addValue("euclidean", METRIC_EUCLIDEAN, "Use Euclidian distance for metric. "
             "This is a good choice for geometric embeddings like the t-SNE "
             "algorithm.");
    addValue("cosine", METRIC_COSINE, "Use cosine distance for metric.  This is "
             "a good choice for normalized and high-dimensional embeddings like the SVD)");
}

/*****************************************************************************/
/* DISTANCE METRIC                                                           */
/*****************************************************************************/

DistanceMetric *
DistanceMetric::
create(MetricSpace space)
{
    switch (space) {
    case METRIC_NONE:
        throw HttpReturnException(400, "No metric space was specified");
    case METRIC_EUCLIDEAN:
        return new EuclideanDistanceMetric();
    case METRIC_COSINE:
        return new CosineDistanceMetric();
    default:
        throw HttpReturnException(400, "Unknown distance metric space "
                                  + to_string(space));
    }
}

/*****************************************************************************/
/* EUCLIDEAN DISTANCE METRIC                                                 */
/*****************************************************************************/

void
EuclideanDistanceMetric::
addRow(int rowNum, const ML::distribution<float> & coords)
{
    //cerr << "addRow " << rowNum << endl;
    ExcAssertEqual(rowNum, sum_dist.size());
    sum_dist.push_back(coords.dotprod(coords));
    ExcAssert(isfinite(sum_dist.back()));
}

float
EuclideanDistanceMetric::
calc(const ML::distribution<float> & coords1,
     const ML::distribution<float> & coords2)
{
    return (coords2 - coords1).two_norm();
}

float
EuclideanDistanceMetric::
dist(int rowNum1, int rowNum2,
     const ML::distribution<float> & coords1,
     const ML::distribution<float> & coords2) const
{
    ExcAssertEqual(coords1.size(), coords2.size());

    if (rowNum1 == -1 || rowNum2 == -1) {
        return calc(coords1, coords2);
    }

    // Make sure dist(x,y) == dist(y,x) irrespective of rounding
    if (rowNum2 < rowNum1)
        return dist(rowNum2, rowNum1, coords2, coords1);

    // Make sure dist(x,x) == 0 irrespective of rounding
    if (rowNum1 == rowNum2)
        return 0.0;

    /*  Given two points x and y, whose coordinates are coords[x] and
        coords[y], this will calculate the euclidian distance
        ||x - y|| = sqrt(sum_i (x[i] - y[i])^2)
                  = sqrt(sum_i (x[i]^2 + y[i]^2 - 2 x[i]y[i]) )
                  = sqrt(sum_i x[i]^2 + sum_i y[i]^2 - 2 sum x[i]y[i])
                  = sqrt(||x||^2 + ||y||^2 - 2 x . y)

        Must satisfy the triangle inequality, so the sqrt is important.  We
        also take pains to ensure that dist(x,y) === dist(y,x) *exactly*,
        and that dist(x,x) == 0.
    */

    // Use the optimized version, since we know the sum
    float dpResult = -2.0 * ML::SIMD::vec_dotprod_dp(&coords1[0],
                                                     &coords2[0],
                                                     coords1.size());
    ExcAssert(isfinite(dpResult));

    float distSquared = dpResult + sum_dist.at(rowNum1) + sum_dist.at(rowNum2);
    ExcAssert(isfinite(distSquared));

    // Deal with rounding errors
    if (distSquared < 0.0)
        distSquared = 0.0;

    return sqrtf(distSquared);
}

/*****************************************************************************/
/* COSINE DISTANCE METRIC                                                    */
/*****************************************************************************/

void
CosineDistanceMetric::
addRow(int rowNum, const ML::distribution<float> & coords)
{
    ExcAssertEqual(rowNum, two_norm_recip.size());
    float twonorm = coords.two_norm();

    // If it's zero, we store the non-finite reciprocal, and the distance
    // it and any vector with a finite reciprocal is 1.

    if (twonorm == 0.0) {
        two_norm_recip.push_back(1.0 / 0.0);
        return;
        throw HttpReturnException(400, "Attempt to add zero magnitude vector to cosine distance",
                                  "coords", coords,
                                  "twoNorm", twonorm);
    }

    if (!isfinite(twonorm))
        throw HttpReturnException(400, "Attempt to add vector with non-finite two norm "
                                  "to cosine distance",
                                  "coords", coords,
                                  "twoNorm", twonorm);
    float recip = 1.0 / twonorm;
    if (!isfinite(recip))
        throw HttpReturnException(400, "Attempt to add vector with non-finite two norm reciprocal "
                                  "to cosine distance",
                                  "coords", coords,
                                  "twoNorm", twonorm,
                                  "recip", recip);

    two_norm_recip.push_back(recip);
}

float
CosineDistanceMetric::
calc(const ML::distribution<float> & coords1,
     const ML::distribution<float> & coords2)
{
    if ((coords1 == coords2).all())
        return 0.0;

    double norm1 = coords1.two_norm();
    double norm2 = coords2.two_norm();

    if (norm1 == 0.0 && norm2 == 0.0) {
        return 0.0;
    }

    if (norm1 == 0.0 || norm2 == 0.0) {
        return 1.0;

        throw HttpReturnException(400, "Error: can't calculate cosine distance between "
                                  "zero length vectors",
                                  "vec1", coords1,
                                  "vec2", coords2,
                                  "vec1norm", norm1,
                                  "vec2norm", norm2);
    }

    return std::max(1 - (coords1.dotprod(coords2) / (norm1 * norm2)), 0.0);
}

float
CosineDistanceMetric::
dist(int rowNum1, int rowNum2,
     const ML::distribution<float> & coords1,
     const ML::distribution<float> & coords2) const
{
    ExcAssertEqual(coords1.size(), coords2.size());

    if (rowNum1 == -1 || rowNum2 == -1) {
        return calc(coords1, coords2);
    }

    // Make sure dist(x,y) == dist(y,x) irrespective of rounding
    if (rowNum2 < rowNum1)
        return dist(rowNum2, rowNum1, coords2, coords1);

    // Make sure dist(x,x) == 0 irrespective of rounding
    if (rowNum1 == rowNum2)
        return 0.0;

    if (!isfinite(two_norm_recip.at(rowNum1))
        && !isfinite(two_norm_recip.at(rowNum2))) {
        return 0.0;
    }
    if (!isfinite(two_norm_recip.at(rowNum1))
        || !isfinite(two_norm_recip.at(rowNum2))) {
        return 1.0;
    }

    float result = 1.0 - coords1.dotprod(coords2) * two_norm_recip.at(rowNum1) * two_norm_recip.at(rowNum2);
    if (result < 0.0) {
        result = 0.0;
#if 0
        cerr << "rowNum1 = " << rowNum1 << endl;
        cerr << "rowNum2 = " << rowNum2 << endl;
        cerr << "coords1 = " << coords1 << endl;
        cerr << "coords2 = " << coords2 << endl;
        cerr << "result = " << result << endl;
#endif
    }

    ExcAssert(isfinite(result));
    ExcAssertGreaterEqual(result, 0.0);

    return result;
}

} // namespace Datacratic
} // namespace MLDB

<commit_msg>[Trivial] typo in description of cosine distance<commit_after>// This file is part of MLDB. Copyright 2015 Datacratic. All rights reserved.

/** metric_space.cc
    Jeremy Barnes, 25 April 2015
    Copyright (c) 2015 Datacratic Inc.  All rights reserved.

    Create a metric space.
*/

#include "metric_space.h"
#include "mldb/jml/stats/distribution_simd.h"
#include "ml/value_descriptions.h"
#include "mldb/types/basic_value_descriptions.h"
#include "mldb/types/distribution_description.h"
#include "mldb/http/http_exception.h"
#include "mldb/base/exc_assert.h"

using namespace std;

namespace Datacratic {
namespace MLDB {

DEFINE_ENUM_DESCRIPTION(MetricSpace);

MetricSpaceDescription::
MetricSpaceDescription()
{
    addValue("none", METRIC_NONE, "No metric is chosen.  This will cause an error.");
    addValue("euclidean", METRIC_EUCLIDEAN, "Use Euclidian distance for metric. "
             "This is a good choice for geometric embeddings like the t-SNE "
             "algorithm.");
    addValue("cosine", METRIC_COSINE, "Use cosine distance for metric.  This is "
             "a good choice for normalized and high-dimensional embeddings like the SVD.");
}

/*****************************************************************************/
/* DISTANCE METRIC                                                           */
/*****************************************************************************/

DistanceMetric *
DistanceMetric::
create(MetricSpace space)
{
    switch (space) {
    case METRIC_NONE:
        throw HttpReturnException(400, "No metric space was specified");
    case METRIC_EUCLIDEAN:
        return new EuclideanDistanceMetric();
    case METRIC_COSINE:
        return new CosineDistanceMetric();
    default:
        throw HttpReturnException(400, "Unknown distance metric space "
                                  + to_string(space));
    }
}

/*****************************************************************************/
/* EUCLIDEAN DISTANCE METRIC                                                 */
/*****************************************************************************/

void
EuclideanDistanceMetric::
addRow(int rowNum, const ML::distribution<float> & coords)
{
    //cerr << "addRow " << rowNum << endl;
    ExcAssertEqual(rowNum, sum_dist.size());
    sum_dist.push_back(coords.dotprod(coords));
    ExcAssert(isfinite(sum_dist.back()));
}

float
EuclideanDistanceMetric::
calc(const ML::distribution<float> & coords1,
     const ML::distribution<float> & coords2)
{
    return (coords2 - coords1).two_norm();
}

float
EuclideanDistanceMetric::
dist(int rowNum1, int rowNum2,
     const ML::distribution<float> & coords1,
     const ML::distribution<float> & coords2) const
{
    ExcAssertEqual(coords1.size(), coords2.size());

    if (rowNum1 == -1 || rowNum2 == -1) {
        return calc(coords1, coords2);
    }

    // Make sure dist(x,y) == dist(y,x) irrespective of rounding
    if (rowNum2 < rowNum1)
        return dist(rowNum2, rowNum1, coords2, coords1);

    // Make sure dist(x,x) == 0 irrespective of rounding
    if (rowNum1 == rowNum2)
        return 0.0;

    /*  Given two points x and y, whose coordinates are coords[x] and
        coords[y], this will calculate the euclidian distance
        ||x - y|| = sqrt(sum_i (x[i] - y[i])^2)
                  = sqrt(sum_i (x[i]^2 + y[i]^2 - 2 x[i]y[i]) )
                  = sqrt(sum_i x[i]^2 + sum_i y[i]^2 - 2 sum x[i]y[i])
                  = sqrt(||x||^2 + ||y||^2 - 2 x . y)

        Must satisfy the triangle inequality, so the sqrt is important.  We
        also take pains to ensure that dist(x,y) === dist(y,x) *exactly*,
        and that dist(x,x) == 0.
    */

    // Use the optimized version, since we know the sum
    float dpResult = -2.0 * ML::SIMD::vec_dotprod_dp(&coords1[0],
                                                     &coords2[0],
                                                     coords1.size());
    ExcAssert(isfinite(dpResult));

    float distSquared = dpResult + sum_dist.at(rowNum1) + sum_dist.at(rowNum2);
    ExcAssert(isfinite(distSquared));

    // Deal with rounding errors
    if (distSquared < 0.0)
        distSquared = 0.0;

    return sqrtf(distSquared);
}

/*****************************************************************************/
/* COSINE DISTANCE METRIC                                                    */
/*****************************************************************************/

void
CosineDistanceMetric::
addRow(int rowNum, const ML::distribution<float> & coords)
{
    ExcAssertEqual(rowNum, two_norm_recip.size());
    float twonorm = coords.two_norm();

    // If it's zero, we store the non-finite reciprocal, and the distance
    // it and any vector with a finite reciprocal is 1.

    if (twonorm == 0.0) {
        two_norm_recip.push_back(1.0 / 0.0);
        return;
        throw HttpReturnException(400, "Attempt to add zero magnitude vector to cosine distance",
                                  "coords", coords,
                                  "twoNorm", twonorm);
    }

    if (!isfinite(twonorm))
        throw HttpReturnException(400, "Attempt to add vector with non-finite two norm "
                                  "to cosine distance",
                                  "coords", coords,
                                  "twoNorm", twonorm);
    float recip = 1.0 / twonorm;
    if (!isfinite(recip))
        throw HttpReturnException(400, "Attempt to add vector with non-finite two norm reciprocal "
                                  "to cosine distance",
                                  "coords", coords,
                                  "twoNorm", twonorm,
                                  "recip", recip);

    two_norm_recip.push_back(recip);
}

float
CosineDistanceMetric::
calc(const ML::distribution<float> & coords1,
     const ML::distribution<float> & coords2)
{
    if ((coords1 == coords2).all())
        return 0.0;

    double norm1 = coords1.two_norm();
    double norm2 = coords2.two_norm();

    if (norm1 == 0.0 && norm2 == 0.0) {
        return 0.0;
    }

    if (norm1 == 0.0 || norm2 == 0.0) {
        return 1.0;

        throw HttpReturnException(400, "Error: can't calculate cosine distance between "
                                  "zero length vectors",
                                  "vec1", coords1,
                                  "vec2", coords2,
                                  "vec1norm", norm1,
                                  "vec2norm", norm2);
    }

    return std::max(1 - (coords1.dotprod(coords2) / (norm1 * norm2)), 0.0);
}

float
CosineDistanceMetric::
dist(int rowNum1, int rowNum2,
     const ML::distribution<float> & coords1,
     const ML::distribution<float> & coords2) const
{
    ExcAssertEqual(coords1.size(), coords2.size());

    if (rowNum1 == -1 || rowNum2 == -1) {
        return calc(coords1, coords2);
    }

    // Make sure dist(x,y) == dist(y,x) irrespective of rounding
    if (rowNum2 < rowNum1)
        return dist(rowNum2, rowNum1, coords2, coords1);

    // Make sure dist(x,x) == 0 irrespective of rounding
    if (rowNum1 == rowNum2)
        return 0.0;

    if (!isfinite(two_norm_recip.at(rowNum1))
        && !isfinite(two_norm_recip.at(rowNum2))) {
        return 0.0;
    }
    if (!isfinite(two_norm_recip.at(rowNum1))
        || !isfinite(two_norm_recip.at(rowNum2))) {
        return 1.0;
    }

    float result = 1.0 - coords1.dotprod(coords2) * two_norm_recip.at(rowNum1) * two_norm_recip.at(rowNum2);
    if (result < 0.0) {
        result = 0.0;
#if 0
        cerr << "rowNum1 = " << rowNum1 << endl;
        cerr << "rowNum2 = " << rowNum2 << endl;
        cerr << "coords1 = " << coords1 << endl;
        cerr << "coords2 = " << coords2 << endl;
        cerr << "result = " << result << endl;
#endif
    }

    ExcAssert(isfinite(result));
    ExcAssertGreaterEqual(result, 0.0);

    return result;
}

} // namespace Datacratic
} // namespace MLDB

<|endoftext|>


<commit_before>#define BOOST_TEST_MODULE test_math
#include <boost/test/included/unit_test.hpp>

#include "../defines.h"
#include "../math/size.h"

USING_NAMESPACE(sway)

BOOST_AUTO_TEST_SUITE(test_suite_size)
BOOST_AUTO_TEST_CASE(test_size)
{
	math::TSize<s32> size (1, 2);
    BOOST_CHECK_EQUAL(size.getW(), 1);
	BOOST_CHECK_EQUAL(size.getH(), 2);
	BOOST_CHECK_EQUAL(size, math::TSize<s32>(1, 2));
}
BOOST_AUTO_TEST_SUITE_END()<commit_msg>Добавлено несколько тестов<commit_after>#define BOOST_TEST_MODULE MathTestModule
#include <boost/test/included/unit_test.hpp>

#include "../defines.h"
#include "../math/size.h"

USING_NAMESPACE(sway)

BOOST_AUTO_TEST_SUITE(TSizeTestSuite)

/*!
 * Убеждаемся, что конструктор по умолчанию приводит все компоненты к нулю.
 */
BOOST_AUTO_TEST_CASE(DefaultConstructor)
{
	const math::TSize<s32> size;

	BOOST_CHECK_EQUAL(size.getW(), 0);
	BOOST_CHECK_EQUAL(size.getH(), 0);
}

/*!
 * Убеждаемся, что конструктор устанавливает все значения компонентов в те,
 * которые были заданы.
 */
BOOST_AUTO_TEST_CASE(ComponentConstructor)
{
	const s32 w = 1, h = 2;
	const math::TSize<s32> size(w, h);

	BOOST_CHECK_EQUAL(size.getW(), w);
	BOOST_CHECK_EQUAL(size.getH(), h);
}

BOOST_AUTO_TEST_CASE(ScalarConstructor)
{
	const s32 w = 1, h = 2;
	const math::TSize<s32> size(math::TSize<s32>(w, h));

	BOOST_CHECK_EQUAL(size.getW(), w);
	BOOST_CHECK_EQUAL(size.getH(), h);
}

/*!
 * Тест для оператора равенства.
 */
BOOST_AUTO_TEST_CASE(EqualityOperator1)
{
	const math::TSize<s32> size1(0, 0), size2(0, 0);

	BOOST_CHECK_EQUAL(size1 == size2, true);
}

/*!
 * Тест для оператора равенства.
 */
BOOST_AUTO_TEST_CASE(EqualityOperator2)
{
	const math::TSize<s32> size1(1, 0), size2(0, 0);

	BOOST_CHECK_EQUAL(size1 == size2, false);
}

/*!
 * Тест для оператора равенства.
 */
BOOST_AUTO_TEST_CASE(EqualityOperator3)
{
	const math::TSize<s32> size1(0, 0), size2(0, 2);

	BOOST_CHECK_EQUAL(size1 == size2, false);
}

BOOST_AUTO_TEST_CASE(EqualityOperator4)
{
	const math::TSize<s32> size (1, 2);

	BOOST_CHECK_EQUAL(size, math::TSize<s32>(1, 2));
}

/*!
 * Тест для оператора неравенства.
 */
BOOST_AUTO_TEST_CASE(NonEqualityOperator1)
{
	const math::TSize<s32> size1(1, 2), size2(1, 2);

	BOOST_CHECK_EQUAL(size1 != size2, false);
}

/*!
 * Тест для оператора неравенства.
 */
BOOST_AUTO_TEST_CASE(NonEqualityOperator2)
{
	const math::TSize<s32> size1(1, 2), size2(0, 2);

	BOOST_CHECK_EQUAL(size1 != size2, true);
}

/*!
 * Тест для оператора неравенства.
 */
BOOST_AUTO_TEST_CASE(NonEqualityOperator3)
{
	const math::TSize<s32> size1(1, 2), size2(1, 0);

	BOOST_CHECK_EQUAL(size1 != size2, true);
}

BOOST_AUTO_TEST_SUITE_END()<|endoftext|>


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

#define BOOST_TEST_MODULE table_test
#include <boost/test/unit_test.hpp>
#include <boost/lexical_cast.hpp>
#include <exception>
#include <iostream>
#include <votca/tools/table.h>

using namespace std;
using namespace votca::tools;

BOOST_AUTO_TEST_SUITE(table_test)

BOOST_AUTO_TEST_CASE(create_test) { Table tb; }

BOOST_AUTO_TEST_CASE(size_test) {
  Table tb;
  BOOST_CHECK_EQUAL(tb.size(), 0);
}

BOOST_AUTO_TEST_CASE(pushback_test) {
  Table tb;
  for (double x = 0; x < 10; ++x) {
    double y = 2 * x;
    tb.push_back(x, y);
  }
  BOOST_CHECK_EQUAL(tb.size(), 10);
}

BOOST_AUTO_TEST_CASE(xy_test) {
  Table tb;
  for (double x = 0; x < 10; ++x) {
    double y = 2 * x;
    tb.push_back(x, y);
  }

  auto x_v = tb.x();
  auto y_v = tb.y();
  for (int i = 0; i < 10; ++i) {
    int x = i;
    int y = 2 * x;
    BOOST_CHECK_EQUAL(static_cast<int>(x_v(i)),
                      static_cast<int>(tb.x(i)));
    BOOST_CHECK_EQUAL(static_cast<int>(y_v(i)),
                      static_cast<int>(tb.y(i)));
    BOOST_CHECK_EQUAL(x, static_cast<int>(tb.x(i)));
    BOOST_CHECK_EQUAL(y, static_cast<int>(tb.y(i)));
    BOOST_CHECK_EQUAL(x, static_cast<int>(x_v(i)));
    BOOST_CHECK_EQUAL(y, static_cast<int>(y_v(i)));
  }
}

BOOST_AUTO_TEST_CASE(getMinMax_test) {
  Table tb;
  for (double x = 0; x < 10; ++x) {
    double y = 2 * x;
    tb.push_back(x, y);
  }

  BOOST_CHECK_EQUAL(static_cast<int>(tb.getMinX()), 0);
  BOOST_CHECK_EQUAL(static_cast<int>(tb.getMaxX()), 9);
  BOOST_CHECK_EQUAL(static_cast<int>(tb.getMinY()), 0);
  BOOST_CHECK_EQUAL(static_cast<int>(tb.getMaxY()), 18);
}

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

#define BOOST_TEST_MODULE table_test
#include <boost/test/unit_test.hpp>
#include <boost/lexical_cast.hpp>
#include <exception>
#include <iostream>
#include <votca/tools/table.h>

using namespace std;
using namespace votca::tools;

BOOST_AUTO_TEST_SUITE(table_test)

BOOST_AUTO_TEST_CASE(create_test) {
  Table tb;
  Table tb2(tb);
}

BOOST_AUTO_TEST_CASE(size_test) {
  Table tb;
  BOOST_CHECK_EQUAL(tb.size(), 0);
}

BOOST_AUTO_TEST_CASE(pushback_test) {
  Table tb;
  for (double x = 0; x < 10; ++x) {
    double y = 2 * x;
    tb.push_back(x, y);
  }
  BOOST_CHECK_EQUAL(tb.size(), 10);
}

BOOST_AUTO_TEST_CASE(resize_test){
  Table tb;
  tb.resize(0);
  tb.resize(10);

  bool error_thrown = false;
  try {
    tb.resize(-5);
  }catch(...){
    error_thrown = true;
  }
  BOOST_CHECK(error_thrown);
}

BOOST_AUTO_TEST_CASE(xy_test) {
  Table tb;
  for (double x = 0; x < 10; ++x) {
    double y = 2 * x;
    tb.push_back(x, y);
  }

  auto x_v = tb.x();
  auto y_v = tb.y();
  for (int i = 0; i < 10; ++i) {
    int x = i;
    int y = 2 * x;
    BOOST_CHECK_EQUAL(static_cast<int>(x_v(i)),
                      static_cast<int>(tb.x(i)));
    BOOST_CHECK_EQUAL(static_cast<int>(y_v(i)),
                      static_cast<int>(tb.y(i)));
    BOOST_CHECK_EQUAL(x, static_cast<int>(tb.x(i)));
    BOOST_CHECK_EQUAL(y, static_cast<int>(tb.y(i)));
    BOOST_CHECK_EQUAL(x, static_cast<int>(x_v(i)));
    BOOST_CHECK_EQUAL(y, static_cast<int>(y_v(i)));
  }
}

BOOST_AUTO_TEST_CASE(getMinMax_test) {
  Table tb;
  for (double x = 0; x < 10; ++x) {
    double y = 2 * x;
    tb.push_back(x, y);
  }

  BOOST_CHECK_EQUAL(static_cast<int>(tb.getMinX()), 0);
  BOOST_CHECK_EQUAL(static_cast<int>(tb.getMaxX()), 9);
  BOOST_CHECK_EQUAL(static_cast<int>(tb.getMinY()), 0);
  BOOST_CHECK_EQUAL(static_cast<int>(tb.getMaxY()), 18);
}

BOOST_AUTO_TEST_CASE(generate_grid_spacing_test){
  Table tb;
  double min_v = 1.2;
  double max_v = 2.0;

  tb.GenerateGridSpacing(min_v,max_v,0.2);

  BOOST_CHECK_EQUAL(tb.size(),5);
  BOOST_CHECK_EQUAL(static_cast<int>(round(tb.getMinX()*10)),12);
  BOOST_CHECK_EQUAL(static_cast<int>(round(tb.getMaxX()*10)),20);
}

BOOST_AUTO_TEST_SUITE_END()
<|endoftext|>


<commit_before>#include "Textbox.h"
#include <cstring>
#include <cstdlib>
#include "utility/misc.h"
#include "input/Input.h"

using WalrusRPG::MAGIC_TOKEN;
using WalrusRPG::COMMAND_LEGNTH;
using WalrusRPG::Textbox;
using WalrusRPG::TextboxState;
using WalrusRPG::TextboxChar;
using WalrusRPG::Graphics::Font;
using WalrusRPG::Graphics::CharacterParameters;
using WalrusRPG::Graphics::Pixel;
using WalrusRPG::Input::Key;
using WalrusRPG::Utils::Rect;

namespace
{
    /**
     * This is a replacment of strlen to allow skipping tokens as they might
     * contain null-terminating characters, making strlen stop earlier than
     * planned.
     */
    signed strlen_tokens(const char *str)
    {
        signed len = 0;
        for (; str[len]; ++len)
        {
            if (str[len] == MAGIC_TOKEN)
                len += COMMAND_LEGNTH;
        }
        return len;
    }
}

Textbox::Textbox(Rect dimensions, Font fnt)
    : fnt(fnt), buffer(0), buffer_index(-1), global_string_offset(0),
      current_color(0, 0, 0), letter_wait(0), letter_wait_cooldown(10),
      dimensions(dimensions), state(Waiting)
{
}

Textbox::Textbox(Font fnt) : Textbox({4, 4, 220, 32}, fnt)
{
}

Textbox::~Textbox()
{
}

void Textbox::set_text(char *new_text)
{
    // Clearing the state variables.
    letter_wait = 0;
    letter_wait_cooldown = 10;
    buffer_index = -1;
    global_string_offset = 0;
    nb_line_to_update = 0;
    for (unsigned i = 0; i < nb_lines; ++i)
    {
        line_nb_characters[i] = 0;
        line_widths[i] = 0;
    }

    buffer.clear();
    // Parsing the passed string into a token list.
    // TODO?: Convert the vector into a dynamically allocated array?
    for (signed i = 0; i < strlen_tokens(new_text); ++i)
    {
        TextboxChar t;
        if (new_text[i] == MAGIC_TOKEN)
        {
            t.c = MAGIC_TOKEN;
            t.routine = new_text[i + 1];
            t.arg1 = new_text[i + 2];
            t.arg2 = new_text[i + 3];
            t.arg3 = new_text[i + 4];
            i += COMMAND_LEGNTH;
        }
        else
        {
            t.c = new_text[i];
            t.routine = 0;
            t.arg1 = 0;
            t.arg2 = 0;
            t.arg3 = 0;
        }
        buffer.push_back(t);
    }
    state = Updating;
}

/**
 * Makes the text box advance of one or more characters/tokens.
 */
void Textbox::add_letter(unsigned nb_letters)
{
    if (state != Updating || buffer.size() <= 0)
        return;

    // Mmh, you who enters here, try to forget how the core logic is programmed.
    // Myself don't have a frigging clue on how to clean it but it *works*.
    // If you ever clean it, I'll be eternally thankful :-°
    for (unsigned i = 0;
         (i < nb_letters) &&
         (buffer_index < 0 || buffer_index < static_cast<signed>(buffer.size()) - 1);
         ++i)
    {
        // As the index starts with -1, increment it before doing anything.
        ++buffer_index;
        // Parsing commands.
        if (buffer[buffer_index].c == MAGIC_TOKEN)
        {
            switch (buffer[buffer_index].routine)
            {
                // wait a bit
                case 0x81:
                    letter_wait = buffer[buffer_index].arg1;
                    break;
            }
            line_nb_characters[nb_line_to_update]++;
        }
        else
        {
            // The frigging static cast...
            CharacterParameters &p =
                fnt.chars[static_cast<signed>(buffer[buffer_index].c)];
            TextboxChar &t = buffer[buffer_index];
            // Manual line-return.
            if (t.c == '\n')
            {
                if (nb_line_to_update + 1 >= nb_lines)
                {
                    // No need to go back in the array. Just have to go forward.
                    line_nb_characters[nb_line_to_update]++;
                    state = Full;
                    return;
                }
                nb_line_to_update++;
            }
            // If adding the character would make the line too big for the text_box
            if (line_widths[nb_line_to_update] + p.dimensions.width + 1 >
                dimensions.width)
            {
                if (nb_line_to_update + 1 >= nb_lines)
                {
                    // Here to avoid getting to lose that character (or the last ones)
                    // We have to put the reading head one character backwards.
                    --buffer_index;
                    state = Full;
                    return;
                }
                nb_line_to_update++;
            }
            // Just adding the correct space width
            if (t.c == ' ')
                line_widths[nb_line_to_update] += fnt.space_width;
            else
                line_widths[nb_line_to_update] += p.dimensions.width + 1;
            // Putting the parsed character in the current text line.
            line_nb_characters[nb_line_to_update]++;
        }
    }
    // Check if the text box finished its work
    if (buffer_index >= static_cast<signed>(buffer.size() - 1))
    {
        state = Done;
    }
    // You prefer having to wait for characters, no?
    letter_wait = letter_wait_cooldown;
}

void Textbox::update(unsigned dt)
{
    // Small state machine.
    switch (state)
    {
        case Waiting:
            return;
            break;
        case Updating:
            // Time-based update.
            if ((buffer_index >= 0) &&
                (buffer_index >= static_cast<signed>(buffer.size())))
                return;
            letter_wait -= dt;
            if (letter_wait <= 0)
            {
                unsigned add = (-letter_wait) / letter_wait_cooldown + 1;
                add_letter(add);
            }
            break;
        case Full:
            // TODO?: Change the trigger (button) into something else (like a function)?
            if (key_pressed(Key::K_A))
            {
                for (unsigned i = 0; i < nb_lines - 1; ++i)
                    global_string_offset += line_nb_characters[i];

                line_widths[0] = line_widths[nb_lines - 1];
                line_nb_characters[0] = line_nb_characters[nb_lines - 1];

                for (unsigned i = 1; i < nb_lines; ++i)
                {
                    line_widths[i] = 0;
                    line_nb_characters[i] = 0;
                }
                nb_line_to_update = 1;
                state = Updating;
            }
        default:
            break;
    }
}

void Textbox::render(unsigned dt)
{
    UNUSED(dt);
    if (buffer_index < 0)
        return;
    // TODO : store the last character's color to correctly reapply it if a line return
    // happens?
    current_color = 0xFFFF;
    put_rectangle(dimensions, Graphics::Black);
    unsigned global_index = global_string_offset;
    for (unsigned l = 0; l < nb_lines; l++)
    {
        unsigned cur_x = dimensions.x;
        unsigned cur_y = dimensions.y + l * fnt.baseline;
        for (unsigned line_index = 0; line_index < line_nb_characters[l]; ++line_index)
        {
            TextboxChar b = buffer[global_index + line_index];
            char c = b.c;
            if (c == MAGIC_TOKEN)
            {
                switch (b.routine)
                {
                    // Change current color
                    case 0x01:
                        current_color = ((b.arg1 << 8) + b.arg2);
                        break;
                }
                continue;
            }
            fnt.draw(cur_x, cur_y, c, current_color);
            // *shrugs*
            if (c == '\n')
                continue;
            else if (c == ' ')
                cur_x += fnt.space_width;
            else
                cur_x += fnt.chars[static_cast<signed>(c)].dimensions.width + 1;
        }
        global_index += line_nb_characters[l];
    }
    // State indicator.
    Pixel indicator_color = Graphics::Black;
    if (state == Full)
        indicator_color = Graphics::Red;
    else if (state == Done)
        indicator_color = Graphics::Blue;

    if (indicator_color != Graphics::Black)
        put_rectangle({dimensions.x + static_cast<signed>(dimensions.width) - 3,
                       dimensions.y + static_cast<signed>(dimensions.height) - 3, 3, 3},
                      indicator_color);
}<commit_msg>Added a small detail.<commit_after>#include "Textbox.h"
#include <cstring>
#include <cstdlib>
#include "utility/misc.h"
#include "input/Input.h"

using WalrusRPG::MAGIC_TOKEN;
using WalrusRPG::COMMAND_LEGNTH;
using WalrusRPG::Textbox;
using WalrusRPG::TextboxState;
using WalrusRPG::TextboxChar;
using WalrusRPG::Graphics::Font;
using WalrusRPG::Graphics::CharacterParameters;
using WalrusRPG::Graphics::Pixel;
using WalrusRPG::Input::Key;
using WalrusRPG::Utils::Rect;

namespace
{
    /**
     * This is a replacment of strlen to allow skipping tokens as they might
     * contain null-terminating characters, making strlen stop earlier than
     * planned.
     */
    signed strlen_tokens(const char *str)
    {
        signed len = 0;
        for (; str[len]; ++len)
        {
            if (str[len] == MAGIC_TOKEN)
                len += COMMAND_LEGNTH;
        }
        return len;
    }
}

Textbox::Textbox(Rect dimensions, Font fnt)
    : fnt(fnt), buffer(0), buffer_index(-1), global_string_offset(0),
      current_color(0, 0, 0), letter_wait(0), letter_wait_cooldown(10),
      dimensions(dimensions), state(Waiting)
{
}

Textbox::Textbox(Font fnt) : Textbox({4, 4, 220, 32}, fnt)
{
}

Textbox::~Textbox()
{
}

void Textbox::set_text(char *new_text)
{
    // Clearing the state variables.
    letter_wait = 0;
    letter_wait_cooldown = 10;
    buffer_index = -1;
    global_string_offset = 0;
    nb_line_to_update = 0;
    for (unsigned i = 0; i < nb_lines; ++i)
    {
        line_nb_characters[i] = 0;
        line_widths[i] = 0;
    }

    buffer.clear();
    // Parsing the passed string into a token list.
    // TODO?: Convert the vector into a dynamically allocated array?
    for (signed i = 0; i < strlen_tokens(new_text); ++i)
    {
        TextboxChar t;
        if (new_text[i] == MAGIC_TOKEN)
        {
            t.c = MAGIC_TOKEN;
            t.routine = new_text[i + 1];
            t.arg1 = new_text[i + 2];
            t.arg2 = new_text[i + 3];
            t.arg3 = new_text[i + 4];
            i += COMMAND_LEGNTH;
        }
        else
        {
            t.c = new_text[i];
            t.routine = 0;
            t.arg1 = 0;
            t.arg2 = 0;
            t.arg3 = 0;
        }
        buffer.push_back(t);
    }
    state = Updating;
}

/**
 * Makes the text box advance of one or more characters/tokens.
 */
void Textbox::add_letter(unsigned nb_letters)
{
    if (state != Updating || buffer.size() <= 0)
        return;

    // Mmh, you who enters here, try to forget how the core logic is programmed.
    // Myself don't have a frigging clue on how to clean it but it *works*.
    // If you ever clean it, I'll be eternally thankful :-°
    // Actually, it works as it does right now, but the signedness is messy as hell
    // and changing it would most likely break it everywhere.
    for (unsigned i = 0;
         (i < nb_letters) &&
         (buffer_index < 0 || buffer_index < static_cast<signed>(buffer.size()) - 1);
         ++i)
    {
        // As the index starts with -1, increment it before doing anything.
        ++buffer_index;
        // Parsing commands.
        if (buffer[buffer_index].c == MAGIC_TOKEN)
        {
            switch (buffer[buffer_index].routine)
            {
                // wait a bit
                case 0x81:
                    letter_wait = buffer[buffer_index].arg1;
                    break;
            }
            line_nb_characters[nb_line_to_update]++;
        }
        else
        {
            // The frigging static cast...
            CharacterParameters &p =
                fnt.chars[static_cast<signed>(buffer[buffer_index].c)];
            TextboxChar &t = buffer[buffer_index];
            // Manual line-return.
            if (t.c == '\n')
            {
                if (nb_line_to_update + 1 >= nb_lines)
                {
                    // No need to go back in the array. Just have to go forward.
                    line_nb_characters[nb_line_to_update]++;
                    state = Full;
                    return;
                }
                nb_line_to_update++;
            }
            // If adding the character would make the line too big for the text_box
            if (line_widths[nb_line_to_update] + p.dimensions.width + 1 >
                dimensions.width)
            {
                if (nb_line_to_update + 1 >= nb_lines)
                {
                    // Here to avoid getting to lose that character (or the last ones)
                    // We have to put the reading head one character backwards.
                    --buffer_index;
                    state = Full;
                    return;
                }
                nb_line_to_update++;
            }
            // Just adding the correct space width
            if (t.c == ' ')
                line_widths[nb_line_to_update] += fnt.space_width;
            else
                line_widths[nb_line_to_update] += p.dimensions.width + 1;
            // Putting the parsed character in the current text line.
            line_nb_characters[nb_line_to_update]++;
        }
    }
    // Check if the text box finished its work
    if (buffer_index >= static_cast<signed>(buffer.size() - 1))
    {
        state = Done;
    }
    // You prefer having to wait for characters, no?
    letter_wait = letter_wait_cooldown;
}

void Textbox::update(unsigned dt)
{
    // Small state machine.
    switch (state)
    {
        case Waiting:
            return;
            break;
        case Updating:
            // Time-based update.
            if ((buffer_index >= 0) &&
                (buffer_index >= static_cast<signed>(buffer.size())))
                return;
            letter_wait -= dt;
            if (letter_wait <= 0)
            {
                unsigned add = (-letter_wait) / letter_wait_cooldown + 1;
                add_letter(add);
            }
            break;
        case Full:
            // TODO?: Change the trigger (button) into something else (like a function)?
            if (key_pressed(Key::K_A))
            {
                for (unsigned i = 0; i < nb_lines - 1; ++i)
                    global_string_offset += line_nb_characters[i];

                line_widths[0] = line_widths[nb_lines - 1];
                line_nb_characters[0] = line_nb_characters[nb_lines - 1];

                for (unsigned i = 1; i < nb_lines; ++i)
                {
                    line_widths[i] = 0;
                    line_nb_characters[i] = 0;
                }
                nb_line_to_update = 1;
                state = Updating;
            }
        default:
            break;
    }
}

void Textbox::render(unsigned dt)
{
    UNUSED(dt);
    if (buffer_index < 0)
        return;
    // TODO : store the last character's color to correctly reapply it if a line return
    // happens?
    current_color = 0xFFFF;
    put_rectangle(dimensions, Graphics::Black);
    unsigned global_index = global_string_offset;
    for (unsigned l = 0; l < nb_lines; l++)
    {
        unsigned cur_x = dimensions.x;
        unsigned cur_y = dimensions.y + l * fnt.baseline;
        for (unsigned line_index = 0; line_index < line_nb_characters[l]; ++line_index)
        {
            TextboxChar b = buffer[global_index + line_index];
            char c = b.c;
            if (c == MAGIC_TOKEN)
            {
                switch (b.routine)
                {
                    // Change current color
                    case 0x01:
                        current_color = ((b.arg1 << 8) + b.arg2);
                        break;
                }
                continue;
            }
            fnt.draw(cur_x, cur_y, c, current_color);
            // *shrugs*
            if (c == '\n')
                continue;
            else if (c == ' ')
                cur_x += fnt.space_width;
            else
                cur_x += fnt.chars[static_cast<signed>(c)].dimensions.width + 1;
        }
        global_index += line_nb_characters[l];
    }
    // State indicator.
    Pixel indicator_color = Graphics::Black;
    if (state == Full)
        indicator_color = Graphics::Red;
    else if (state == Done)
        indicator_color = Graphics::Blue;

    if (indicator_color != Graphics::Black)
        put_rectangle({dimensions.x + static_cast<signed>(dimensions.width) - 3,
                       dimensions.y + static_cast<signed>(dimensions.height) - 3, 3, 3},
                      indicator_color);
}<|endoftext|>


<commit_before>// $Id: ooiEnergy.C,v 1.11 2000/09/20 07:11:39 oliver Exp $

#include <BALL/SOLVATION/ooiEnergy.h>

#include <BALL/common.h>
#include <BALL/SYSTEM/path.h>
#include <BALL/COMMON/exception.h>
#include <BALL/DATATYPE/hashGrid.h>
#include <BALL/DATATYPE/string.h>
#include <BALL/DATATYPE/stringHashMap.h>
#include <BALL/STRUCTURE/geometricProperties.h>
#include <BALL/KERNEL/atom.h>
#include <BALL/KERNEL/bond.h>
#include <BALL/STRUCTURE/numericalSAS.h>
#include <BALL/FORMAT/parameters.h>
#include <BALL/FORMAT/parameterSection.h>
#include <BALL/MOLMEC/COMMON/typeRuleProcessor.h>

#define OOI_PARAMETER_FILENAME "solvation/Ooi.ini"

//#define BALL_DEBUG_OOI

using namespace std;

namespace BALL
{

	namespace OoiEnergy
	{
		bool is_initialized = false;

		// a hash map to convert names to Ooi types
		StringHashMap<Atom::Type> type_map;

		// a type rule assignment processor
		TypeRuleProcessor type_rule;

		// free energy is calculated as
		// dG = \sum_i g_i A_i
		// where A_i is the atomic solvent accesible surface
		// area. The atomic radii are taken from the vector radius below.
		vector<float> radius;
		vector<float> g;

		// read the parameter files for calculateOoiEnergy
		// and set up the basic data structures
		void init()
		{
			// extract the parameters from the file
			Path path;
			String filename = path.find(OOI_PARAMETER_FILENAME);
			if (filename == "")
			{
				filename = OOI_PARAMETER_FILENAME;
			}
			Parameters parameters(filename);

			ParameterSection parameter_section;
			if (!parameter_section.extractSection(parameters, "OoiParameters"))
			{
				Log.error() << "calculateOoiEnergy: cannot find section [OoiParameters] in file "
					<< parameters.getFilename() << "." << endl;
				return;
			}
			if (!parameter_section.hasVariable("g") || !parameter_section.hasVariable("radius"))
			{
				Log.error() << "OoiEnergy: section [OoiTypes] of file "
					<< parameters.getFilename() << " requires at least the columns 'g' and 'radius'." << endl;
				return;
			}

			ParameterSection type_section;
			if (!type_section.extractSection(parameters, "OoiTypes"))
			{
				Log.error() << "calculateOoiEnergy: cannot find section [OoiTypes] in file "
					<< parameters.getFilename() << "." << endl;
				return;
			}
			if (!type_section.hasVariable("type"))
			{
				Log.error() << "OoiEnergy: section [OoiTypes] of file "
					<< parameters.getFilename() << " does not contain a variable column 'type'." << endl;
				return;
			}

			// extract the parameters for each type
			//
			Position radius_column = parameter_section.getColumnIndex("radius");
			Position g_column = parameter_section.getColumnIndex("g");
			Index max_index = -1;
			Size i;
			for (i = 1; i <= parameter_section.getNumberOfKeys(); i++)
			{

				String index_str(parameter_section.getKey(i));
				Index index;
				try
				{
					index = index_str.trim().toInt();
					if (index < 0)
					{
						Log.error() << "calculateOoiEnergy: illegal atom type index: " << index << endl;
					}
					else
					{
						if (index > max_index)
						{
							max_index = index;
						}
					}
				}
				catch (Exception::InvalidFormat)
				{
					Log.error() << "calculateOoiEnergy: cannot convert to a number: " << index_str << endl;
				}
			}

			if (max_index < 0)
			{
				Log.error() << "calculateOoiEnergy: could not find any atom type in file "
									  << parameters.getFilename() << endl;
				return;
			}

			// resize the vectors to hold all indices
			radius.resize((Size)max_index + 1);
			g.resize((Size)max_index + 1);

			// and read all values from the parameter section
			for (i = 1; i <= parameter_section.getNumberOfKeys(); i++)
			{
				String index_str(parameter_section.getKey(i));
				Index index;
				try
				{
					index = index_str.trim().toInt();

					// we ignore illegal (negative) indices
					if (index >= 0)
					{
						radius[index] = parameter_section.getValue(i, radius_column).toFloat();
						g[index] = parameter_section.getValue(i, g_column).toFloat();
					}
				}
				catch (Exception::InvalidFormat)
				{
					Log.error() << "calculateOoiEnergy: cannot convert to a number: " << index_str << endl;
				}
			}

			// extract all known types by iterating over all keys
		  // and construct the hash map type_map
			Position type_column = type_section.getColumnIndex("type");
			for (i = 1; i <= type_section.getNumberOfKeys(); i++)
			{
				// retrieve the type and check for validity
				String index_str(type_section.getValue(i, type_column));
				Atom::Type type = index_str.trim().toInt();
				if (type >= (Atom::Type)radius.size())
				{
					Log.error() << "calculateOoiEnergy: illegal atom type: " << type << " while reading parameter file." << endl;
				}
				else
				{
					index_str = type_section.getValue(i, type_column);
					type_map.insert(type_section.getKey(i), (Atom::Type)index_str.trim().toInt());
				}
			}

			// set up the type rule processor
			// from the rules in the INI file
			type_rule.initialize(parameters.getParameterFile(), "TypeRules");

			// we're done with the initialization
			is_initialized = true;
		}
	}

	double calculateOoiEnergy(AtomContainer& atoms)
	{
		using namespace OoiEnergy;

		// read and interpret the parameters
		// this is only done the first time calculateOoiEnergy is called
		if (!is_initialized)
		{
			init();
			if (!is_initialized)
			{
				return 0;
			}
		}

		// assign radii and atom types for all atoms
		AtomIterator atom_it = atoms.beginAtom();
		for (; +atom_it; ++atom_it)
		{
			// construct correct name, <RESNAME>:<ATOMNAME>
			String atom_name = atom_it->getFullName();

			// get the atom type from hash table
			// first, try a direct match
			Atom::Type atom_type = -1;
			if (type_map.has(atom_name))
			{
				atom_type = type_map[atom_name];
			}
			else
			{
				atom_name = atom_it->getFullName(Atom::NO_VARIANT_EXTENSIONS);
				if (type_map.has(atom_name))
				{
					atom_type = type_map[atom_name];
				}
				else
				{
					// try wildcard match
					atom_name = "*:" + atom_it->getName();
					if (type_map.has(atom_name))
					{
						atom_type = type_map[atom_name];
					}
				}
			}

			// if the atom type could not be determined, complain
			if (atom_type < 0)
			{
				// try to apply the type rule processor
				atom_it->setType(-1);
				type_rule(*atom_it);
				atom_type = atom_it->getType();
			}

			if (atom_type < 0)
			{
				Log.warn() << "calculateOOIEnergy: did not find a suitable type for " << atom_it->getFullName() << endl;

				// ignore this atom....
				atom_it->setType(-1);
				atom_it->setRadius(0.0);
			}
			else
			{
				// assign type and radius
				atom_it->setType(atom_type);
				atom_it->setRadius(radius[atom_type]);
			}
		}

		// calculate the atomic SAS areas
		// atom_SAS_areas hashes the atom pointer to the
		// surface area (in Angstrom^2)
		HashMap<Atom*,float> atom_SAS_areas;
		calculateSASAtomAreas(atoms, atom_SAS_areas, 1.4, 1888);

		// iterate over all atoms and add up the energies
		float energy = 0.0;
		for (atom_it = atoms.beginAtom(); +atom_it; ++atom_it)
		{

			if (atom_SAS_areas.has(&*atom_it))
			{
				Atom::Type atom_type = atom_it->getType();
				if (atom_type >= 0)
				{
					// add the energy contribution of the atom
					float tmp = atom_SAS_areas[&*atom_it] * g[atom_type];
					energy += tmp;

					#ifdef BALL_DEBUG_OOI
						Log.info() << atom_it->getFullName() << " A = " << atom_SAS_areas[&*atom_it]
    									 << " E = " << tmp << " kJ/mol" << endl;
					#endif
				}
			}
		}

		// we're done.
		return energy;
	}

} // namespace BALL
<commit_msg>massive cosmetic changes fixed: now starting at the first key<commit_after>// $Id: ooiEnergy.C,v 1.12 2001/05/05 21:11:46 amoll Exp $

#include <BALL/SOLVATION/ooiEnergy.h>

#include <BALL/common.h>
#include <BALL/SYSTEM/path.h>
#include <BALL/COMMON/exception.h>
#include <BALL/DATATYPE/hashGrid.h>
#include <BALL/DATATYPE/string.h>
#include <BALL/DATATYPE/stringHashMap.h>
#include <BALL/STRUCTURE/geometricProperties.h>
#include <BALL/KERNEL/atom.h>
#include <BALL/KERNEL/bond.h>
#include <BALL/STRUCTURE/numericalSAS.h>
#include <BALL/FORMAT/parameters.h>
#include <BALL/FORMAT/parameterSection.h>
#include <BALL/MOLMEC/COMMON/typeRuleProcessor.h>

#define OOI_PARAMETER_FILENAME "solvation/Ooi.ini"

//#define BALL_DEBUG_OOI

using namespace std;

namespace BALL
{
	namespace OoiEnergy
	{
		bool is_initialized = false;

		// a hash map to convert names to Ooi types
		StringHashMap<Atom::Type> type_map;

		// a type rule assignment processor
		TypeRuleProcessor type_rule;

		// free energy is calculated as
		// dG = \sum_i g_i A_i
		// where A_i is the atomic solvent accesible surface
		// area. The atomic radii are taken from the vector radius below.
		vector<float> radius;
		vector<float> g;

		// read the parameter files for calculateOoiEnergy
		// and set up the basic data structures
		void init()
		{
			// extract the parameters from the file
			Path path;
			String filename = path.find(OOI_PARAMETER_FILENAME);
			if (filename == "")
			{
				filename = OOI_PARAMETER_FILENAME;
			}
			Parameters parameters(filename);

			ParameterSection parameter_section;
			if (!parameter_section.extractSection(parameters, "OoiParameters"))
			{
				Log.error() << "calculateOoiEnergy: cannot find section [OoiParameters] in file "
										<< parameters.getFilename() << "." << endl;
				return;
			}
			if (!parameter_section.hasVariable("g") || !parameter_section.hasVariable("radius"))
			{
				Log.error() << "OoiEnergy: section [OoiTypes] of file "
										<< parameters.getFilename()
										<< " requires at least the columns 'g' and 'radius'." << endl;
				return;
			}

			ParameterSection type_section;
			if (!type_section.extractSection(parameters, "OoiTypes"))
			{
				Log.error() << "calculateOoiEnergy: cannot find section [OoiTypes] in file "
										<< parameters.getFilename() << "." << endl;
				return;
			}
			if (!type_section.hasVariable("type"))
			{
				Log.error() << "OoiEnergy: section [OoiTypes] of file "
										<< parameters.getFilename()
										<< " does not contain a variable column 'type'." << endl;
				return;
			}

			// extract the parameters for each type
			Position radius_column = parameter_section.getColumnIndex("radius");
			Position g_column = parameter_section.getColumnIndex("g");
			Index max_index = -1;
			for (Size i = 0; i < parameter_section.getNumberOfKeys(); i++)
			{
				String index_str(parameter_section.getKey(i));
				Index index;
				try
				{
					index = index_str.trim().toInt();
				}
				catch (Exception::InvalidFormat)
				{
					Log.error() << "calculateOoiEnergy: cannot convert to a number: " << index_str << endl;
					continue;
				}

				if (index < 0)
				{
					Log.error() << "calculateOoiEnergy: illegal atom type index: " << index << endl;
					continue;
				}

				if (index > max_index)
				{
					max_index = index;
				}
			}

			if (max_index < 0)
			{
				Log.error() << "calculateOoiEnergy: could not find any atom type in file "
									  << parameters.getFilename() << endl;
				return;
			}

			// resize the vectors to hold all indices
			radius.resize((Size)max_index + 1);
			g.resize((Size)max_index + 1);

			// and read all values from the parameter section
			for (Size i = 0; i < parameter_section.getNumberOfKeys(); i++)
			{
				String index_str(parameter_section.getKey(i));
				Index index;
				try
				{
					index = index_str.trim().toInt();

					// we ignore illegal (negative) indices
					if (index >= 0)
					{
						radius[index] = parameter_section.getValue(i, radius_column).toFloat();
						g[index] = parameter_section.getValue(i, g_column).toFloat();
					}
				}
				catch (Exception::InvalidFormat)
				{
					Log.error() << "calculateOoiEnergy: cannot convert to a number: " << index_str << endl;
				}
			}

			// extract all known types by iterating over all keys
		  // and construct the hash map type_map
			Position type_column = type_section.getColumnIndex("type");
			for (Size i = 0; i < type_section.getNumberOfKeys(); i++)
			{
				// retrieve the type and check for validity
				String index_str(type_section.getValue(i, type_column));
				try
				{
					Atom::Type type = index_str.trim().toInt();
					if (type >= (Atom::Type)radius.size())
					{
						Log.error() << "calculateOoiEnergy: illegal atom type: " << type
												<< " while reading parameter file." << endl;
					}
					else
					{
						index_str = type_section.getValue(i, type_column);
						type_map.insert(type_section.getKey(i), (Atom::Type)index_str.trim().toInt());
					}
				}
				catch (Exception::InvalidFormat)
				{
					Log.error() << "calculateOoiEnergy: cannot convert to a number: " << index_str << endl;
				}
			}

			// set up the type rule processor
			// from the rules in the INI file
			type_rule.initialize(parameters.getParameterFile(), "TypeRules");

			// we're done with the initialization
			is_initialized = true;
		}
	}

	double calculateOoiEnergy(AtomContainer& atoms)
	{
		using namespace OoiEnergy;

		// read and interpret the parameters
		// this is only done the first time calculateOoiEnergy is called
		if (!is_initialized)
		{
			init();
			if (!is_initialized)
			{
				return 0;
			}
		}

		// assign radii and atom types for all atoms
		AtomIterator atom_it = atoms.beginAtom();
		for (; +atom_it; ++atom_it)
		{
			// construct correct name, <RESNAME>:<ATOMNAME>
			String atom_name = atom_it->getFullName();

			// get the atom type from hash table
			// first, try a direct match
			Atom::Type atom_type = -1;
			if (type_map.has(atom_name))
			{
				atom_type = type_map[atom_name];
			}
			else
			{
				atom_name = atom_it->getFullName(Atom::NO_VARIANT_EXTENSIONS);
				if (type_map.has(atom_name))
				{
					atom_type = type_map[atom_name];
				}
				else
				{
					// try wildcard match
					atom_name = "*:" + atom_it->getName();
					if (type_map.has(atom_name))
					{
						atom_type = type_map[atom_name];
					}
				}
			}

			// if the atom type could not be determined, complain
			if (atom_type < 0)
			{
				// try to apply the type rule processor
				atom_it->setType(-1);
				type_rule(*atom_it);
				atom_type = atom_it->getType();
			}

			if (atom_type < 0)
			{
				Log.warn() << "calculateOOIEnergy: did not find a suitable type for "
									 << atom_it->getFullName() << endl;

				// ignore this atom....
				atom_it->setType(-1);
				atom_it->setRadius(0.0);
			}
			else
			{
				// assign type and radius
				atom_it->setType(atom_type);
				atom_it->setRadius(radius[atom_type]);
			}
		}

		// calculate the atomic SAS areas
		// atom_SAS_areas hashes the atom pointer to the
		// surface area (in Angstrom^2)
		HashMap<Atom*,float> atom_SAS_areas;
		calculateSASAtomAreas(atoms, atom_SAS_areas, 1.4, 1888);

		// iterate over all atoms and add up the energies
		float energy = 0.0;
		for (atom_it = atoms.beginAtom(); +atom_it; ++atom_it)
		{
			if (atom_SAS_areas.has(&*atom_it))
			{
				Atom::Type atom_type = atom_it->getType();
				if (atom_type >= 0)
				{
					// add the energy contribution of the atom
					float tmp = atom_SAS_areas[&*atom_it] * g[atom_type];
					energy += tmp;

					#ifdef BALL_DEBUG_OOI
						Log.info() << atom_it->getFullName() << " A = " << atom_SAS_areas[&*atom_it]
    									 << " E = " << tmp << " kJ/mol" << endl;
					#endif
				}
			}
		}

		// we're done.
		return energy;
	}

} // namespace BALL
<|endoftext|>


<commit_before>/*
 @ Kingsley Chen
*/

#include "kbase\guid.h"

#include <objbase.h>

#include <array>
#include <cassert>

#include "kbase\strings\string_piece.h"
#include "kbase\strings\sys_string_encoding_conversions.h"

namespace kbase {

std::string GenerateGUID()
{
    GUID guid;
    auto rv = CoCreateGuid(&guid);
    assert(SUCCEEDED(rv));
    if (!SUCCEEDED(rv)) {
        return std::string();
    }

    const int kGUIDSize = 40;
    std::array<wchar_t, kGUIDSize> guid_str;
    int count_written = StringFromGUID2(guid, guid_str.data(), kGUIDSize);

    // Since GUID contains ASCII-only characters, it is safe to do this conversion.
    // Strips off { and }.
    return count_written ?
        WideToASCII(WStringPiece(guid_str.data() + 1, count_written - 3)) :
        std::string();
}

}   // namespace kbase<commit_msg>GUID: IsGUIDValid working on<commit_after>/*
 @ Kingsley Chen
*/

#include "kbase\guid.h"

#include <objbase.h>

#include <array>
#include <cassert>

#include "kbase\strings\string_piece.h"
#include "kbase\strings\sys_string_encoding_conversions.h"

namespace kbase {

std::string GenerateGUID()
{
    GUID guid;
    auto rv = CoCreateGuid(&guid);
    assert(SUCCEEDED(rv));
    if (!SUCCEEDED(rv)) {
        return std::string();
    }

    const int kGUIDStrSize = 40;
    std::array<wchar_t, kGUIDStrSize> guid_str;
    int count_written = StringFromGUID2(guid, guid_str.data(), kGUIDStrSize);

    // Since GUID contains ASCII-only characters, it is safe to do this conversion.
    // Strips off { and }.
    return count_written ?
        WideToASCII(WStringPiece(guid_str.data() + 1, count_written - 3)) :
        std::string();
}

bool IsGUIDValid(const std::string& guid)
{
    // Without { prepended and } appended.
    const size_t kGUIDSize = 36;
    if (guid.length() != kGUIDSize) {
        return false;
    }

    for (size_t i = 0; i < guid.length(); ++i) {
        if (i == 8 || i == 13 || i == 18 || i == 23) {

        }
    }
}

}   // namespace kbase<|endoftext|>


<commit_before>/******************************************************************************
 * Copyright 2018 The Apollo Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *****************************************************************************/

#include "modules/map/relative_map/navigation_lane.h"

#include <limits>

#include "modules/common/log.h"
#include "modules/common/math/math_utils.h"
#include "modules/common/util/util.h"
#include "modules/common/vehicle_state/vehicle_state_provider.h"

namespace apollo {
namespace relative_map {

using apollo::common::VehicleStateProvider;
using apollo::common::util::DistanceXY;
using apollo::perception::PerceptionObstacles;

NavigationLane::NavigationLane(const NavigationLaneConfig& config)
    : config_(config) {}

void NavigationLane::SetConfig(const NavigationLaneConfig& config) {
  config_ = config;
}

bool NavigationLane::Update(const PerceptionObstacles& perception_obstacles) {
  // udpate perception_obstacles_
  perception_obstacles_ = perception_obstacles;
  if (!perception_obstacles.has_lane_marker()) {
    AERROR << "No lane marker in perception_obstacles.";
    return false;
  }

  // update adc_state_ from VehicleStateProvider
  adc_state_ = VehicleStateProvider::instance()->vehicle_state();

  navigation_path_.Clear();
  auto* path = navigation_path_.mutable_path();
  const auto& lane_marker = perception_obstacles_.lane_marker();

  if (std::fmin(lane_marker.left_lane_marker().quality(),
                lane_marker.right_lane_marker().quality()) >
      config_.min_lane_marker_quality()) {
    ConvertLaneMarkerToPath(perception_obstacles_.lane_marker(), path);
  } else if (navigation_info_.navigation_path_size() > 0) {
    ConvertNavigationLineToPath(path);
  } else {
    AERROR << "Navigation Path is empty because neither lane markers nor "
              "navigation line are available.";
  }

  return true;
}

double NavigationLane::EvaluateCubicPolynomial(const double c0, const double c1,
                                               const double c2, const double c3,
                                               const double z) const {
  return c3 * std::pow(z, 3) + c2 * std::pow(z, 2) + c1 * z + c0;
}

void NavigationLane::ConvertNavigationLineToPath(common::Path* path) {
  CHECK_NOTNULL(path);
  if (navigation_info_.navigation_path_size() == 0 ||
      !navigation_info_.navigation_path(0).has_path() ||
      navigation_info_.navigation_path(0).path().path_point_size() == 0) {
    // path is empty
    return;
  }
  path->set_name("Path from navigation.");
  UpdateProjectionIndex();

  // TODO(All): support multiple navigation path
  // currently, only 1 navigation path is supported
  const auto& navigation_path = navigation_info_.navigation_path(0).path();
  int curr_project_index = last_project_index_;

  // offset between the current vehicle state and navigation line
  const double dx = -navigation_path.path_point(curr_project_index).x();
  const double dy = -navigation_path.path_point(curr_project_index).y();
  for (int i = curr_project_index; i < navigation_path.path_point_size(); ++i) {
    auto* point = path->add_path_point();
    point->CopyFrom(navigation_path.path_point(i));

    // shift to (0, 0)
    double emu_x = point->x() + dx;
    double emu_y = point->y() + dy;

    double flu_x = 0.0;
    double flu_y = 0.0;
    common::math::RotateAxis(adc_state_.heading(), emu_x, emu_y, &flu_x,
                             &flu_y);

    point->set_x(flu_x);
    point->set_y(flu_y);
    const double accumulated_s =
        navigation_path.path_point(i).s() -
        navigation_path.path_point(curr_project_index).s();
    point->set_s(accumulated_s);
  }

  // set left/right width invalid as no width info from navigation line
  left_width_ = -1.0;
  right_width_ = -1.0;
}

// project adc_state_ onto path
void NavigationLane::UpdateProjectionIndex() {
  // TODO(All): support multiple navigation path
  // currently, only 1 navigation path is supported
  const auto& path = navigation_info_.navigation_path(0).path();
  int index = 0;
  double min_d = std::numeric_limits<double>::max();
  for (int i = last_project_index_; i + 1 < path.path_point_size(); ++i) {
    const double d = DistanceXY(adc_state_, path.path_point(i));
    if (d < min_d) {
      min_d = d;
      index = i;
    }
    const double kMaxDistance = 50.0;
    if (d > kMaxDistance) {
      break;
    }
  }
  last_project_index_ = index;
}

void NavigationLane::ConvertLaneMarkerToPath(
    const perception::LaneMarkers& lane_marker, common::Path* path) {
  CHECK_NOTNULL(path);

  path->set_name("Path from lane markers.");
  const auto& left_lane = lane_marker.left_lane_marker();
  const auto& right_lane = lane_marker.right_lane_marker();

  const double unit_z = 1.0;
  if (left_lane.view_range() > right_lane.view_range()) {
    const double x_l_0 = EvaluateCubicPolynomial(
        left_lane.c0_position(), left_lane.c1_heading_angle(),
        left_lane.c2_curvature(), left_lane.c3_curvature_derivative(), 0.0);

    double accumulated_s = 0.0;
    for (double z = 0; z <= left_lane.view_range(); z += unit_z) {
      const double x_l = EvaluateCubicPolynomial(
          left_lane.c0_position(), left_lane.c1_heading_angle(),
          left_lane.c2_curvature(), left_lane.c3_curvature_derivative(), z);

      if (left_width_ < 0.0) {
        left_width_ = std::fabs(x_l);
      }
      if (right_width_ < 0.0) {
        right_width_ = left_width_;
      }

      double x1 = z;
      // TODO(All): use more precise method to shift y
      double y1 = std::fabs(x_l) - std::fabs(x_l_0);

      auto* point = path->add_path_point();
      point->set_x(x1);
      point->set_y(y1);
      point->set_s(accumulated_s);

      if (path->path_point_size() > 1) {
        auto& pre_point = path->path_point(path->path_point_size() - 2);
        accumulated_s += std::hypot(x1 - pre_point.x(), y1 - pre_point.y());
      }
    }
  } else {
    const double x_r_0 = EvaluateCubicPolynomial(
        right_lane.c0_position(), right_lane.c1_heading_angle(),
        right_lane.c2_curvature(), right_lane.c3_curvature_derivative(), 0.0);
    double accumulated_s = 0.0;
    for (double z = 0; z <= right_lane.view_range(); z += unit_z) {
      const double x_r = EvaluateCubicPolynomial(
          right_lane.c0_position(), right_lane.c1_heading_angle(),
          right_lane.c2_curvature(), right_lane.c3_curvature_derivative(), z);

      if (right_width_ < 0.0) {
        right_width_ = left_width_;
      }
      if (left_width_ < 0.0) {
        left_width_ = right_width_;
      }

      double x1 = z;
      // TODO(All): use more precise method to shift y
      double y1 = -std::fabs(x_r) + std::fabs(x_r_0);

      auto* point = path->add_path_point();
      point->set_x(x1);
      point->set_y(y1);
      point->set_s(accumulated_s);

      if (path->path_point_size() > 1) {
        auto& pre_point = path->path_point(path->path_point_size() - 2);
        accumulated_s += std::hypot(x1 - pre_point.x(), y1 - pre_point.y());
      }
    }
  }
}

}  // namespace relative_map
}  // namespace apollo
<commit_msg>Navigation: set max navigation lane length to 100m<commit_after>/******************************************************************************
 * Copyright 2018 The Apollo Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *****************************************************************************/

#include "modules/map/relative_map/navigation_lane.h"

#include <limits>

#include "modules/common/log.h"
#include "modules/common/math/math_utils.h"
#include "modules/common/util/util.h"
#include "modules/common/vehicle_state/vehicle_state_provider.h"

namespace apollo {
namespace relative_map {

using apollo::common::VehicleStateProvider;
using apollo::common::util::DistanceXY;
using apollo::perception::PerceptionObstacles;

NavigationLane::NavigationLane(const NavigationLaneConfig& config)
    : config_(config) {}

void NavigationLane::SetConfig(const NavigationLaneConfig& config) {
  config_ = config;
}

bool NavigationLane::Update(const PerceptionObstacles& perception_obstacles) {
  // udpate perception_obstacles_
  perception_obstacles_ = perception_obstacles;
  if (!perception_obstacles.has_lane_marker()) {
    AERROR << "No lane marker in perception_obstacles.";
    return false;
  }

  // update adc_state_ from VehicleStateProvider
  adc_state_ = VehicleStateProvider::instance()->vehicle_state();

  navigation_path_.Clear();
  auto* path = navigation_path_.mutable_path();
  const auto& lane_marker = perception_obstacles_.lane_marker();

  if (std::fmin(lane_marker.left_lane_marker().quality(),
                lane_marker.right_lane_marker().quality()) >
      config_.min_lane_marker_quality()) {
    ConvertLaneMarkerToPath(perception_obstacles_.lane_marker(), path);
  } else if (navigation_info_.navigation_path_size() > 0) {
    ConvertNavigationLineToPath(path);
  } else {
    AERROR << "Navigation Path is empty because neither lane markers nor "
              "navigation line are available.";
  }

  return true;
}

double NavigationLane::EvaluateCubicPolynomial(const double c0, const double c1,
                                               const double c2, const double c3,
                                               const double z) const {
  return c3 * std::pow(z, 3) + c2 * std::pow(z, 2) + c1 * z + c0;
}

void NavigationLane::ConvertNavigationLineToPath(common::Path* path) {
  CHECK_NOTNULL(path);
  if (navigation_info_.navigation_path_size() == 0 ||
      !navigation_info_.navigation_path(0).has_path() ||
      navigation_info_.navigation_path(0).path().path_point_size() == 0) {
    // path is empty
    return;
  }
  path->set_name("Path from navigation.");
  UpdateProjectionIndex();

  // TODO(All): support multiple navigation path
  // currently, only 1 navigation path is supported
  const auto& navigation_path = navigation_info_.navigation_path(0).path();
  int curr_project_index = last_project_index_;

  // offset between the current vehicle state and navigation line
  const double dx = -navigation_path.path_point(curr_project_index).x();
  const double dy = -navigation_path.path_point(curr_project_index).y();
  for (int i = curr_project_index; i < navigation_path.path_point_size(); ++i) {
    auto* point = path->add_path_point();
    point->CopyFrom(navigation_path.path_point(i));

    // shift to (0, 0)
    double emu_x = point->x() + dx;
    double emu_y = point->y() + dy;

    double flu_x = 0.0;
    double flu_y = 0.0;
    common::math::RotateAxis(adc_state_.heading(), emu_x, emu_y, &flu_x,
                             &flu_y);

    point->set_x(flu_x);
    point->set_y(flu_y);
    const double accumulated_s =
        navigation_path.path_point(i).s() -
        navigation_path.path_point(curr_project_index).s();
    point->set_s(accumulated_s);

    constexpr double kMaxAccumulatedS = 100.0;
    if (accumulated_s > kMaxAccumulatedS) {
      break;
    }
  }

  // set left/right width invalid as no width info from navigation line
  left_width_ = -1.0;
  right_width_ = -1.0;
}

// project adc_state_ onto path
void NavigationLane::UpdateProjectionIndex() {
  // TODO(All): support multiple navigation path
  // currently, only 1 navigation path is supported
  const auto& path = navigation_info_.navigation_path(0).path();
  int index = 0;
  double min_d = std::numeric_limits<double>::max();
  for (int i = last_project_index_; i + 1 < path.path_point_size(); ++i) {
    const double d = DistanceXY(adc_state_, path.path_point(i));
    if (d < min_d) {
      min_d = d;
      index = i;
    }
    const double kMaxDistance = 50.0;
    if (d > kMaxDistance) {
      break;
    }
  }
  last_project_index_ = index;
}

void NavigationLane::ConvertLaneMarkerToPath(
    const perception::LaneMarkers& lane_marker, common::Path* path) {
  CHECK_NOTNULL(path);

  path->set_name("Path from lane markers.");
  const auto& left_lane = lane_marker.left_lane_marker();
  const auto& right_lane = lane_marker.right_lane_marker();

  const double unit_z = 1.0;
  if (left_lane.view_range() > right_lane.view_range()) {
    const double x_l_0 = EvaluateCubicPolynomial(
        left_lane.c0_position(), left_lane.c1_heading_angle(),
        left_lane.c2_curvature(), left_lane.c3_curvature_derivative(), 0.0);

    double accumulated_s = 0.0;
    for (double z = 0; z <= left_lane.view_range(); z += unit_z) {
      const double x_l = EvaluateCubicPolynomial(
          left_lane.c0_position(), left_lane.c1_heading_angle(),
          left_lane.c2_curvature(), left_lane.c3_curvature_derivative(), z);

      if (left_width_ < 0.0) {
        left_width_ = std::fabs(x_l);
      }
      if (right_width_ < 0.0) {
        right_width_ = left_width_;
      }

      double x1 = z;
      // TODO(All): use more precise method to shift y
      double y1 = std::fabs(x_l) - std::fabs(x_l_0);

      auto* point = path->add_path_point();
      point->set_x(x1);
      point->set_y(y1);
      point->set_s(accumulated_s);

      if (path->path_point_size() > 1) {
        auto& pre_point = path->path_point(path->path_point_size() - 2);
        accumulated_s += std::hypot(x1 - pre_point.x(), y1 - pre_point.y());
      }
    }
  } else {
    const double x_r_0 = EvaluateCubicPolynomial(
        right_lane.c0_position(), right_lane.c1_heading_angle(),
        right_lane.c2_curvature(), right_lane.c3_curvature_derivative(), 0.0);
    double accumulated_s = 0.0;
    for (double z = 0; z <= right_lane.view_range(); z += unit_z) {
      const double x_r = EvaluateCubicPolynomial(
          right_lane.c0_position(), right_lane.c1_heading_angle(),
          right_lane.c2_curvature(), right_lane.c3_curvature_derivative(), z);

      if (right_width_ < 0.0) {
        right_width_ = left_width_;
      }
      if (left_width_ < 0.0) {
        left_width_ = right_width_;
      }

      double x1 = z;
      // TODO(All): use more precise method to shift y
      double y1 = -std::fabs(x_r) + std::fabs(x_r_0);

      auto* point = path->add_path_point();
      point->set_x(x1);
      point->set_y(y1);
      point->set_s(accumulated_s);

      if (path->path_point_size() > 1) {
        auto& pre_point = path->path_point(path->path_point_size() - 2);
        accumulated_s += std::hypot(x1 - pre_point.x(), y1 - pre_point.y());
      }
    }
  }
}

}  // namespace relative_map
}  // namespace apollo
<|endoftext|>


<commit_before>/******************************************************************************
 * Copyright 2018 The Apollo Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *****************************************************************************/

#include "modules/map/relative_map/navigation_lane.h"

#include "modules/common/log.h"
#include "modules/common/math/math_utils.h"
#include "modules/common/vehicle_state/vehicle_state_provider.h"

namespace apollo {
namespace relative_map {
namespace {

double GetDistance(const common::VehicleState& adc_state,
                   const common::PathPoint& p) {
  return std::hypot(adc_state.x() - p.x(), adc_state.y() - p.y());
}
}
using apollo::perception::PerceptionObstacles;
using apollo::common::VehicleStateProvider;

bool NavigationLane::Update(const PerceptionObstacles& perception_obstacles) {
  // udpate perception_obstacles_
  perception_obstacles_ = perception_obstacles;
  if (!perception_obstacles.has_lane_marker()) {
    AERROR << "No lane marker in perception_obstacles.";
    return false;
  }

  // update adc_state_ from VehicleStateProvider
  adc_state_ = VehicleStateProvider::instance()->vehicle_state();

  navigation_path_.Clear();
  auto* path = navigation_path_.mutable_path();
  const auto& lane_marker = perception_obstacles_.lane_marker();

  const double kQualityThreshold = 0.5;
  if (navigation_info_.navigation_path_size() > 0 &&
      std::fmax(lane_marker.left_lane_marker().quality(),
                lane_marker.right_lane_marker().quality()) <
          kQualityThreshold) {
    ConvertNavigationLineToPath();
  } else {
    ConvertLaneMarkerToPath(perception_obstacles_.lane_marker(), path);
  }
  return true;
}

double NavigationLane::EvaluateCubicPolynomial(const double c0, const double c1,
                                               const double c2, const double c3,
                                               const double z) const {
  return c3 * std::pow(z, 3) + c2 * std::pow(z, 2) + c1 * z + c0;
}

void NavigationLane::ConvertNavigationLineToPath() {
  if (navigation_info_.navigation_path_size() == 0 ||
      !navigation_info_.navigation_path(0).has_path() ||
      navigation_info_.navigation_path(0).path().path_point_size() == 0) {
    // path is empty
    return;
  }
  navigation_path_.Clear();
  auto* curr_path = navigation_path_.mutable_path();
  UpdateProjectionIndex();

  // TODO(All): support multiple navigation path
  // only support 1 navigation path
  const auto& path = navigation_info_.navigation_path(0).path();
  int curr_project_index = last_project_index_;
  for (int i = curr_project_index; i < path.path_point_size(); ++i) {
    auto* point = curr_path->add_path_point();
    point->CopyFrom(path.path_point(i));
    const double accumulated_s =
        path.path_point(i).s() - path.path_point(curr_project_index).s();
    point->set_s(accumulated_s);
  }
}

// project adc_state_ onto path
void NavigationLane::UpdateProjectionIndex() {
  // TODO(All): support multiple navigation path
  // only support 1 navigation path
  const auto& path = navigation_info_.navigation_path(0).path();
  int index = 0;
  double min_d = std::numeric_limits<double>::max();
  for (int i = last_project_index_; i + 1 < path.path_point_size(); ++i) {
    const double d = GetDistance(adc_state_, path.path_point(i));
    if (d < min_d) {
      min_d = d;
      index = i;
    }
    const double kMaxDistance = 50.0;
    if (d > kMaxDistance) {
      break;
    }
  }
  last_project_index_ = index;
}

void NavigationLane::ConvertLaneMarkerToPath(
    const perception::LaneMarkers& lane_marker, common::Path* path) {
  const auto& left_lane = lane_marker.left_lane_marker();
  const auto& right_lane = lane_marker.right_lane_marker();

  const double unit_z = 1.0;
  double accumulated_s = 0.0;
  for (double z = 0;
       z <= std::fmin(left_lane.view_range(), right_lane.view_range());
       z += unit_z) {
    const double x_l = EvaluateCubicPolynomial(
        left_lane.c0_position(), left_lane.c1_heading_angle(),
        left_lane.c2_curvature(), left_lane.c3_curvature_derivative(), z);
    const double x_r = EvaluateCubicPolynomial(
        right_lane.c0_position(), right_lane.c1_heading_angle(),
        right_lane.c2_curvature(), right_lane.c3_curvature_derivative(), z);

    double x1 = 0.0;
    double y1 = 0.0;
    // rotate from vehicle axis to x-y axis
    common::math::RotateAxis(-adc_state_.heading(), z, (x_l + x_r) / 2.0, &x1,
                             &y1);

    // shift to get point on x-y axis
    x1 += adc_state_.x();
    y1 += adc_state_.y();

    auto* point = path->add_path_point();
    point->set_x(x1);
    point->set_y(y1);
    point->set_s(accumulated_s);
    accumulated_s += std::hypot(x1, y1);
  }
}

}  // namespace relative_map
}  // namespace apollo
<commit_msg>Update navigation_lane.cc<commit_after>/******************************************************************************
 * Copyright 2018 The Apollo Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *****************************************************************************/

#include "modules/map/relative_map/navigation_lane.h"

#include <limits>

#include "modules/common/log.h"
#include "modules/common/math/math_utils.h"
#include "modules/common/vehicle_state/vehicle_state_provider.h"

namespace apollo {
namespace relative_map {
namespace {

double GetDistance(const common::VehicleState& adc_state,
                   const common::PathPoint& p) {
  return std::hypot(adc_state.x() - p.x(), adc_state.y() - p.y());
}
}
using apollo::perception::PerceptionObstacles;
using apollo::common::VehicleStateProvider;

bool NavigationLane::Update(const PerceptionObstacles& perception_obstacles) {
  // udpate perception_obstacles_
  perception_obstacles_ = perception_obstacles;
  if (!perception_obstacles.has_lane_marker()) {
    AERROR << "No lane marker in perception_obstacles.";
    return false;
  }

  // update adc_state_ from VehicleStateProvider
  adc_state_ = VehicleStateProvider::instance()->vehicle_state();

  navigation_path_.Clear();
  auto* path = navigation_path_.mutable_path();
  const auto& lane_marker = perception_obstacles_.lane_marker();

  const double kQualityThreshold = 0.5;
  if (navigation_info_.navigation_path_size() > 0 &&
      std::fmax(lane_marker.left_lane_marker().quality(),
                lane_marker.right_lane_marker().quality()) <
          kQualityThreshold) {
    ConvertNavigationLineToPath();
  } else {
    ConvertLaneMarkerToPath(perception_obstacles_.lane_marker(), path);
  }
  return true;
}

double NavigationLane::EvaluateCubicPolynomial(const double c0, const double c1,
                                               const double c2, const double c3,
                                               const double z) const {
  return c3 * std::pow(z, 3) + c2 * std::pow(z, 2) + c1 * z + c0;
}

void NavigationLane::ConvertNavigationLineToPath() {
  if (navigation_info_.navigation_path_size() == 0 ||
      !navigation_info_.navigation_path(0).has_path() ||
      navigation_info_.navigation_path(0).path().path_point_size() == 0) {
    // path is empty
    return;
  }
  navigation_path_.Clear();
  auto* curr_path = navigation_path_.mutable_path();
  UpdateProjectionIndex();

  // TODO(All): support multiple navigation path
  // only support 1 navigation path
  const auto& path = navigation_info_.navigation_path(0).path();
  int curr_project_index = last_project_index_;
  for (int i = curr_project_index; i < path.path_point_size(); ++i) {
    auto* point = curr_path->add_path_point();
    point->CopyFrom(path.path_point(i));
    const double accumulated_s =
        path.path_point(i).s() - path.path_point(curr_project_index).s();
    point->set_s(accumulated_s);
  }
}

// project adc_state_ onto path
void NavigationLane::UpdateProjectionIndex() {
  // TODO(All): support multiple navigation path
  // only support 1 navigation path
  const auto& path = navigation_info_.navigation_path(0).path();
  int index = 0;
  double min_d = std::numeric_limits<double>::max();
  for (int i = last_project_index_; i + 1 < path.path_point_size(); ++i) {
    const double d = GetDistance(adc_state_, path.path_point(i));
    if (d < min_d) {
      min_d = d;
      index = i;
    }
    const double kMaxDistance = 50.0;
    if (d > kMaxDistance) {
      break;
    }
  }
  last_project_index_ = index;
}

void NavigationLane::ConvertLaneMarkerToPath(
    const perception::LaneMarkers& lane_marker, common::Path* path) {
  const auto& left_lane = lane_marker.left_lane_marker();
  const auto& right_lane = lane_marker.right_lane_marker();

  const double unit_z = 1.0;
  double accumulated_s = 0.0;
  for (double z = 0;
       z <= std::fmin(left_lane.view_range(), right_lane.view_range());
       z += unit_z) {
    const double x_l = EvaluateCubicPolynomial(
        left_lane.c0_position(), left_lane.c1_heading_angle(),
        left_lane.c2_curvature(), left_lane.c3_curvature_derivative(), z);
    const double x_r = EvaluateCubicPolynomial(
        right_lane.c0_position(), right_lane.c1_heading_angle(),
        right_lane.c2_curvature(), right_lane.c3_curvature_derivative(), z);

    double x1 = 0.0;
    double y1 = 0.0;
    // rotate from vehicle axis to x-y axis
    common::math::RotateAxis(-adc_state_.heading(), z, (x_l + x_r) / 2.0, &x1,
                             &y1);

    // shift to get point on x-y axis
    x1 += adc_state_.x();
    y1 += adc_state_.y();

    auto* point = path->add_path_point();
    point->set_x(x1);
    point->set_y(y1);
    point->set_s(accumulated_s);
    accumulated_s += std::hypot(x1, y1);
  }
}

}  // namespace relative_map
}  // namespace apollo
<|endoftext|>


<commit_before>//===- lib/ReaderWriter/PECOFF/PECOFFLinkingContext.cpp -------------------===//
//
//                             The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "Atoms.h"
#include "GroupedSectionsPass.h"
#include "IdataPass.h"
#include "LinkerGeneratedSymbolFile.h"

#include "lld/Core/PassManager.h"
#include "lld/Passes/LayoutPass.h"
#include "lld/Passes/RoundTripNativePass.h"
#include "lld/Passes/RoundTripYAMLPass.h"
#include "lld/ReaderWriter/PECOFFLinkingContext.h"
#include "lld/ReaderWriter/Reader.h"
#include "lld/ReaderWriter/Simple.h"
#include "lld/ReaderWriter/Writer.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/Path.h"

#include <bitset>
#include <set>

namespace lld {

namespace {} // anonymous namespace

bool PECOFFLinkingContext::validateImpl(raw_ostream &diagnostics) {
  if (_stackReserve < _stackCommit) {
    diagnostics << "Invalid stack size: reserve size must be equal to or "
                << "greater than commit size, but got " << _stackCommit
                << " and " << _stackReserve << ".\n";
    return false;
  }

  if (_heapReserve < _heapCommit) {
    diagnostics << "Invalid heap size: reserve size must be equal to or "
                << "greater than commit size, but got " << _heapCommit
                << " and " << _heapReserve << ".\n";
    return false;
  }

  // It's an error if the base address is not multiple of 64K.
  if (_baseAddress & 0xffff) {
    diagnostics << "Base address have to be multiple of 64K, but got "
                << _baseAddress << "\n";
    return false;
  }

  std::bitset<64> alignment(_sectionDefaultAlignment);
  if (alignment.count() != 1) {
    diagnostics << "Section alignment must be a power of 2, but got "
                << _sectionDefaultAlignment << "\n";
    return false;
  }

  // Architectures other than i386 is not supported yet.
  if (_machineType != llvm::COFF::IMAGE_FILE_MACHINE_I386) {
    diagnostics << "Machine type other than x86 is not supported.\n";
    return false;
  }

  _reader = createReaderPECOFF(*this);
  _writer = createWriterPECOFF(*this);
  return true;
}

std::unique_ptr<File> PECOFFLinkingContext::createEntrySymbolFile() const {
  if (entrySymbolName().empty())
    return nullptr;
  std::unique_ptr<SimpleFile> entryFile(
      new SimpleFile(*this, "command line option /entry"));
  entryFile->addAtom(
      *(new (_allocator) SimpleUndefinedAtom(*entryFile, entrySymbolName())));
  return std::move(entryFile);
}

std::unique_ptr<File> PECOFFLinkingContext::createUndefinedSymbolFile() const {
  if (_initialUndefinedSymbols.empty())
    return nullptr;
  std::unique_ptr<SimpleFile> undefinedSymFile(
      new SimpleFile(*this, "command line option /c (or) /include"));
  for (auto undefSymStr : _initialUndefinedSymbols)
    undefinedSymFile->addAtom(*(new (_allocator) SimpleUndefinedAtom(
                                   *undefinedSymFile, undefSymStr)));
  return std::move(undefinedSymFile);
}

bool PECOFFLinkingContext::createImplicitFiles(
    std::vector<std::unique_ptr<File> > &) const {
  std::unique_ptr<SimpleFileNode> fileNode(
      new SimpleFileNode("Implicit Files"));
  std::unique_ptr<File> linkerGeneratedSymFile(
      new coff::LinkerGeneratedSymbolFile(*this));
  fileNode->appendInputFile(std::move(linkerGeneratedSymFile));
  inputGraph().insertOneElementAt(std::move(fileNode),
                                  InputGraph::Position::END);
  return true;
}

/// Returns the section name in the resulting executable.
///
/// Sections in object files are usually output to the executable with the same
/// name, but you can rename by command line option. /merge:from=to makes the
/// linker to combine "from" section contents to "to" section in the
/// executable. We have a mapping for the renaming. This method looks up the
/// table and returns a new section name if renamed.
StringRef
PECOFFLinkingContext::getFinalSectionName(StringRef sectionName) const {
  auto it = _renamedSections.find(sectionName);
  if (it == _renamedSections.end())
    return sectionName;
  return getFinalSectionName(it->second);
}

/// Adds a mapping to the section renaming table. This method will be used for
/// /merge command line option.
bool PECOFFLinkingContext::addSectionRenaming(raw_ostream &diagnostics,
                                              StringRef from, StringRef to) {
  auto it = _renamedSections.find(from);
  if (it != _renamedSections.end()) {
    if (it->second == to)
      // There's already the same mapping.
      return true;
    diagnostics << "Section \"" << from << "\" is already mapped to \""
                << it->second << ", so it cannot be mapped to \"" << to << "\".";
    return true;
  }

  // Add a mapping, and check if there's no cycle in the renaming mapping. The
  // cycle detection algorithm we use here is naive, but that's OK because the
  // number of mapping is usually less than 10.
  _renamedSections[from] = to;
  for (auto elem : _renamedSections) {
    StringRef sectionName = elem.first;
    std::set<StringRef> visited;
    visited.insert(sectionName);
    for (;;) {
      auto it = _renamedSections.find(sectionName);
      if (it == _renamedSections.end())
        break;
      if (visited.count(it->second)) {
        diagnostics << "/merge:" << from << "=" << to << " makes a cycle";
        return false;
      }
      sectionName = it->second;
      visited.insert(sectionName);
    }
  }
  return true;
}

/// Try to find the input library file from the search paths and append it to
/// the input file list. Returns true if the library file is found.
StringRef PECOFFLinkingContext::searchLibraryFile(StringRef filename) const {
  // Current directory always takes precedence over the search paths.
  if (llvm::sys::path::is_absolute(filename) || llvm::sys::fs::exists(filename))
    return filename;
  // Iterate over the search paths.
  for (StringRef dir : _inputSearchPaths) {
    SmallString<128> path = dir;
    llvm::sys::path::append(path, filename);
    if (llvm::sys::fs::exists(path.str()))
      return allocateString(path.str());
  }
  return filename;
}

Writer &PECOFFLinkingContext::writer() const { return *_writer; }

ErrorOr<Reference::Kind>
PECOFFLinkingContext::relocKindFromString(StringRef str) const {
#define LLD_CASE(name) .Case(#name, llvm::COFF::name)
  int32_t ret = llvm::StringSwitch<int32_t>(str)
        LLD_CASE(IMAGE_REL_I386_ABSOLUTE)
        LLD_CASE(IMAGE_REL_I386_DIR32)
        LLD_CASE(IMAGE_REL_I386_DIR32NB)
        LLD_CASE(IMAGE_REL_I386_REL32)
        .Default(-1);
#undef LLD_CASE
  if (ret == -1)
    return make_error_code(YamlReaderError::illegal_value);
  return ret;
}

ErrorOr<std::string>
PECOFFLinkingContext::stringFromRelocKind(Reference::Kind kind) const {
  switch (kind) {
#define LLD_CASE(name)                          \
    case llvm::COFF::name:                      \
      return std::string(#name);

    LLD_CASE(IMAGE_REL_I386_ABSOLUTE)
    LLD_CASE(IMAGE_REL_I386_DIR32)
    LLD_CASE(IMAGE_REL_I386_DIR32NB)
    LLD_CASE(IMAGE_REL_I386_REL32)
#undef LLD_CASE
  }
  return make_error_code(YamlReaderError::illegal_value);
}

void PECOFFLinkingContext::addPasses(PassManager &pm) {
  pm.add(std::unique_ptr<Pass>(new pecoff::GroupedSectionsPass()));
  pm.add(std::unique_ptr<Pass>(new pecoff::IdataPass(*this)));
  pm.add(std::unique_ptr<Pass>(new LayoutPass()));
}
} // end namespace lld
<commit_msg>rename local variable to avoid shadowing warning<commit_after>//===- lib/ReaderWriter/PECOFF/PECOFFLinkingContext.cpp -------------------===//
//
//                             The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "Atoms.h"
#include "GroupedSectionsPass.h"
#include "IdataPass.h"
#include "LinkerGeneratedSymbolFile.h"

#include "lld/Core/PassManager.h"
#include "lld/Passes/LayoutPass.h"
#include "lld/Passes/RoundTripNativePass.h"
#include "lld/Passes/RoundTripYAMLPass.h"
#include "lld/ReaderWriter/PECOFFLinkingContext.h"
#include "lld/ReaderWriter/Reader.h"
#include "lld/ReaderWriter/Simple.h"
#include "lld/ReaderWriter/Writer.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/Path.h"

#include <bitset>
#include <set>

namespace lld {

namespace {} // anonymous namespace

bool PECOFFLinkingContext::validateImpl(raw_ostream &diagnostics) {
  if (_stackReserve < _stackCommit) {
    diagnostics << "Invalid stack size: reserve size must be equal to or "
                << "greater than commit size, but got " << _stackCommit
                << " and " << _stackReserve << ".\n";
    return false;
  }

  if (_heapReserve < _heapCommit) {
    diagnostics << "Invalid heap size: reserve size must be equal to or "
                << "greater than commit size, but got " << _heapCommit
                << " and " << _heapReserve << ".\n";
    return false;
  }

  // It's an error if the base address is not multiple of 64K.
  if (_baseAddress & 0xffff) {
    diagnostics << "Base address have to be multiple of 64K, but got "
                << _baseAddress << "\n";
    return false;
  }

  std::bitset<64> alignment(_sectionDefaultAlignment);
  if (alignment.count() != 1) {
    diagnostics << "Section alignment must be a power of 2, but got "
                << _sectionDefaultAlignment << "\n";
    return false;
  }

  // Architectures other than i386 is not supported yet.
  if (_machineType != llvm::COFF::IMAGE_FILE_MACHINE_I386) {
    diagnostics << "Machine type other than x86 is not supported.\n";
    return false;
  }

  _reader = createReaderPECOFF(*this);
  _writer = createWriterPECOFF(*this);
  return true;
}

std::unique_ptr<File> PECOFFLinkingContext::createEntrySymbolFile() const {
  if (entrySymbolName().empty())
    return nullptr;
  std::unique_ptr<SimpleFile> entryFile(
      new SimpleFile(*this, "command line option /entry"));
  entryFile->addAtom(
      *(new (_allocator) SimpleUndefinedAtom(*entryFile, entrySymbolName())));
  return std::move(entryFile);
}

std::unique_ptr<File> PECOFFLinkingContext::createUndefinedSymbolFile() const {
  if (_initialUndefinedSymbols.empty())
    return nullptr;
  std::unique_ptr<SimpleFile> undefinedSymFile(
      new SimpleFile(*this, "command line option /c (or) /include"));
  for (auto undefSymStr : _initialUndefinedSymbols)
    undefinedSymFile->addAtom(*(new (_allocator) SimpleUndefinedAtom(
                                   *undefinedSymFile, undefSymStr)));
  return std::move(undefinedSymFile);
}

bool PECOFFLinkingContext::createImplicitFiles(
    std::vector<std::unique_ptr<File> > &) const {
  std::unique_ptr<SimpleFileNode> fileNode(
      new SimpleFileNode("Implicit Files"));
  std::unique_ptr<File> linkerGeneratedSymFile(
      new coff::LinkerGeneratedSymbolFile(*this));
  fileNode->appendInputFile(std::move(linkerGeneratedSymFile));
  inputGraph().insertOneElementAt(std::move(fileNode),
                                  InputGraph::Position::END);
  return true;
}

/// Returns the section name in the resulting executable.
///
/// Sections in object files are usually output to the executable with the same
/// name, but you can rename by command line option. /merge:from=to makes the
/// linker to combine "from" section contents to "to" section in the
/// executable. We have a mapping for the renaming. This method looks up the
/// table and returns a new section name if renamed.
StringRef
PECOFFLinkingContext::getFinalSectionName(StringRef sectionName) const {
  auto it = _renamedSections.find(sectionName);
  if (it == _renamedSections.end())
    return sectionName;
  return getFinalSectionName(it->second);
}

/// Adds a mapping to the section renaming table. This method will be used for
/// /merge command line option.
bool PECOFFLinkingContext::addSectionRenaming(raw_ostream &diagnostics,
                                              StringRef from, StringRef to) {
  auto it = _renamedSections.find(from);
  if (it != _renamedSections.end()) {
    if (it->second == to)
      // There's already the same mapping.
      return true;
    diagnostics << "Section \"" << from << "\" is already mapped to \""
                << it->second << ", so it cannot be mapped to \"" << to << "\".";
    return true;
  }

  // Add a mapping, and check if there's no cycle in the renaming mapping. The
  // cycle detection algorithm we use here is naive, but that's OK because the
  // number of mapping is usually less than 10.
  _renamedSections[from] = to;
  for (auto elem : _renamedSections) {
    StringRef sectionName = elem.first;
    std::set<StringRef> visited;
    visited.insert(sectionName);
    for (;;) {
      auto pos = _renamedSections.find(sectionName);
      if (pos == _renamedSections.end())
        break;
      if (visited.count(pos->second)) {
        diagnostics << "/merge:" << from << "=" << to << " makes a cycle";
        return false;
      }
      sectionName = pos->second;
      visited.insert(sectionName);
    }
  }
  return true;
}

/// Try to find the input library file from the search paths and append it to
/// the input file list. Returns true if the library file is found.
StringRef PECOFFLinkingContext::searchLibraryFile(StringRef filename) const {
  // Current directory always takes precedence over the search paths.
  if (llvm::sys::path::is_absolute(filename) || llvm::sys::fs::exists(filename))
    return filename;
  // Iterate over the search paths.
  for (StringRef dir : _inputSearchPaths) {
    SmallString<128> path = dir;
    llvm::sys::path::append(path, filename);
    if (llvm::sys::fs::exists(path.str()))
      return allocateString(path.str());
  }
  return filename;
}

Writer &PECOFFLinkingContext::writer() const { return *_writer; }

ErrorOr<Reference::Kind>
PECOFFLinkingContext::relocKindFromString(StringRef str) const {
#define LLD_CASE(name) .Case(#name, llvm::COFF::name)
  int32_t ret = llvm::StringSwitch<int32_t>(str)
        LLD_CASE(IMAGE_REL_I386_ABSOLUTE)
        LLD_CASE(IMAGE_REL_I386_DIR32)
        LLD_CASE(IMAGE_REL_I386_DIR32NB)
        LLD_CASE(IMAGE_REL_I386_REL32)
        .Default(-1);
#undef LLD_CASE
  if (ret == -1)
    return make_error_code(YamlReaderError::illegal_value);
  return ret;
}

ErrorOr<std::string>
PECOFFLinkingContext::stringFromRelocKind(Reference::Kind kind) const {
  switch (kind) {
#define LLD_CASE(name)                          \
    case llvm::COFF::name:                      \
      return std::string(#name);

    LLD_CASE(IMAGE_REL_I386_ABSOLUTE)
    LLD_CASE(IMAGE_REL_I386_DIR32)
    LLD_CASE(IMAGE_REL_I386_DIR32NB)
    LLD_CASE(IMAGE_REL_I386_REL32)
#undef LLD_CASE
  }
  return make_error_code(YamlReaderError::illegal_value);
}

void PECOFFLinkingContext::addPasses(PassManager &pm) {
  pm.add(std::unique_ptr<Pass>(new pecoff::GroupedSectionsPass()));
  pm.add(std::unique_ptr<Pass>(new pecoff::IdataPass(*this)));
  pm.add(std::unique_ptr<Pass>(new LayoutPass()));
}
} // end namespace lld
<|endoftext|>