<commit_before>// Copyright (c) 2014 Baidu, Inc.G
//
// 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.
//
// Authors: Lei He (helei@qiyi.com)

#include <cmath>
#include <gflags/gflags.h>
#include "brpc/circuit_breaker.h"

namespace brpc {

DEFINE_int32(circuit_breaker_short_window_size, 100,
    "Short window sample size.");
DEFINE_int32(circuit_breaker_long_window_size, 1000,
    "Long window sample size.");
DEFINE_int32(circuit_breaker_short_window_error_percent, 5,
    "The maximum error rate allowed by the short window, ranging from 0-99.");
DEFINE_int32(circuit_breaker_long_window_error_percent, 3,
    "The maximum error rate allowed by the long window, ranging from 0-99.");
DEFINE_int32(circuit_breaker_min_error_cost_us, 100,
    "The minimum error_cost, when the ema of error cost is less than this "
    "value, it will be set to zero.");

namespace {
// EPSILON is used to generate the smoothing coefficient when calculating EMA.
// The larger the EPSILON, the larger the smoothing coefficient, which means
// that the proportion of early data is larger.
// smooth = pow(EPSILON, 1 / window_size),
// eg: when window_size = 100,
// EPSILON = 0.1, smooth = 0.9772
// EPSILON = 0.3, smooth = 0.9880
// when window_size = 30,
// EPSILON = 0.1, smooth = 0.9261
// EPSILON = 0.3, smooth = 0.9606
const double EPSILON = 0.1;
}  // namepace

CircuitBreaker::EmaErrorRecorder::EmaErrorRecorder(int window_size,
                                                   int max_error_percent)
    : _window_size(window_size)
    , _max_error_percent(max_error_percent)
    , _smooth(std::pow(EPSILON, 1.0/window_size))
    , _init_completed(false)
    , _sample_count(0)
    , _ema_error_cost(0)
    , _ema_latency(0)
    , _broken(false) {
}

bool CircuitBreaker::EmaErrorRecorder::OnCallEnd(int error_code,
                                                 int64_t latency) {
    if (_broken.load(butil::memory_order_relaxed)) {
        return false;
    }

    int64_t ema_latency = 0;
    bool healthy = false;
    if (error_code == 0) {
        ema_latency = UpdateLatency(latency);
        healthy = UpdateErrorCost(0, ema_latency);
    } else {
        ema_latency = _ema_latency.load(butil::memory_order_relaxed);
        healthy = UpdateErrorCost(latency, ema_latency);
    }

    int sample_count = _sample_count.fetch_add(1, butil::memory_order_relaxed);
    bool init_completed = _init_completed.load(butil::memory_order_acquire);
    if (!init_completed && sample_count >= _window_size) {
        _init_completed.store(true, butil::memory_order_release);
        init_completed = true;
    }

    if (!init_completed) {
        return true;
    }
    if (!healthy) {
        _broken.store(true, butil::memory_order_relaxed);
    }
    return healthy;
}

void CircuitBreaker::EmaErrorRecorder::Reset() {
    _init_completed.store(false, butil::memory_order_relaxed);
    _sample_count.store(0, butil::memory_order_relaxed);
    _ema_error_cost.store(0, butil::memory_order_relaxed);
    _ema_latency.store(0, butil::memory_order_relaxed);
    _broken.store(false, butil::memory_order_relaxed);
}

int64_t CircuitBreaker::EmaErrorRecorder::UpdateLatency(int64_t latency) {
    while (true) {
        int64_t ema_latency = _ema_latency.load(butil::memory_order_relaxed);
        int64_t next_ema_latency = 0;
        if (0 == ema_latency) {
            next_ema_latency = latency;
        } else {
            next_ema_latency = ema_latency * _smooth + latency * (1 - _smooth);
        }
        if (_ema_latency.compare_exchange_weak(ema_latency, next_ema_latency)) {
            return next_ema_latency;
        }
    }
}

bool CircuitBreaker::EmaErrorRecorder::UpdateErrorCost(int64_t error_cost,
                                                       int64_t ema_latency) {
    //Errorous response
    if (error_cost != 0) {
        int64_t ema_error_cost =
            _ema_error_cost.fetch_add(error_cost, butil::memory_order_relaxed);
        ema_error_cost += error_cost;
        int64_t max_error_cost = ema_latency * _window_size *
            (_max_error_percent / 100.0) * (1.0 + EPSILON);
        return ema_error_cost <= max_error_cost;
    }

    //Ordinary response
    while (true) {
        int64_t ema_error_cost =
            _ema_error_cost.load(butil::memory_order_relaxed);
        if (ema_error_cost == 0) {
            break;
        } else if (ema_error_cost < FLAGS_circuit_breaker_min_error_cost_us) {
            if (_ema_error_cost.compare_exchange_weak(
                ema_error_cost, 0, butil::memory_order_relaxed)) {
                break;
            }
        } else {
            int64_t next_ema_error_cost = ema_error_cost * _smooth;
            if (_ema_error_cost.compare_exchange_weak(
                ema_error_cost, next_ema_error_cost)) {
                break;
            }
        }
    }
    return true;
}

CircuitBreaker::CircuitBreaker()
    : _long_window(FLAGS_circuit_breaker_long_window_size,
                   FLAGS_circuit_breaker_long_window_error_percent)
    , _short_window(FLAGS_circuit_breaker_short_window_size,
                    FLAGS_circuit_breaker_short_window_error_percent) {
}

bool CircuitBreaker::OnCallEnd(int error_code, int64_t latency) {
    return _long_window.OnCallEnd(error_code, latency) &&
           _short_window.OnCallEnd(error_code, latency);
}

void CircuitBreaker::Reset() {
    _long_window.Reset();
    _short_window.Reset();
}

}  // namespace brpc
<commit_msg> Fix the isolation caused by the excessive latency of the timeout requests<commit_after>// Copyright (c) 2014 Baidu, Inc.G
//
// 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.
//
// Authors: Lei He (helei@qiyi.com)

#include <cmath>
#include <gflags/gflags.h>
#include "brpc/circuit_breaker.h"

namespace brpc {

DEFINE_int32(circuit_breaker_short_window_size, 100,
    "Short window sample size.");
DEFINE_int32(circuit_breaker_long_window_size, 1000,
    "Long window sample size.");
DEFINE_int32(circuit_breaker_short_window_error_percent, 5,
    "The maximum error rate allowed by the short window, ranging from 0-99.");
DEFINE_int32(circuit_breaker_long_window_error_percent, 3,
    "The maximum error rate allowed by the long window, ranging from 0-99.");
DEFINE_int32(circuit_breaker_min_error_cost_us, 100,
    "The minimum error_cost, when the ema of error cost is less than this "
    "value, it will be set to zero.");
DEFINE_int32(circuit_breaker_max_failed_latency_mutliple, 2,
    "The maximum multiple of the latency of the failed request relative to "
    "the average latency of the success requests.");

namespace {
// EPSILON is used to generate the smoothing coefficient when calculating EMA.
// The larger the EPSILON, the larger the smoothing coefficient, which means
// that the proportion of early data is larger.
// smooth = pow(EPSILON, 1 / window_size),
// eg: when window_size = 100,
// EPSILON = 0.1, smooth = 0.9772
// EPSILON = 0.3, smooth = 0.9880
// when window_size = 1000,
// EPSILON = 0.1, smooth = 0.9977
// EPSILON = 0.3, smooth = 0.9987
const double EPSILON = 0.1;
}  // namepace

CircuitBreaker::EmaErrorRecorder::EmaErrorRecorder(int window_size,
                                                   int max_error_percent)
    : _window_size(window_size)
    , _max_error_percent(max_error_percent)
    , _smooth(std::pow(EPSILON, 1.0/window_size))
    , _init_completed(false)
    , _sample_count(0)
    , _ema_error_cost(0)
    , _ema_latency(0)
    , _broken(false) {
}

bool CircuitBreaker::EmaErrorRecorder::OnCallEnd(int error_code,
                                                 int64_t latency) {
    if (_broken.load(butil::memory_order_relaxed)) {
        return false;
    }

    int64_t ema_latency = 0;
    bool healthy = false;
    if (error_code == 0) {
        ema_latency = UpdateLatency(latency);
        healthy = UpdateErrorCost(0, ema_latency);
    } else {
        ema_latency = _ema_latency.load(butil::memory_order_relaxed);
        healthy = UpdateErrorCost(latency, ema_latency);
    }

    int sample_count = _sample_count.fetch_add(1, butil::memory_order_relaxed);
    bool init_completed = _init_completed.load(butil::memory_order_acquire);
    if (!init_completed && sample_count >= _window_size) {
        _init_completed.store(true, butil::memory_order_release);
        init_completed = true;
    }

    if (!init_completed) {
        return true;
    }
    if (!healthy) {
        _broken.store(true, butil::memory_order_relaxed);
    }
    return healthy;
}

void CircuitBreaker::EmaErrorRecorder::Reset() {
    _init_completed.store(false, butil::memory_order_relaxed);
    _sample_count.store(0, butil::memory_order_relaxed);
    _ema_error_cost.store(0, butil::memory_order_relaxed);
    _ema_latency.store(0, butil::memory_order_relaxed);
    _broken.store(false, butil::memory_order_relaxed);
}

int64_t CircuitBreaker::EmaErrorRecorder::UpdateLatency(int64_t latency) {
    while (true) {
        int64_t ema_latency = _ema_latency.load(butil::memory_order_relaxed);
        int64_t next_ema_latency = 0;
        if (0 == ema_latency) {
            next_ema_latency = latency;
        } else {
            next_ema_latency = ema_latency * _smooth + latency * (1 - _smooth);
        }
        if (_ema_latency.compare_exchange_weak(ema_latency, next_ema_latency)) {
            return next_ema_latency;
        }
    }
}

bool CircuitBreaker::EmaErrorRecorder::UpdateErrorCost(int64_t error_cost,
                                                       int64_t ema_latency) {
    const int max_mutilple = FLAGS_circuit_breaker_max_failed_latency_mutliple;
    error_cost = std::min(ema_latency * max_mutilple, error_cost);
    //Errorous response
    if (error_cost != 0) {
        int64_t ema_error_cost =
            _ema_error_cost.fetch_add(error_cost, butil::memory_order_relaxed);
        ema_error_cost += error_cost;
        int64_t max_error_cost = ema_latency * _window_size *
            (_max_error_percent / 100.0) * (1.0 + EPSILON);
        return ema_error_cost <= max_error_cost;
    }

    //Ordinary response
    while (true) {
        int64_t ema_error_cost =
            _ema_error_cost.load(butil::memory_order_relaxed);
        if (ema_error_cost == 0) {
            break;
        } else if (ema_error_cost < FLAGS_circuit_breaker_min_error_cost_us) {
            if (_ema_error_cost.compare_exchange_weak(
                ema_error_cost, 0, butil::memory_order_relaxed)) {
                break;
            }
        } else {
            int64_t next_ema_error_cost = ema_error_cost * _smooth;
            if (_ema_error_cost.compare_exchange_weak(
                ema_error_cost, next_ema_error_cost)) {
                break;
            }
        }
    }
    return true;
}

CircuitBreaker::CircuitBreaker()
    : _long_window(FLAGS_circuit_breaker_long_window_size,
                   FLAGS_circuit_breaker_long_window_error_percent)
    , _short_window(FLAGS_circuit_breaker_short_window_size,
                    FLAGS_circuit_breaker_short_window_error_percent) {
}

bool CircuitBreaker::OnCallEnd(int error_code, int64_t latency) {
    return _long_window.OnCallEnd(error_code, latency) &&
           _short_window.OnCallEnd(error_code, latency);
}

void CircuitBreaker::Reset() {
    _long_window.Reset();
    _short_window.Reset();
}

}  // namespace brpc
<|endoftext|>


<commit_before>// Copyright 2014 MongoDB Inc.
//
// 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.

#pragma once

#include <bsoncxx/config/prelude.hpp>

#include <type_traits>

namespace bsoncxx {
BSONCXX_INLINE_NAMESPACE_BEGIN
namespace util {
namespace functor {

template <typename, typename>
struct build_free_function;

template <typename F, typename R, typename... Args>
struct build_free_function<F, R(Args...)> {
    typedef R (*type)(Args...);
};

template <typename, typename>
struct build_class_function;

template <typename C, typename R, typename... Args>
struct build_class_function<C, R(Args...)> {
    typedef R (C::*type)(Args...);
};

template <typename>
struct strip_cv_from_class_function;

template <typename C, typename R, typename... Args>
struct strip_cv_from_class_function<R (C::*)(Args...)> {
    typedef R (C::*type)(Args...);
};

template <typename C, typename R, typename... Args>
struct strip_cv_from_class_function<R (C::*)(Args...) const> {
    typedef R (C::*type)(Args...);
};

template <typename C, typename R, typename... Args>
struct strip_cv_from_class_function<R (C::*)(Args...) volatile> {
    typedef R (C::*type)(Args...);
};

template <typename C, typename S>
struct is_class_method_with_signature {
    typedef int yes;
    typedef char no;

    // T stands for SFINAE
    template <typename T>
    static typename std::enable_if<std::is_convertible<typename build_class_function<C, S>::type,
                                                       typename strip_cv_from_class_function<
                                                           decltype(&T::operator())>::type>::value,
                                   yes>::type
        sfinae(void *);

    template <typename>
    static no sfinae(...);

    static bool constexpr value = sizeof(sfinae<C>(nullptr)) == sizeof(yes);
};

template <typename F, typename S>
struct is_function_with_signature
    : std::is_convertible<F, typename build_free_function<F, S>::type> {};

template <typename C, typename S, bool>
struct is_functor_impl : is_class_method_with_signature<C, S> {};

template <typename F, typename S>
struct is_functor_impl<F, S, false> : is_function_with_signature<F, S> {};

}  // namespace functor

template <typename C, typename S>
struct is_functor : functor::is_functor_impl<C, S, std::is_class<C>::value> {};

}  // namespace util
BSONCXX_INLINE_NAMESPACE_END
}  // namespace bsoncxx

#include <bsoncxx/config/postlude.hpp>
<commit_msg>CXX-616 simplify is_functor<commit_after>// Copyright 2014 MongoDB Inc.
//
// 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.

#pragma once

#include <bsoncxx/config/prelude.hpp>

#include <type_traits>

namespace bsoncxx {
BSONCXX_INLINE_NAMESPACE_BEGIN
namespace util {

template <typename FunctionLike, typename Signature>
using is_functor = std::is_convertible<FunctionLike, std::function<Signature>>;

}  // namespace util
BSONCXX_INLINE_NAMESPACE_END
}  // namespace bsoncxx

#include <bsoncxx/config/postlude.hpp>
<|endoftext|>


<commit_before>#include <vector>
#include <string>
#include <iostream>
#include <streambuf>
#include <sstream>

#include "gtest/gtest.h"

#include "commandline.h"

#define REQUIRED_TABLES "-I", "a", "-O", "b"
#define REQUIRED_STORAGE "-s", "sqlite", "--sqlite_file", "abc"
#define REQUIRED REQUIRED_STORAGE, REQUIRED_TABLES

class ServerCommandline : public ::testing::Test
{
private:
	std::stringstream _std_out_stream;
	std::stringstream _std_err_stream;
	std::streambuf *_orig_out;
	std::streambuf *_orig_err;

public:
	ServerCommandline()
	:
		_std_out_stream(),
		_std_err_stream(),
		_orig_out(std::cout.rdbuf()),
		_orig_err(std::cerr.rdbuf())
	{
		std::cout.rdbuf(_std_out_stream.rdbuf());
		std::cerr.rdbuf(_std_err_stream.rdbuf());
	}

	~ServerCommandline()
	{
		std::cout.rdbuf(_orig_out);
		std::cerr.rdbuf(_orig_err);
	}

	std::string output() const
	{
		return _std_out_stream.str();
	}

	std::string error() const
	{
		return _std_err_stream.str();
	}
};

// Test if -h prints help and exits
TEST_F(ServerCommandline, test_help) {
	traffic::Commandline cmd;
	const char* argv[] = {"foobar", "-h"};

	EXPECT_FALSE(cmd.parse(2, argv));

	EXPECT_NE(output().find("Print help message."), std::string::npos);
}

// Test if default bind works
TEST_F(ServerCommandline, test_bind_default) {
	traffic::Commandline cmd;
	const char* argv[] = {"foobar", REQUIRED};

	EXPECT_TRUE(cmd.parse(1 + 4 + 4, argv));

	std::vector<std::string> addr(cmd.addresses());
	EXPECT_EQ(addr.size(), 1U);
	EXPECT_EQ(addr[0], "*:3444");
}

// Test if we can bind a single address
TEST_F(ServerCommandline, test_bind_single) {
	traffic::Commandline cmd;
	const char* argv[] = {"foobar", "-b", "abcde", REQUIRED};

	EXPECT_TRUE(cmd.parse(3 + 4 + 4, argv));

	std::vector<std::string> addr(cmd.addresses());
	EXPECT_EQ(addr.size(), 1U);
	EXPECT_EQ(addr[0], "abcde");
}

// Test if we can bind multiple addresses
TEST_F(ServerCommandline, test_bind_multi) {
	traffic::Commandline cmd;
	const char* argv[] = {"foobar",
		"-b", "abcde",
		"--bind", "1234",
		"-b", "12ab",
		REQUIRED};

	EXPECT_TRUE(cmd.parse(7 + 4 + 4, argv));

	std::vector<std::string> addr(cmd.addresses());
	EXPECT_EQ(addr.size(), 3U);
	EXPECT_EQ(addr[0], "abcde");
	EXPECT_EQ(addr[1], "1234");
	EXPECT_EQ(addr[2], "12ab");
}

// Test if we get an error if bind arg is omitted
TEST_F(ServerCommandline, test_bind_missing) {
	traffic::Commandline cmd;
	const char* argv[] = {"foobar", REQUIRED, "-b"};

	EXPECT_FALSE(cmd.parse(2 + 4 + 4, argv));
	EXPECT_NE(error().find("the required argument for "
			       "option '--bind' is missing"),
		  std::string::npos);
}

// Test missing storage type
TEST_F(ServerCommandline, test_missing_storage_type) {
	traffic::Commandline cmd;
	const char* argv[] = {"foobar", REQUIRED_TABLES};

	EXPECT_FALSE(cmd.parse(1 + 4, argv));
	EXPECT_NE(error().find("the option '--storage' "
			       "is required but missing"),
		  std::string::npos);
}

// Test for sqlite storage type
TEST_F(ServerCommandline, test_storage_sqlite) {
	{
		traffic::Commandline cmd;
		const char* argv[] = {"foobar", REQUIRED_TABLES,
				      "-s", "sqlite", "--sqlite_file", "ab"};

		EXPECT_TRUE(cmd.parse(5 + 4, argv));
		EXPECT_EQ(cmd.storage_type(), traffic::Commandline::SQLITE);
		EXPECT_EQ(cmd.sqlite_file(), "ab");
	}
	{
		traffic::Commandline cmd;
		const char* argv[] = {"foobar", REQUIRED_TABLES,
				      "-s", "sqlite"};

		EXPECT_FALSE(cmd.parse(3 + 4, argv));
		EXPECT_NE(error().find("option --sqlite_file missing for "
				       "storage_type sqlite!"),
			  std::string::npos);
	}
}

// Test for mysql storage type
TEST_F(ServerCommandline, test_storage_mysql) {
	traffic::Commandline cmd;
	const char* argv[] = {"foobar", "-s", "mysql", REQUIRED_TABLES};

	EXPECT_TRUE(cmd.parse(3 + 4, argv));

	EXPECT_EQ(cmd.storage_type(), traffic::Commandline::MYSQL);
}

// Test for postgres storage type
TEST_F(ServerCommandline, test_storage_postgres) {
	traffic::Commandline cmd;
	const char* argv[] = {"foobar", "-s", "postgres", REQUIRED_TABLES};

	EXPECT_TRUE(cmd.parse(3 + 4, argv));

	EXPECT_EQ(cmd.storage_type(), traffic::Commandline::POSTGRES);
}

// Test for invalid storage type
TEST_F(ServerCommandline, test_storage_invalid) {
	traffic::Commandline cmd;
	const char* argv[] = {"foobar", "-s", "peng", REQUIRED_TABLES};

	EXPECT_FALSE(cmd.parse(3 + 4, argv));

	EXPECT_NE(error().find("the argument for option "
			       "'--storage' is invalid"),
		  std::string::npos);
}

// Test for sqlite storage type
TEST_F(ServerCommandline, test_worker_default) {
	traffic::Commandline cmd;
	const char* argv[] = {"foobar", REQUIRED};

	EXPECT_TRUE(cmd.parse(3 + 4 + 4, argv));

	EXPECT_EQ(cmd.worker(), 2U);
}

// Test for sqlite storage type
TEST_F(ServerCommandline, test_worker_count) {
	traffic::Commandline cmd;
	const char* argv[] = {"foobar", "-w", "5", REQUIRED};

	EXPECT_TRUE(cmd.parse(3 + 4 + 4, argv));

	EXPECT_EQ(cmd.worker(), 5U);
}

// Test output table missing
TEST_F(ServerCommandline, test_tables_out_missing) {
	traffic::Commandline cmd;
	const char* argv[] = {"foobar", REQUIRED_STORAGE, "-I", "a"};

	EXPECT_FALSE(cmd.parse(3 + 4, argv));
	EXPECT_NE(error().find("the option '--table_out' is "
				"required but missing"),
			std::string::npos);
}

// Test input table missing
TEST_F(ServerCommandline, test_tables_in_missing) {
	traffic::Commandline cmd;
	const char* argv[] = {"foobar", REQUIRED_STORAGE, "-O", "a"};

	EXPECT_FALSE(cmd.parse(3 + 4, argv));
	EXPECT_NE(error().find("the option '--table_in' is "
				"required but missing"),
			std::string::npos);
}

// Test tables
TEST_F(ServerCommandline, test_tables) {
	traffic::Commandline cmd;
	const char* argv[] = {"foobar", REQUIRED_STORAGE,
			      "-O", "a", "-I", "b"};

	EXPECT_TRUE(cmd.parse(5 + 4, argv));
	EXPECT_EQ(cmd.table_outgoing(), "a");
	EXPECT_EQ(cmd.table_incomming(), "b");
}
<commit_msg>Fix wrong argument count<commit_after>#include <vector>
#include <string>
#include <iostream>
#include <streambuf>
#include <sstream>

#include "gtest/gtest.h"

#include "commandline.h"

#define REQUIRED_TABLES "-I", "a", "-O", "b"
#define REQUIRED_STORAGE "-s", "sqlite", "--sqlite_file", "abc"
#define REQUIRED REQUIRED_STORAGE, REQUIRED_TABLES

class ServerCommandline : public ::testing::Test
{
private:
	std::stringstream _std_out_stream;
	std::stringstream _std_err_stream;
	std::streambuf *_orig_out;
	std::streambuf *_orig_err;

public:
	ServerCommandline()
	:
		_std_out_stream(),
		_std_err_stream(),
		_orig_out(std::cout.rdbuf()),
		_orig_err(std::cerr.rdbuf())
	{
		std::cout.rdbuf(_std_out_stream.rdbuf());
		std::cerr.rdbuf(_std_err_stream.rdbuf());
	}

	~ServerCommandline()
	{
		std::cout.rdbuf(_orig_out);
		std::cerr.rdbuf(_orig_err);
	}

	std::string output() const
	{
		return _std_out_stream.str();
	}

	std::string error() const
	{
		return _std_err_stream.str();
	}
};

// Test if -h prints help and exits
TEST_F(ServerCommandline, test_help) {
	traffic::Commandline cmd;
	const char* argv[] = {"foobar", "-h"};

	EXPECT_FALSE(cmd.parse(2, argv));

	EXPECT_NE(output().find("Print help message."), std::string::npos);
}

// Test if default bind works
TEST_F(ServerCommandline, test_bind_default) {
	traffic::Commandline cmd;
	const char* argv[] = {"foobar", REQUIRED};

	EXPECT_TRUE(cmd.parse(1 + 4 + 4, argv));

	std::vector<std::string> addr(cmd.addresses());
	EXPECT_EQ(addr.size(), 1U);
	EXPECT_EQ(addr[0], "*:3444");
}

// Test if we can bind a single address
TEST_F(ServerCommandline, test_bind_single) {
	traffic::Commandline cmd;
	const char* argv[] = {"foobar", "-b", "abcde", REQUIRED};

	EXPECT_TRUE(cmd.parse(3 + 4 + 4, argv));

	std::vector<std::string> addr(cmd.addresses());
	EXPECT_EQ(addr.size(), 1U);
	EXPECT_EQ(addr[0], "abcde");
}

// Test if we can bind multiple addresses
TEST_F(ServerCommandline, test_bind_multi) {
	traffic::Commandline cmd;
	const char* argv[] = {"foobar",
		"-b", "abcde",
		"--bind", "1234",
		"-b", "12ab",
		REQUIRED};

	EXPECT_TRUE(cmd.parse(7 + 4 + 4, argv));

	std::vector<std::string> addr(cmd.addresses());
	EXPECT_EQ(addr.size(), 3U);
	EXPECT_EQ(addr[0], "abcde");
	EXPECT_EQ(addr[1], "1234");
	EXPECT_EQ(addr[2], "12ab");
}

// Test if we get an error if bind arg is omitted
TEST_F(ServerCommandline, test_bind_missing) {
	traffic::Commandline cmd;
	const char* argv[] = {"foobar", REQUIRED, "-b"};

	EXPECT_FALSE(cmd.parse(2 + 4 + 4, argv));
	EXPECT_NE(error().find("the required argument for "
			       "option '--bind' is missing"),
		  std::string::npos);
}

// Test missing storage type
TEST_F(ServerCommandline, test_missing_storage_type) {
	traffic::Commandline cmd;
	const char* argv[] = {"foobar", REQUIRED_TABLES};

	EXPECT_FALSE(cmd.parse(1 + 4, argv));
	EXPECT_NE(error().find("the option '--storage' "
			       "is required but missing"),
		  std::string::npos);
}

// Test for sqlite storage type
TEST_F(ServerCommandline, test_storage_sqlite) {
	{
		traffic::Commandline cmd;
		const char* argv[] = {"foobar", REQUIRED_TABLES,
				      "-s", "sqlite", "--sqlite_file", "ab"};

		EXPECT_TRUE(cmd.parse(5 + 4, argv));
		EXPECT_EQ(cmd.storage_type(), traffic::Commandline::SQLITE);
		EXPECT_EQ(cmd.sqlite_file(), "ab");
	}
	{
		traffic::Commandline cmd;
		const char* argv[] = {"foobar", REQUIRED_TABLES,
				      "-s", "sqlite"};

		EXPECT_FALSE(cmd.parse(3 + 4, argv));
		EXPECT_NE(error().find("option --sqlite_file missing for "
				       "storage_type sqlite!"),
			  std::string::npos);
	}
}

// Test for mysql storage type
TEST_F(ServerCommandline, test_storage_mysql) {
	traffic::Commandline cmd;
	const char* argv[] = {"foobar", "-s", "mysql", REQUIRED_TABLES};

	EXPECT_TRUE(cmd.parse(3 + 4, argv));

	EXPECT_EQ(cmd.storage_type(), traffic::Commandline::MYSQL);
}

// Test for postgres storage type
TEST_F(ServerCommandline, test_storage_postgres) {
	traffic::Commandline cmd;
	const char* argv[] = {"foobar", "-s", "postgres", REQUIRED_TABLES};

	EXPECT_TRUE(cmd.parse(3 + 4, argv));

	EXPECT_EQ(cmd.storage_type(), traffic::Commandline::POSTGRES);
}

// Test for invalid storage type
TEST_F(ServerCommandline, test_storage_invalid) {
	traffic::Commandline cmd;
	const char* argv[] = {"foobar", "-s", "peng", REQUIRED_TABLES};

	EXPECT_FALSE(cmd.parse(3 + 4, argv));

	EXPECT_NE(error().find("the argument for option "
			       "'--storage' is invalid"),
		  std::string::npos);
}

// Test for sqlite storage type
TEST_F(ServerCommandline, test_worker_default) {
	traffic::Commandline cmd;
	const char* argv[] = {"foobar", REQUIRED};

	EXPECT_TRUE(cmd.parse(1 + 4 + 4, argv));

	EXPECT_EQ(cmd.worker(), 2U);
}

// Test for sqlite storage type
TEST_F(ServerCommandline, test_worker_count) {
	traffic::Commandline cmd;
	const char* argv[] = {"foobar", "-w", "5", REQUIRED};

	EXPECT_TRUE(cmd.parse(3 + 4 + 4, argv));

	EXPECT_EQ(cmd.worker(), 5U);
}

// Test output table missing
TEST_F(ServerCommandline, test_tables_out_missing) {
	traffic::Commandline cmd;
	const char* argv[] = {"foobar", REQUIRED_STORAGE, "-I", "a"};

	EXPECT_FALSE(cmd.parse(3 + 4, argv));
	EXPECT_NE(error().find("the option '--table_out' is "
				"required but missing"),
			std::string::npos);
}

// Test input table missing
TEST_F(ServerCommandline, test_tables_in_missing) {
	traffic::Commandline cmd;
	const char* argv[] = {"foobar", REQUIRED_STORAGE, "-O", "a"};

	EXPECT_FALSE(cmd.parse(3 + 4, argv));
	EXPECT_NE(error().find("the option '--table_in' is "
				"required but missing"),
			std::string::npos);
}

// Test tables
TEST_F(ServerCommandline, test_tables) {
	traffic::Commandline cmd;
	const char* argv[] = {"foobar", REQUIRED_STORAGE,
			      "-O", "a", "-I", "b"};

	EXPECT_TRUE(cmd.parse(5 + 4, argv));
	EXPECT_EQ(cmd.table_outgoing(), "a");
	EXPECT_EQ(cmd.table_incomming(), "b");
}
<|endoftext|>


<commit_before>//* This file is part of the MOOSE framework
//* https://www.mooseframework.org
//*
//* All rights reserved, see COPYRIGHT for full restrictions
//* https://github.com/idaholab/moose/blob/master/COPYRIGHT
//*
//* Licensed under LGPL 2.1, please see LICENSE for details
//* https://www.gnu.org/licenses/lgpl-2.1.html

#include "TensorMechanicsActionBase.h"
#include "CommonTensorMechanicsAction.h"
#include "ActionWarehouse.h"
#include "ComputeFiniteStrain.h"

// map tensor name shortcuts to tensor material property names
const std::map<std::string, std::string> TensorMechanicsActionBase::_ranktwoaux_table = {
    {"strain", "total_strain"},
    {"stress", "stress"},
    {"elastic_strain", "elastic_strain"},
    {"plastic_strain", "plastic_strain"},
    {"creep_strain", "creep_strain"}};
const std::vector<char> TensorMechanicsActionBase::_component_table = {'x', 'y', 'z'};
// map aux variable name prefixes to RanTwoScalarAux option and list of permitted tensor name
// shortcuts
const std::map<std::string, std::pair<std::string, std::vector<std::string>>>
    TensorMechanicsActionBase::_ranktwoscalaraux_table = {
        {"vonmises", {"VonMisesStress", {"stress"}}},
        {"hydrostatic", {"Hydrostatic", {"stress"}}},
        {"max_principal", {"MaxPrincipal", {"stress"}}},
        {"mid_principal", {"MidPrincipal", {"stress"}}},
        {"min_principal", {"MinPrincipal", {"stress"}}},
        {"effective", {"EffectiveStrain", {"plastic_strain", "creep_strain"}}},
        {"firstinv", {"FirstInvariant", {"stress", "strain"}}},
        {"secondinv", {"SecondInvariant", {"stress", "strain"}}},
        {"thirdinv", {"ThirdInvariant", {"stress", "strain"}}}};

template <>
InputParameters
validParams<TensorMechanicsActionBase>()
{
  InputParameters params = validParams<Action>();

  params.addRequiredParam<std::vector<NonlinearVariableName>>(
      "displacements", "The nonlinear displacement variables for the problem");
  params.addParam<NonlinearVariableName>("temperature", "The temperature");

  MooseEnum strainType("SMALL FINITE", "SMALL");
  params.addParam<MooseEnum>("strain", strainType, "Strain formulation");
  params.addParam<bool>("incremental", "Use incremental or total strain");

  params.addParam<std::string>("base_name", "Material property base name");
  params.addParam<bool>(
      "volumetric_locking_correction", false, "Flag to correct volumetric locking");
  params.addParam<bool>(
      "use_finite_deform_jacobian", false, "Jacobian for corrotational finite strain");
  params.addParam<bool>(
      "use_displaced_mesh", false, "Whether to use displaced mesh in the kernels");
  params.addParam<bool>("add_variables", false, "Add the displacement variables");
  params.addParam<std::vector<MaterialPropertyName>>(
      "eigenstrain_names", "List of eigenstrains to be applied in this strain calculation");
  // Global Strain
  params.addParam<MaterialPropertyName>(
      "global_strain",
      "Name of the global strain material to be applied in this strain calculation. "
      "The global strain tensor is constant over the whole domain and allows visualization "
      "of the deformed shape with the periodic BC");

  // Advanced
  params.addParam<std::vector<AuxVariableName>>("save_in", "The displacement residuals");
  params.addParam<std::vector<AuxVariableName>>("diag_save_in",
                                                "The displacement diagonal preconditioner terms");
  params.addParam<MooseEnum>("decomposition_method",
                             ComputeFiniteStrain::decompositionType(),
                             "Methods to calculate the finite strain and rotation increments");
  params.addParamNamesToGroup("save_in diag_save_in", "Advanced");

  // Planar Formulation
  MooseEnum planarFormulationType("NONE PLANE_STRAIN GENERALIZED_PLANE_STRAIN",
                                  "NONE"); // PLANE_STRESS
  params.addParam<MooseEnum>(
      "planar_formulation", planarFormulationType, "Out-of-plane stress/strain formulation");
  params.addParam<NonlinearVariableName>("scalar_out_of_plane_strain",
                                         "Scalar variable for the out-of-plane strain (in y "
                                         "direction for 1D Axisymmetric or in z direction for 2D "
                                         "Cartesian problems)");
  MooseEnum outOfPlaneDirection("x y z", "z");
  params.addParam<MooseEnum>(
      "out_of_plane_direction", outOfPlaneDirection, "The direction of the out-of-plane strain.");
  params.addParam<FunctionName>("out_of_plane_pressure",
                                "0",
                                "Function used to prescribe pressure in the out-of-plane direction "
                                "(y for 1D Axisymmetric or z for 2D Cartesian problems)");
  params.addParam<Real>("pressure_factor", 1.0, "Scale factor applied to prescribed pressure");
  params.addParamNamesToGroup("planar_formulation scalar_out_of_plane_strain out_of_plane_pressure "
                              "pressure_factor out_of_plane_direction",
                              "Out-of-plane stress/strain");

  // Output
  params.addParam<MultiMooseEnum>("generate_output",
                                  TensorMechanicsActionBase::outputPropertiesType(),
                                  "Add scalar quantity output for stress and/or strain");
  params.addParamNamesToGroup("generate_output", "Output");

  return params;
}

TensorMechanicsActionBase::TensorMechanicsActionBase(const InputParameters & parameters)
  : Action(parameters)
{
  // check if a container block with common parameters is found
  auto action = _awh.getActions<CommonTensorMechanicsAction>();
  if (action.size() == 1)
    _pars.applyParameters(action[0]->parameters());

  // append additional_generate_output
  if (isParamValid("additional_generate_output"))
  {
    MultiMooseEnum generate_output = getParam<MultiMooseEnum>("generate_output");
    MultiMooseEnum additional_generate_output =
        getParam<MultiMooseEnum>("additional_generate_output");
    for (auto & output : additional_generate_output)
      generate_output.push_back(output);

    _pars.set<MultiMooseEnum>("generate_output") = generate_output;
  }
}

MultiMooseEnum
TensorMechanicsActionBase::outputPropertiesType()
{
  std::string options = "";
  for (auto & r2a : _ranktwoaux_table)
    for (unsigned int a = 0; a < 3; ++a)
      for (unsigned int b = 0; b < 3; ++b)
        options += (options == "" ? "" : " ") + r2a.first + '_' + _component_table[a] +
                   _component_table[b];

  for (auto & r2sa : _ranktwoscalaraux_table)
    for (auto & t : r2sa.second.second)
      options += " " + r2sa.first + "_" + t;

  return MultiMooseEnum(options);
}
<commit_msg>Remove options to output effective_plastic_strain and effective_creep_strain ref #12386<commit_after>//* This file is part of the MOOSE framework
//* https://www.mooseframework.org
//*
//* All rights reserved, see COPYRIGHT for full restrictions
//* https://github.com/idaholab/moose/blob/master/COPYRIGHT
//*
//* Licensed under LGPL 2.1, please see LICENSE for details
//* https://www.gnu.org/licenses/lgpl-2.1.html

#include "TensorMechanicsActionBase.h"
#include "CommonTensorMechanicsAction.h"
#include "ActionWarehouse.h"
#include "ComputeFiniteStrain.h"

// map tensor name shortcuts to tensor material property names
const std::map<std::string, std::string> TensorMechanicsActionBase::_ranktwoaux_table = {
    {"strain", "total_strain"},
    {"stress", "stress"},
    {"elastic_strain", "elastic_strain"},
    {"plastic_strain", "plastic_strain"},
    {"creep_strain", "creep_strain"}};
const std::vector<char> TensorMechanicsActionBase::_component_table = {'x', 'y', 'z'};
// map aux variable name prefixes to RanTwoScalarAux option and list of permitted tensor name
// shortcuts
const std::map<std::string, std::pair<std::string, std::vector<std::string>>>
    TensorMechanicsActionBase::_ranktwoscalaraux_table = {
        {"vonmises", {"VonMisesStress", {"stress"}}},
        {"hydrostatic", {"Hydrostatic", {"stress"}}},
        {"max_principal", {"MaxPrincipal", {"stress"}}},
        {"mid_principal", {"MidPrincipal", {"stress"}}},
        {"min_principal", {"MinPrincipal", {"stress"}}},
        {"firstinv", {"FirstInvariant", {"stress", "strain"}}},
        {"secondinv", {"SecondInvariant", {"stress", "strain"}}},
        {"thirdinv", {"ThirdInvariant", {"stress", "strain"}}}};

template <>
InputParameters
validParams<TensorMechanicsActionBase>()
{
  InputParameters params = validParams<Action>();

  params.addRequiredParam<std::vector<NonlinearVariableName>>(
      "displacements", "The nonlinear displacement variables for the problem");
  params.addParam<NonlinearVariableName>("temperature", "The temperature");

  MooseEnum strainType("SMALL FINITE", "SMALL");
  params.addParam<MooseEnum>("strain", strainType, "Strain formulation");
  params.addParam<bool>("incremental", "Use incremental or total strain");

  params.addParam<std::string>("base_name", "Material property base name");
  params.addParam<bool>(
      "volumetric_locking_correction", false, "Flag to correct volumetric locking");
  params.addParam<bool>(
      "use_finite_deform_jacobian", false, "Jacobian for corrotational finite strain");
  params.addParam<bool>(
      "use_displaced_mesh", false, "Whether to use displaced mesh in the kernels");
  params.addParam<bool>("add_variables", false, "Add the displacement variables");
  params.addParam<std::vector<MaterialPropertyName>>(
      "eigenstrain_names", "List of eigenstrains to be applied in this strain calculation");
  // Global Strain
  params.addParam<MaterialPropertyName>(
      "global_strain",
      "Name of the global strain material to be applied in this strain calculation. "
      "The global strain tensor is constant over the whole domain and allows visualization "
      "of the deformed shape with the periodic BC");

  // Advanced
  params.addParam<std::vector<AuxVariableName>>("save_in", "The displacement residuals");
  params.addParam<std::vector<AuxVariableName>>("diag_save_in",
                                                "The displacement diagonal preconditioner terms");
  params.addParam<MooseEnum>("decomposition_method",
                             ComputeFiniteStrain::decompositionType(),
                             "Methods to calculate the finite strain and rotation increments");
  params.addParamNamesToGroup("save_in diag_save_in", "Advanced");

  // Planar Formulation
  MooseEnum planarFormulationType("NONE PLANE_STRAIN GENERALIZED_PLANE_STRAIN",
                                  "NONE"); // PLANE_STRESS
  params.addParam<MooseEnum>(
      "planar_formulation", planarFormulationType, "Out-of-plane stress/strain formulation");
  params.addParam<NonlinearVariableName>("scalar_out_of_plane_strain",
                                         "Scalar variable for the out-of-plane strain (in y "
                                         "direction for 1D Axisymmetric or in z direction for 2D "
                                         "Cartesian problems)");
  MooseEnum outOfPlaneDirection("x y z", "z");
  params.addParam<MooseEnum>(
      "out_of_plane_direction", outOfPlaneDirection, "The direction of the out-of-plane strain.");
  params.addParam<FunctionName>("out_of_plane_pressure",
                                "0",
                                "Function used to prescribe pressure in the out-of-plane direction "
                                "(y for 1D Axisymmetric or z for 2D Cartesian problems)");
  params.addParam<Real>("pressure_factor", 1.0, "Scale factor applied to prescribed pressure");
  params.addParamNamesToGroup("planar_formulation scalar_out_of_plane_strain out_of_plane_pressure "
                              "pressure_factor out_of_plane_direction",
                              "Out-of-plane stress/strain");

  // Output
  params.addParam<MultiMooseEnum>("generate_output",
                                  TensorMechanicsActionBase::outputPropertiesType(),
                                  "Add scalar quantity output for stress and/or strain");
  params.addParamNamesToGroup("generate_output", "Output");

  return params;
}

TensorMechanicsActionBase::TensorMechanicsActionBase(const InputParameters & parameters)
  : Action(parameters)
{
  // check if a container block with common parameters is found
  auto action = _awh.getActions<CommonTensorMechanicsAction>();
  if (action.size() == 1)
    _pars.applyParameters(action[0]->parameters());

  // append additional_generate_output
  if (isParamValid("additional_generate_output"))
  {
    MultiMooseEnum generate_output = getParam<MultiMooseEnum>("generate_output");
    MultiMooseEnum additional_generate_output =
        getParam<MultiMooseEnum>("additional_generate_output");
    for (auto & output : additional_generate_output)
      generate_output.push_back(output);

    _pars.set<MultiMooseEnum>("generate_output") = generate_output;
  }
}

MultiMooseEnum
TensorMechanicsActionBase::outputPropertiesType()
{
  std::string options = "";
  for (auto & r2a : _ranktwoaux_table)
    for (unsigned int a = 0; a < 3; ++a)
      for (unsigned int b = 0; b < 3; ++b)
        options += (options == "" ? "" : " ") + r2a.first + '_' + _component_table[a] +
                   _component_table[b];

  for (auto & r2sa : _ranktwoscalaraux_table)
    for (auto & t : r2sa.second.second)
      options += " " + r2sa.first + "_" + t;

  return MultiMooseEnum(options);
}
<|endoftext|>


<commit_before>#include "inverseIndexStorageUnorderedMap.h"

InverseIndexStorageUnorderedMap::InverseIndexStorageUnorderedMap(size_t pSizeOfInverseIndex, size_t pMaxBinSize) {
	mSignatureStorage = new vector__umapVector(pSizeOfInverseIndex);
	mMaxBinSize = pMaxBinSize;
	mKeys = new vvsize_t(pSizeOfInverseIndex, vsize_t());
	mValues = new vvsize_t(pSizeOfInverseIndex, vsize_t());
}
InverseIndexStorageUnorderedMap::~InverseIndexStorageUnorderedMap() {

}
size_t InverseIndexStorageUnorderedMap::size() const {
	return mSignatureStorage->size();
}
vsize_t* InverseIndexStorageUnorderedMap::getElement(size_t pVectorId, size_t pHashValue) {
	auto iterator = (*mSignatureStorage)[pVectorId].find(pHashValue);
	if (iterator != (*mSignatureStorage)[pVectorId].end()) {
		return &(iterator->second);
	}
	return new vsize_t(0);

}
void InverseIndexStorageUnorderedMap::insert(size_t pVectorId, size_t pHashValue, size_t pInstance) {
#pragma omp critical
    {
        auto itHashValue_InstanceVector = (*mSignatureStorage)[pVectorId].find(pHashValue);
        // if for hash function h_i() the given hash values is already stored
        if (itHashValue_InstanceVector != (*mSignatureStorage)[pVectorId].end()) {
            // insert the instance id if not too many collisions (maxBinSize)
            if (itHashValue_InstanceVector->second.size() && itHashValue_InstanceVector->second.size() < mMaxBinSize) {
                // insert only if there wasn't any collisions in the past
                if (itHashValue_InstanceVector->second.size() > 0) {
                    itHashValue_InstanceVector->second.push_back(pInstance);
                }
            } else {
                // too many collisions: delete stored ids. empty vector is interpreted as an error code
                // for too many collisions
                itHashValue_InstanceVector->second.clear();
            }
        } else {
            // given hash value for the specific hash function was not avaible: insert new hash value
            vsize_t instanceIdVector;
            instanceIdVector.push_back(pInstance);
            (*mSignatureStorage)[pVectorId][pHashValue] = instanceIdVector;
        }
    }
}
<commit_msg>Added preprocessor flags and returning NULL instead of an empty vector in function getElement<commit_after>#include "inverseIndexStorageUnorderedMap.h"
#ifdef OPENMP
#include <omp.h>
#endif

InverseIndexStorageUnorderedMap::InverseIndexStorageUnorderedMap(size_t pSizeOfInverseIndex, size_t pMaxBinSize) {
	mSignatureStorage = new vector__umapVector(pSizeOfInverseIndex);
	mMaxBinSize = pMaxBinSize;
	mKeys = new vvsize_t(pSizeOfInverseIndex, vsize_t());
	mValues = new vvsize_t(pSizeOfInverseIndex, vsize_t());
}
InverseIndexStorageUnorderedMap::~InverseIndexStorageUnorderedMap() {

}
size_t InverseIndexStorageUnorderedMap::size() const {
	return mSignatureStorage->size();
}
vsize_t* InverseIndexStorageUnorderedMap::getElement(size_t pVectorId, size_t pHashValue) {
	auto iterator = (*mSignatureStorage)[pVectorId].find(pHashValue);
	if (iterator != (*mSignatureStorage)[pVectorId].end()) {
		return &(iterator->second);
	}
	return NULL;

}
void InverseIndexStorageUnorderedMap::insert(size_t pVectorId, size_t pHashValue, size_t pInstance) {
#ifdef OPENMP
#pragma omp critical
#endif
    {
        auto itHashValue_InstanceVector = (*mSignatureStorage)[pVectorId].find(pHashValue);
        // if for hash function h_i() the given hash values is already stored
        if (itHashValue_InstanceVector != (*mSignatureStorage)[pVectorId].end()) {
            // insert the instance id if not too many collisions (maxBinSize)
            if (itHashValue_InstanceVector->second.size() && itHashValue_InstanceVector->second.size() < mMaxBinSize) {
                // insert only if there wasn't any collisions in the past
                if (itHashValue_InstanceVector->second.size() > 0) {
                    itHashValue_InstanceVector->second.push_back(pInstance);
                }
            } else {
                // too many collisions: delete stored ids. empty vector is interpreted as an error code
                // for too many collisions
                itHashValue_InstanceVector->second.clear();
            }
        } else {
            // given hash value for the specific hash function was not avaible: insert new hash value
            vsize_t instanceIdVector;
            instanceIdVector.push_back(pInstance);
            (*mSignatureStorage)[pVectorId][pHashValue] = instanceIdVector;
        }
    }
}
<|endoftext|>


<commit_before>/************************************************
 *  static_vector_test.hpp
 *  bptree
 *
 *  Copyright (c) 2017, Chi-En Wu
 *  Distributed under MIT License
 ************************************************/

#include <cassert>
#include <cstddef>

#include <array>
#include <stdexcept>

#include <gtest/gtest.h>

#include <bptree/internal/static_vector.hpp>

using bptree::internal::static_vector;

enum class constructed_with {
    default_ctor,
    copy_ctor,
    move_ctor,
    value_ctor
};

class custom_type {
 public:  // Public Method(s)
    custom_type()
      : custom_type(constructed_with::default_ctor, 0)
        { /* do nothing */ }

    custom_type(custom_type const& other)
      : custom_type(constructed_with::copy_ctor, other.val_)
        { /* do nothing */ }

    custom_type(custom_type&& other)
      : custom_type(constructed_with::move_ctor, other.val_)
        { other.val_ = 0; }

    explicit custom_type(int n)
      : custom_type(constructed_with::value_ctor, n)
        { /* do nothing */ }

    // used only for generating expected results in tests
    custom_type(constructed_with ctor, int n)
      : ctor_(ctor), val_(n)
        { ++num_instances_; }

    ~custom_type()
        { --num_instances_; }

    custom_type& operator=(custom_type const& other)
        { val_ = other.val_; return *this; }

    custom_type& operator=(custom_type&& other)
        { val_ = other.val_; other.val_ = 0; return *this; }

    bool operator==(custom_type const& other) const
        { return val_ == other.val_ && ctor_ == other.ctor_; }

    int get() const
        { return val_; }

    constructed_with ctor() const
        { return ctor_; }

 public:  // Public Static Method(s)
    static custom_type construct_with_default_ctor(int val)
        { return custom_type(constructed_with::default_ctor, val); }

    static custom_type construct_with_copy_ctor(int val)
        { return custom_type(constructed_with::copy_ctor, val); }

    static custom_type construct_with_move_ctor(int val)
        { return custom_type(constructed_with::move_ctor, val); }

    static custom_type construct_with_value_ctor(int val)
        { return custom_type(constructed_with::value_ctor, val); }

    static void reset_num_instances()
        { num_instances_ = 0; }

    static std::size_t num_instances()
        { return num_instances_; }

 private:  // Private Property(ies)
    int val_;
    constructed_with ctor_;

 private:  // Private Static Property(ies)
    static std::size_t num_instances_;
};

class StaticVectorTest : public ::testing::Test {
 protected:
     virtual void SetUp() {
         custom_type::reset_num_instances();
     }
};

std::size_t custom_type::num_instances_ = 0;

#define VA_NARGS_IMPL(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, n, ...) n
#define VA_NARGS(...) VA_NARGS_IMPL(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)

#define WRAP_VALUES_IMPL_1(wrap, value) wrap(value)
#define WRAP_VALUES_IMPL_2(wrap, _1, value) \
        WRAP_VALUES_IMPL_1(wrap, _1), wrap(value)
#define WRAP_VALUES_IMPL_3(wrap, _1, _2, value) \
        WRAP_VALUES_IMPL_2(wrap, _1, _2), wrap(value)
#define WRAP_VALUES_IMPL_4(wrap, _1, _2, _3, value) \
        WRAP_VALUES_IMPL_3(wrap, _1, _2, _3), wrap(value)
#define WRAP_VALUES_IMPL_5(wrap, _1, _2, _3, _4, value) \
        WRAP_VALUES_IMPL_4(wrap, _1, _2, _3, _4), wrap(value)
#define WRAP_VALUES_IMPL_6(wrap, _1, _2, _3, _4, _5, value) \
        WRAP_VALUES_IMPL_5(wrap, _1, _2, _3, _4, _5), wrap(value)
#define WRAP_VALUES_IMPL_7(wrap, _1, _2, _3, _4, _5, _6, value) \
        WRAP_VALUES_IMPL_6(wrap, _1, _2, _3, _4, _5, _6), wrap(value)
#define WRAP_VALUES_IMPL_8(wrap, _1, _2, _3, _4, _5, _6, _7, value) \
        WRAP_VALUES_IMPL_7(wrap, _1, _2, _3, _4, _5, _6, _7), wrap(value)
#define WRAP_VALUES_IMPL_9(wrap, _1, _2, _3, _4, _5, _6, _7, _8, value) \
        WRAP_VALUES_IMPL_8(wrap, _1, _2, _3, _4, _5, _6, _7, _8), wrap(value)
#define WRAP_VALUES_IMPL_10(wrap, _1, _2, _3, _4, _5, _6, _7, _8, _9, value) \
        WRAP_VALUES_IMPL_9(wrap, _1, _2, _3, _4, _5, _6, _7, _8, _9), wrap(value)
#define WRAP_VALUES_IMPL_(n, wrap, ...) WRAP_VALUES_IMPL_##n(wrap, __VA_ARGS__)
#define WRAP_VALUES_IMPL(n, wrap, ...)  WRAP_VALUES_IMPL_(n, wrap, __VA_ARGS__)
#define WRAP_VALUES(wrap, ...)          WRAP_VALUES_IMPL(VA_NARGS(__VA_ARGS__), wrap, __VA_ARGS__)

#define REPEAT_VALUE_IMPL_1(value)      value
#define REPEAT_VALUE_IMPL_2(value)      REPEAT_VALUE_IMPL_1(value), value
#define REPEAT_VALUE_IMPL_3(value)      REPEAT_VALUE_IMPL_2(value), value
#define REPEAT_VALUE_IMPL_4(value)      REPEAT_VALUE_IMPL_3(value), value
#define REPEAT_VALUE_IMPL_5(value)      REPEAT_VALUE_IMPL_4(value), value
#define REPEAT_VALUE_IMPL_6(value)      REPEAT_VALUE_IMPL_5(value), value
#define REPEAT_VALUE_IMPL_7(value)      REPEAT_VALUE_IMPL_6(value), value
#define REPEAT_VALUE_IMPL_8(value)      REPEAT_VALUE_IMPL_7(value), value
#define REPEAT_VALUE_IMPL_9(value)      REPEAT_VALUE_IMPL_8(value), value
#define REPEAT_VALUE_IMPL_10(value)     REPEAT_VALUE_IMPL_9(value), value
#define REPEAT_VALUE_IMPL(n, value)     REPEAT_VALUE_IMPL_##n(value)
#define REPEAT_VALUE(n, value)          REPEAT_VALUE_IMPL(n, value)

#define SIZE_VECTOR                     10
#define TEST_VALUES                     1, 2, 3, 5, 8
#define EXTRA_TEST_VALUES               13, 21, 34

template <typename T, std::size_t N>
void assert_static_vector_size(static_vector<T, N> const& v, std::size_t size) {
    assert(size <= N);

    EXPECT_EQ(size == 0, v.empty());
    EXPECT_EQ(size == N, v.full());
    EXPECT_EQ(size, v.size());
    EXPECT_EQ(N, v.max_size());
    EXPECT_EQ(N, v.capacity());

    EXPECT_THROW(v.at(size), std::out_of_range);
}

template <typename T, std::size_t N, std::size_t M>
void assert_static_vector_values(static_vector<T, N> const& v, T const (&expected)[M]) {
    assert_static_vector_size(v, M);

    typename static_vector<T, N>::const_pointer data_ptr = v.data();
    for (std::size_t pos = 0; pos < M; ++pos, ++data_ptr) {
        EXPECT_EQ(expected[pos], v[pos]);
        EXPECT_EQ(expected[pos], v.at(pos));
        EXPECT_EQ(expected[pos], *data_ptr);
    }
}

TEST_F(StaticVectorTest, EmptyVector) {
    static_vector<custom_type, SIZE_VECTOR> v;

    EXPECT_EQ(0, custom_type::num_instances());
    assert_static_vector_size(v, 0);
}

TEST_F(StaticVectorTest, ConstructWithCount) {
#define COUNT 3
    static_vector<custom_type, SIZE_VECTOR> v(COUNT);

    EXPECT_EQ(COUNT, custom_type::num_instances());
    assert_static_vector_values(v, {
        REPEAT_VALUE(COUNT, custom_type(constructed_with::default_ctor, 0))
    });
#undef COUNT
}

TEST_F(StaticVectorTest, ConstructEmptyVector) {
    static_vector<custom_type, SIZE_VECTOR> v(0);

    EXPECT_EQ(0, custom_type::num_instances());
    assert_static_vector_size(v, 0);
}

TEST_F(StaticVectorTest, ConstructFullVector) {
    static_vector<custom_type, SIZE_VECTOR> v(SIZE_VECTOR);

    EXPECT_EQ(SIZE_VECTOR, custom_type::num_instances());
    assert_static_vector_values(v, {
        REPEAT_VALUE(SIZE_VECTOR, custom_type(constructed_with::default_ctor, 0))
    });
}

TEST_F(StaticVectorTest, ConstructWithCountAndValue) {
#define COUNT 3
    int const VALUE = 10;
    static_vector<custom_type, SIZE_VECTOR> v(COUNT, custom_type(VALUE));

    EXPECT_EQ(COUNT, custom_type::num_instances());
    assert_static_vector_values(v, {
        REPEAT_VALUE(COUNT, custom_type(constructed_with::copy_ctor, VALUE))
    });
#undef COUNT
}

TEST_F(StaticVectorTest, ConstructWithIteratorPair) {
    std::size_t constexpr SIZE = VA_NARGS(TEST_VALUES);
    std::array<custom_type, SIZE> arr = { WRAP_VALUES(custom_type, TEST_VALUES) };

    static_vector<custom_type, SIZE_VECTOR> v(arr.begin(), arr.end());

    EXPECT_EQ(SIZE * 2, custom_type::num_instances());
    assert_static_vector_values(v, {
        WRAP_VALUES(custom_type::construct_with_copy_ctor, TEST_VALUES)
    });
}

TEST_F(StaticVectorTest, ConstructWithInitializerList) {
    std::size_t const SIZE = VA_NARGS(TEST_VALUES);
    static_vector<custom_type, SIZE_VECTOR> v = { WRAP_VALUES(custom_type, TEST_VALUES) };

    EXPECT_EQ(SIZE, custom_type::num_instances());
    assert_static_vector_values(v, {
        WRAP_VALUES(custom_type::construct_with_copy_ctor, TEST_VALUES)
    });
}

TEST_F(StaticVectorTest, ConstructWithAnotherVector) {
    std::size_t const SIZE = VA_NARGS(TEST_VALUES);
    static_vector<custom_type, SIZE_VECTOR> v1 = { WRAP_VALUES(custom_type, TEST_VALUES) };
    static_vector<custom_type, SIZE_VECTOR> v2 = v1;

    EXPECT_EQ(SIZE * 2, custom_type::num_instances());
    assert_static_vector_values(v2, {
        WRAP_VALUES(custom_type::construct_with_copy_ctor, TEST_VALUES)
    });
}

TEST_F(StaticVectorTest, DestructValues) {
    {
        static_vector<custom_type, SIZE_VECTOR> v(3);
        EXPECT_EQ(3, custom_type::num_instances());
    }

    EXPECT_EQ(0, custom_type::num_instances());
}

TEST_F(StaticVectorTest, ClearValues) {
    static_vector<custom_type, SIZE_VECTOR> v = { WRAP_VALUES(custom_type, TEST_VALUES) };

    v.clear();

    EXPECT_EQ(0, custom_type::num_instances());
    assert_static_vector_size(v, 0);
}

TEST_F(StaticVectorTest, AssignWithOperatorAndInitializerList) {
    std::size_t const SIZE = VA_NARGS(EXTRA_TEST_VALUES);
    static_vector<custom_type, SIZE_VECTOR> v = { WRAP_VALUES(custom_type, TEST_VALUES) };
    v = { WRAP_VALUES(custom_type, EXTRA_TEST_VALUES) };

    EXPECT_EQ(SIZE, custom_type::num_instances());
    assert_static_vector_values(v, {
        WRAP_VALUES(custom_type::construct_with_copy_ctor, EXTRA_TEST_VALUES)
    });
}

TEST_F(StaticVectorTest, AssignWithOperatorAndAnotherVector) {
    std::size_t const SIZE = VA_NARGS(EXTRA_TEST_VALUES);
    static_vector<custom_type, SIZE_VECTOR> v1 = { WRAP_VALUES(custom_type, TEST_VALUES) };
    static_vector<custom_type, SIZE_VECTOR> v2 = { WRAP_VALUES(custom_type, EXTRA_TEST_VALUES) };
    v1 = v2;

    EXPECT_EQ(SIZE * 2, custom_type::num_instances());
    assert_static_vector_values(v2, {
        WRAP_VALUES(custom_type::construct_with_copy_ctor, EXTRA_TEST_VALUES)
    });
}

TEST_F(StaticVectorTest, AssignWithMethodAndCountAndValues) {
#define COUNT 3
    int const VALUE = 10;
    static_vector<custom_type, SIZE_VECTOR> v = { WRAP_VALUES(custom_type, TEST_VALUES) };
    v.assign(COUNT, custom_type(VALUE));

    EXPECT_EQ(COUNT, custom_type::num_instances());
    assert_static_vector_values(v, {
        REPEAT_VALUE(COUNT, custom_type(constructed_with::copy_ctor, VALUE))
    });
#undef COUNT
}
<commit_msg>:art: Distinguish variable names from macro<commit_after>/************************************************
 *  static_vector_test.hpp
 *  bptree
 *
 *  Copyright (c) 2017, Chi-En Wu
 *  Distributed under MIT License
 ************************************************/

#include <cassert>
#include <cstddef>

#include <array>
#include <stdexcept>

#include <gtest/gtest.h>

#include <bptree/internal/static_vector.hpp>

using bptree::internal::static_vector;

enum class constructed_with {
    default_ctor,
    copy_ctor,
    move_ctor,
    value_ctor
};

class custom_type {
 public:  // Public Method(s)
    custom_type()
      : custom_type(constructed_with::default_ctor, 0)
        { /* do nothing */ }

    custom_type(custom_type const& other)
      : custom_type(constructed_with::copy_ctor, other.val_)
        { /* do nothing */ }

    custom_type(custom_type&& other)
      : custom_type(constructed_with::move_ctor, other.val_)
        { other.val_ = 0; }

    explicit custom_type(int n)
      : custom_type(constructed_with::value_ctor, n)
        { /* do nothing */ }

    // used only for generating expected results in tests
    custom_type(constructed_with ctor, int n)
      : ctor_(ctor), val_(n)
        { ++num_instances_; }

    ~custom_type()
        { --num_instances_; }

    custom_type& operator=(custom_type const& other)
        { val_ = other.val_; return *this; }

    custom_type& operator=(custom_type&& other)
        { val_ = other.val_; other.val_ = 0; return *this; }

    bool operator==(custom_type const& other) const
        { return val_ == other.val_ && ctor_ == other.ctor_; }

    int get() const
        { return val_; }

    constructed_with ctor() const
        { return ctor_; }

 public:  // Public Static Method(s)
    static custom_type construct_with_default_ctor(int val)
        { return custom_type(constructed_with::default_ctor, val); }

    static custom_type construct_with_copy_ctor(int val)
        { return custom_type(constructed_with::copy_ctor, val); }

    static custom_type construct_with_move_ctor(int val)
        { return custom_type(constructed_with::move_ctor, val); }

    static custom_type construct_with_value_ctor(int val)
        { return custom_type(constructed_with::value_ctor, val); }

    static void reset_num_instances()
        { num_instances_ = 0; }

    static std::size_t num_instances()
        { return num_instances_; }

 private:  // Private Property(ies)
    int val_;
    constructed_with ctor_;

 private:  // Private Static Property(ies)
    static std::size_t num_instances_;
};

class StaticVectorTest : public ::testing::Test {
 protected:
     virtual void SetUp() {
         custom_type::reset_num_instances();
     }
};

std::size_t custom_type::num_instances_ = 0;

#define VA_NARGS_IMPL(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, n, ...) n
#define VA_NARGS(...) VA_NARGS_IMPL(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)

#define WRAP_VALUES_IMPL_1(wrap, value) wrap(value)
#define WRAP_VALUES_IMPL_2(wrap, _1, value) \
        WRAP_VALUES_IMPL_1(wrap, _1), wrap(value)
#define WRAP_VALUES_IMPL_3(wrap, _1, _2, value) \
        WRAP_VALUES_IMPL_2(wrap, _1, _2), wrap(value)
#define WRAP_VALUES_IMPL_4(wrap, _1, _2, _3, value) \
        WRAP_VALUES_IMPL_3(wrap, _1, _2, _3), wrap(value)
#define WRAP_VALUES_IMPL_5(wrap, _1, _2, _3, _4, value) \
        WRAP_VALUES_IMPL_4(wrap, _1, _2, _3, _4), wrap(value)
#define WRAP_VALUES_IMPL_6(wrap, _1, _2, _3, _4, _5, value) \
        WRAP_VALUES_IMPL_5(wrap, _1, _2, _3, _4, _5), wrap(value)
#define WRAP_VALUES_IMPL_7(wrap, _1, _2, _3, _4, _5, _6, value) \
        WRAP_VALUES_IMPL_6(wrap, _1, _2, _3, _4, _5, _6), wrap(value)
#define WRAP_VALUES_IMPL_8(wrap, _1, _2, _3, _4, _5, _6, _7, value) \
        WRAP_VALUES_IMPL_7(wrap, _1, _2, _3, _4, _5, _6, _7), wrap(value)
#define WRAP_VALUES_IMPL_9(wrap, _1, _2, _3, _4, _5, _6, _7, _8, value) \
        WRAP_VALUES_IMPL_8(wrap, _1, _2, _3, _4, _5, _6, _7, _8), wrap(value)
#define WRAP_VALUES_IMPL_10(wrap, _1, _2, _3, _4, _5, _6, _7, _8, _9, value) \
        WRAP_VALUES_IMPL_9(wrap, _1, _2, _3, _4, _5, _6, _7, _8, _9), wrap(value)
#define WRAP_VALUES_IMPL_(n, wrap, ...) WRAP_VALUES_IMPL_##n(wrap, __VA_ARGS__)
#define WRAP_VALUES_IMPL(n, wrap, ...)  WRAP_VALUES_IMPL_(n, wrap, __VA_ARGS__)
#define WRAP_VALUES(wrap, ...)          WRAP_VALUES_IMPL(VA_NARGS(__VA_ARGS__), wrap, __VA_ARGS__)

#define REPEAT_VALUE_IMPL_1(value)      value
#define REPEAT_VALUE_IMPL_2(value)      REPEAT_VALUE_IMPL_1(value), value
#define REPEAT_VALUE_IMPL_3(value)      REPEAT_VALUE_IMPL_2(value), value
#define REPEAT_VALUE_IMPL_4(value)      REPEAT_VALUE_IMPL_3(value), value
#define REPEAT_VALUE_IMPL_5(value)      REPEAT_VALUE_IMPL_4(value), value
#define REPEAT_VALUE_IMPL_6(value)      REPEAT_VALUE_IMPL_5(value), value
#define REPEAT_VALUE_IMPL_7(value)      REPEAT_VALUE_IMPL_6(value), value
#define REPEAT_VALUE_IMPL_8(value)      REPEAT_VALUE_IMPL_7(value), value
#define REPEAT_VALUE_IMPL_9(value)      REPEAT_VALUE_IMPL_8(value), value
#define REPEAT_VALUE_IMPL_10(value)     REPEAT_VALUE_IMPL_9(value), value
#define REPEAT_VALUE_IMPL(n, value)     REPEAT_VALUE_IMPL_##n(value)
#define REPEAT_VALUE(n, value)          REPEAT_VALUE_IMPL(n, value)

#define SIZE_VECTOR                     10
#define TEST_VALUES                     1, 2, 3, 5, 8
#define EXTRA_TEST_VALUES               13, 21, 34

template <typename T, std::size_t N>
void assert_static_vector_size(static_vector<T, N> const& v, std::size_t size) {
    assert(size <= N);

    EXPECT_EQ(size == 0, v.empty());
    EXPECT_EQ(size == N, v.full());
    EXPECT_EQ(size, v.size());
    EXPECT_EQ(N, v.max_size());
    EXPECT_EQ(N, v.capacity());

    EXPECT_THROW(v.at(size), std::out_of_range);
}

template <typename T, std::size_t N, std::size_t M>
void assert_static_vector_values(static_vector<T, N> const& v, T const (&expected)[M]) {
    assert_static_vector_size(v, M);

    typename static_vector<T, N>::const_pointer data_ptr = v.data();
    for (std::size_t pos = 0; pos < M; ++pos, ++data_ptr) {
        EXPECT_EQ(expected[pos], v[pos]);
        EXPECT_EQ(expected[pos], v.at(pos));
        EXPECT_EQ(expected[pos], *data_ptr);
    }
}

TEST_F(StaticVectorTest, EmptyVector) {
    static_vector<custom_type, SIZE_VECTOR> v;

    EXPECT_EQ(0, custom_type::num_instances());
    assert_static_vector_size(v, 0);
}

TEST_F(StaticVectorTest, ConstructWithCount) {
#define COUNT 3
    static_vector<custom_type, SIZE_VECTOR> v(COUNT);

    EXPECT_EQ(COUNT, custom_type::num_instances());
    assert_static_vector_values(v, {
        REPEAT_VALUE(COUNT, custom_type(constructed_with::default_ctor, 0))
    });
#undef COUNT
}

TEST_F(StaticVectorTest, ConstructEmptyVector) {
    static_vector<custom_type, SIZE_VECTOR> v(0);

    EXPECT_EQ(0, custom_type::num_instances());
    assert_static_vector_size(v, 0);
}

TEST_F(StaticVectorTest, ConstructFullVector) {
    static_vector<custom_type, SIZE_VECTOR> v(SIZE_VECTOR);

    EXPECT_EQ(SIZE_VECTOR, custom_type::num_instances());
    assert_static_vector_values(v, {
        REPEAT_VALUE(SIZE_VECTOR, custom_type(constructed_with::default_ctor, 0))
    });
}

TEST_F(StaticVectorTest, ConstructWithCountAndValue) {
#define COUNT 3
    int const value = 10;
    static_vector<custom_type, SIZE_VECTOR> v(COUNT, custom_type(value));

    EXPECT_EQ(COUNT, custom_type::num_instances());
    assert_static_vector_values(v, {
        REPEAT_VALUE(COUNT, custom_type(constructed_with::copy_ctor, value))
    });
#undef COUNT
}

TEST_F(StaticVectorTest, ConstructWithIteratorPair) {
    std::size_t constexpr size = VA_NARGS(TEST_VALUES);
    std::array<custom_type, size> arr = { WRAP_VALUES(custom_type, TEST_VALUES) };

    static_vector<custom_type, SIZE_VECTOR> v(arr.begin(), arr.end());

    EXPECT_EQ(size * 2, custom_type::num_instances());
    assert_static_vector_values(v, {
        WRAP_VALUES(custom_type::construct_with_copy_ctor, TEST_VALUES)
    });
}

TEST_F(StaticVectorTest, ConstructWithInitializerList) {
    std::size_t const size = VA_NARGS(TEST_VALUES);
    static_vector<custom_type, SIZE_VECTOR> v = { WRAP_VALUES(custom_type, TEST_VALUES) };

    EXPECT_EQ(size, custom_type::num_instances());
    assert_static_vector_values(v, {
        WRAP_VALUES(custom_type::construct_with_copy_ctor, TEST_VALUES)
    });
}

TEST_F(StaticVectorTest, ConstructWithAnotherVector) {
    std::size_t const size = VA_NARGS(TEST_VALUES);
    static_vector<custom_type, SIZE_VECTOR> v1 = { WRAP_VALUES(custom_type, TEST_VALUES) };
    static_vector<custom_type, SIZE_VECTOR> v2 = v1;

    EXPECT_EQ(size * 2, custom_type::num_instances());
    assert_static_vector_values(v2, {
        WRAP_VALUES(custom_type::construct_with_copy_ctor, TEST_VALUES)
    });
}

TEST_F(StaticVectorTest, DestructValues) {
    {
        static_vector<custom_type, SIZE_VECTOR> v(3);
        EXPECT_EQ(3, custom_type::num_instances());
    }

    EXPECT_EQ(0, custom_type::num_instances());
}

TEST_F(StaticVectorTest, ClearValues) {
    static_vector<custom_type, SIZE_VECTOR> v = { WRAP_VALUES(custom_type, TEST_VALUES) };

    v.clear();

    EXPECT_EQ(0, custom_type::num_instances());
    assert_static_vector_size(v, 0);
}

TEST_F(StaticVectorTest, AssignWithOperatorAndInitializerList) {
    std::size_t const size = VA_NARGS(EXTRA_TEST_VALUES);
    static_vector<custom_type, SIZE_VECTOR> v = { WRAP_VALUES(custom_type, TEST_VALUES) };
    v = { WRAP_VALUES(custom_type, EXTRA_TEST_VALUES) };

    EXPECT_EQ(size, custom_type::num_instances());
    assert_static_vector_values(v, {
        WRAP_VALUES(custom_type::construct_with_copy_ctor, EXTRA_TEST_VALUES)
    });
}

TEST_F(StaticVectorTest, AssignWithOperatorAndAnotherVector) {
    std::size_t const size = VA_NARGS(EXTRA_TEST_VALUES);
    static_vector<custom_type, SIZE_VECTOR> v1 = { WRAP_VALUES(custom_type, TEST_VALUES) };
    static_vector<custom_type, SIZE_VECTOR> v2 = { WRAP_VALUES(custom_type, EXTRA_TEST_VALUES) };
    v1 = v2;

    EXPECT_EQ(size * 2, custom_type::num_instances());
    assert_static_vector_values(v2, {
        WRAP_VALUES(custom_type::construct_with_copy_ctor, EXTRA_TEST_VALUES)
    });
}

TEST_F(StaticVectorTest, AssignWithMethodAndCountAndValues) {
#define COUNT 3
    int const value = 10;
    static_vector<custom_type, SIZE_VECTOR> v = { WRAP_VALUES(custom_type, TEST_VALUES) };
    v.assign(COUNT, custom_type(value));

    EXPECT_EQ(COUNT, custom_type::num_instances());
    assert_static_vector_values(v, {
        REPEAT_VALUE(COUNT, custom_type(constructed_with::copy_ctor, value))
    });
#undef COUNT
}
<|endoftext|>


<commit_before>/************************************************
 *  string_bucket_test.cpp
 *  HAT-Trie
 *
 *  Copyright (c) 2015, Chi-En Wu
 *  Distributed under The BSD 3-Clause License
 ************************************************/

#include <string>
#include <stdexcept>

#include <gtest/gtest.h>

#include "hat_trie/string_bucket.hpp"

using std::string;
using std::out_of_range;
using hat_trie::impl::string_bucket;

struct some_type
{
    explicit some_type(bool &alive) : alive(alive)
                        { alive = true; }
    ~some_type(void)    { alive = false; }
    bool &alive;
};

void construct_string_bucket(string_bucket<uint32_t> &sb)
{
    sb.push_back("cat", 1);
    sb.push_back("elephant", 3);
    sb.push_back("rabbit", 5);
    sb.push_back("bird", 7);
    sb.push_back("horse", 9);
}

void check_key_and_value(string_bucket<uint32_t>::iterator const &it,
                         string const &expect_key, uint32_t expect_val)
{
    string key(it->get_key().begin(), it->get_key().end());
    EXPECT_EQ(expect_key, key);
    EXPECT_EQ(expect_val, it->get_value());
}

TEST(StringBucket, CreateEmptyBucket)
{
    string_bucket<uint32_t> sb;
    EXPECT_TRUE(sb.empty());
    EXPECT_EQ(0, sb.size());
    EXPECT_EQ(0, sb.bytes_count());
    EXPECT_EQ(sb.end(), sb.begin());
}

TEST(StringBucket, InsertKeyAndValue)
{
    string_bucket<uint32_t> sb;

    auto it1 = sb.push_back("hello", 3);
    check_key_and_value(it1, "hello", 3);
    EXPECT_EQ(1, sb.size());
    EXPECT_EQ(10, sb.bytes_count()); // 1 (prefix) + 5 (key) + 4 (value) = 10 bytes
    EXPECT_FALSE(sb.empty());

    auto it2 = sb.push_back("hi", 5);
    check_key_and_value(it2, "hi", 5);
    EXPECT_EQ(2, sb.size());
    EXPECT_EQ(17, sb.bytes_count()); // 10 + 1 (prefix) + 2 (key) + 4 (value) = 17 bytes
}

TEST(StringBucket, TraverseBucket)
{
    string_bucket<uint32_t> sb;
    construct_string_bucket(sb);

    auto it = sb.begin();
    check_key_and_value(it, "cat", 1);
    check_key_and_value(++it, "elephant", 3);
    check_key_and_value(++it, "rabbit", 5);
    check_key_and_value(++it, "bird", 7);
    check_key_and_value(++it, "horse", 9);
    EXPECT_EQ(sb.end(), ++it);
}

TEST(StringBucket, FindElementWithKey)
{
    string_bucket<uint32_t> sb;
    construct_string_bucket(sb);

    auto it1 = sb.find("rabbit");
    check_key_and_value(it1, "rabbit", 5);

    auto it2 = sb.find("horse");
    check_key_and_value(it2, "horse", 9);

    auto it3 = sb.find("tiger");
    EXPECT_EQ(sb.end(), it3);
}

TEST(StringBucket, AccessElement)
{
    string_bucket<uint32_t> sb;
    construct_string_bucket(sb);

    EXPECT_EQ(1, sb["cat"]);
    EXPECT_EQ(7, sb["bird"]);

    EXPECT_EQ(3, sb.at("elephant"));
    EXPECT_EQ(5, sb.at("rabbit"));

    EXPECT_THROW(sb.at("tiger"), out_of_range);
}

TEST(StringBucket, EraseElement)
{
    string_bucket<uint32_t> sb;
    construct_string_bucket(sb);

    auto it1 = sb.find("cat");
    auto next_it1 = sb.erase(it1);
    check_key_and_value(next_it1, "elephant", 3);

    auto it2 = sb.find("rabbit");
    auto next_it2 = sb.erase(it2);
    check_key_and_value(next_it2, "bird", 7);

    auto it3 = sb.find("horse");
    auto next_it = sb.erase(it3);
    EXPECT_EQ(sb.end(), next_it);

    EXPECT_EQ(2, sb.size());

    auto it = sb.begin();
    check_key_and_value(it, "elephant", 3);
    check_key_and_value(++it, "bird", 7);
    EXPECT_EQ(sb.end(), ++it);
}

TEST(StringBucket, ClearBucket)
{
    string_bucket<uint32_t> sb;
    construct_string_bucket(sb);

    sb.clear();
    EXPECT_TRUE(sb.empty());
    EXPECT_EQ(0, sb.size());
    EXPECT_EQ(0, sb.bytes_count());
    EXPECT_EQ(sb.end(), sb.begin());
}

TEST(StringBucket, ConstructAndDestructObject)
{
    bool alive = false;
    {
        string_bucket<some_type> sb;
        sb.emplace_back("key", alive);
        EXPECT_TRUE(alive);
    }

    EXPECT_FALSE(alive);
}

TEST(StringBucket, DestructObjectWhenErasingElement)
{
    string_bucket<some_type> sb;
    bool alive = false;
    auto it = sb.emplace_back("key", alive);
    sb.erase(it);
    EXPECT_FALSE(alive);
}

// TODO: Add some test cases for inserting/erasing objects of non-trivially
//       copyable type. We need to check whether their copy/move constructor
//       is called when re-allocating memory.
<commit_msg>Check whether copy/move constructor is called when re-allocating memory<commit_after>/************************************************
 *  string_bucket_test.cpp
 *  HAT-Trie
 *
 *  Copyright (c) 2015, Chi-En Wu
 *  Distributed under The BSD 3-Clause License
 ************************************************/

#include <string>
#include <stdexcept>

#include <gtest/gtest.h>

#include "hat_trie/string_bucket.hpp"

using std::string;
using std::out_of_range;
using hat_trie::impl::string_bucket;

struct some_type
{
    explicit some_type(bool &alive) : alive(alive)
                        { alive = true; }
    ~some_type(void)    { alive = false; }
    bool &alive;
};

struct spy
{
    size_t move_count, copy_count;
};

struct some_type_with_non_trival_copy_constructor
{
    explicit some_type_with_non_trival_copy_constructor(spy &c) : c(c)
        { c.move_count = c.copy_count = 0; }
    some_type_with_non_trival_copy_constructor(
            some_type_with_non_trival_copy_constructor const &obj) : c(obj.c)
        { c.copy_count++; }
    some_type_with_non_trival_copy_constructor(
            some_type_with_non_trival_copy_constructor &&obj) : c(obj.c)
        { c.move_count++; }

    spy &c;
};

struct some_type_with_noexcept_move_constructor
{
    explicit some_type_with_noexcept_move_constructor(spy &c) : c(c)
        { c.move_count = c.copy_count = 0; }
    some_type_with_noexcept_move_constructor(
            some_type_with_noexcept_move_constructor const &obj) : c(obj.c)
        { c.copy_count++; }
    some_type_with_noexcept_move_constructor(
            some_type_with_noexcept_move_constructor &&obj) noexcept : c(obj.c)
        { c.move_count++; }

    spy &c;
};

void construct_string_bucket(string_bucket<uint32_t> &sb)
{
    sb.push_back("cat", 1);
    sb.push_back("elephant", 3);
    sb.push_back("rabbit", 5);
    sb.push_back("bird", 7);
    sb.push_back("horse", 9);
}

void check_key_and_value(string_bucket<uint32_t>::iterator const &it,
                         string const &expect_key, uint32_t expect_val)
{
    string key(it->get_key().begin(), it->get_key().end());
    EXPECT_EQ(expect_key, key);
    EXPECT_EQ(expect_val, it->get_value());
}

TEST(StringBucket, CreateEmptyBucket)
{
    string_bucket<uint32_t> sb;
    EXPECT_TRUE(sb.empty());
    EXPECT_EQ(0, sb.size());
    EXPECT_EQ(0, sb.bytes_count());
    EXPECT_EQ(sb.end(), sb.begin());
}

TEST(StringBucket, InsertKeyAndValue)
{
    string_bucket<uint32_t> sb;

    auto it1 = sb.push_back("hello", 3);
    check_key_and_value(it1, "hello", 3);
    EXPECT_EQ(1, sb.size());
    EXPECT_EQ(10, sb.bytes_count()); // 1 (prefix) + 5 (key) + 4 (value) = 10 bytes
    EXPECT_FALSE(sb.empty());

    auto it2 = sb.push_back("hi", 5);
    check_key_and_value(it2, "hi", 5);
    EXPECT_EQ(2, sb.size());
    EXPECT_EQ(17, sb.bytes_count()); // 10 + 1 (prefix) + 2 (key) + 4 (value) = 17 bytes
}

TEST(StringBucket, TraverseBucket)
{
    string_bucket<uint32_t> sb;
    construct_string_bucket(sb);

    auto it = sb.begin();
    check_key_and_value(it, "cat", 1);
    check_key_and_value(++it, "elephant", 3);
    check_key_and_value(++it, "rabbit", 5);
    check_key_and_value(++it, "bird", 7);
    check_key_and_value(++it, "horse", 9);
    EXPECT_EQ(sb.end(), ++it);
}

TEST(StringBucket, FindElementWithKey)
{
    string_bucket<uint32_t> sb;
    construct_string_bucket(sb);

    auto it1 = sb.find("rabbit");
    check_key_and_value(it1, "rabbit", 5);

    auto it2 = sb.find("horse");
    check_key_and_value(it2, "horse", 9);

    auto it3 = sb.find("tiger");
    EXPECT_EQ(sb.end(), it3);
}

TEST(StringBucket, AccessElement)
{
    string_bucket<uint32_t> sb;
    construct_string_bucket(sb);

    EXPECT_EQ(1, sb["cat"]);
    EXPECT_EQ(7, sb["bird"]);

    EXPECT_EQ(3, sb.at("elephant"));
    EXPECT_EQ(5, sb.at("rabbit"));

    EXPECT_THROW(sb.at("tiger"), out_of_range);
}

TEST(StringBucket, EraseElement)
{
    string_bucket<uint32_t> sb;
    construct_string_bucket(sb);

    auto it1 = sb.find("cat");
    auto next_it1 = sb.erase(it1);
    check_key_and_value(next_it1, "elephant", 3);

    auto it2 = sb.find("rabbit");
    auto next_it2 = sb.erase(it2);
    check_key_and_value(next_it2, "bird", 7);

    auto it3 = sb.find("horse");
    auto next_it = sb.erase(it3);
    EXPECT_EQ(sb.end(), next_it);

    EXPECT_EQ(2, sb.size());

    auto it = sb.begin();
    check_key_and_value(it, "elephant", 3);
    check_key_and_value(++it, "bird", 7);
    EXPECT_EQ(sb.end(), ++it);
}

TEST(StringBucket, ClearBucket)
{
    string_bucket<uint32_t> sb;
    construct_string_bucket(sb);

    sb.clear();
    EXPECT_TRUE(sb.empty());
    EXPECT_EQ(0, sb.size());
    EXPECT_EQ(0, sb.bytes_count());
    EXPECT_EQ(sb.end(), sb.begin());
}

TEST(StringBucket, ConstructAndDestructObject)
{
    bool alive = false;
    {
        string_bucket<some_type> sb;
        sb.emplace_back("key", alive);
        EXPECT_TRUE(alive);
    }

    EXPECT_FALSE(alive);
}

TEST(StringBucket, DestructObjectWhenErasingElement)
{
    string_bucket<some_type> sb;
    bool alive = false;
    auto it = sb.emplace_back("key", alive);
    sb.erase(it);
    EXPECT_FALSE(alive);
}

TEST(StringBucket, InsertRValueReference)
{
    string_bucket<some_type_with_non_trival_copy_constructor> sb;
    spy c;
    sb.push_back("key", some_type_with_non_trival_copy_constructor(c));
    EXPECT_EQ(0, c.copy_count);
    EXPECT_EQ(1, c.move_count);
}

TEST(StringBucket, InsertLValueReference)
{
    string_bucket<some_type_with_non_trival_copy_constructor> sb;
    spy c;
    some_type_with_non_trival_copy_constructor obj(c);
    sb.push_back("key", obj);
    EXPECT_EQ(1, c.copy_count);
    EXPECT_EQ(0, c.move_count);
}

TEST(StringBucket, CopyObjectWhenReallocatingMemory)
{
    string_bucket<some_type_with_non_trival_copy_constructor> sb;
    spy c1, c2;
    sb.emplace_back("key1", c1);
    sb.emplace_back("key2", c2);
    EXPECT_EQ(1, c1.copy_count);
    EXPECT_EQ(0, c1.move_count);
}

TEST(StringBucket, MoveObjectWhenReallocatingMemory)
{
    string_bucket<some_type_with_noexcept_move_constructor> sb;
    spy c1, c2;
    sb.emplace_back("key1", c1);
    sb.emplace_back("key2", c2);
    EXPECT_EQ(0, c1.copy_count);
    EXPECT_EQ(1, c1.move_count);
}
<|endoftext|>


<commit_before>/* tensorimaging tests */

#include <iostream>
#include <functional>
#include <cmath>
#include "unittest.hxx"
#include "vigra/stdimage.hxx"
#include "vigra/impex.hxx"
#include "vigra/tensorutilities.hxx"
#include "vigra/orientedtensorfilters.hxx"
#include "vigra/boundarytensor.hxx"
#include "vigra/gradient_energy_tensor.hxx"

using namespace vigra;

struct TensorUtilityTest
{
    typedef vigra::DImage Image;
    typedef vigra::DVector2Image V2Image;
    typedef vigra::DVector3Image V3Image;

    TensorUtilityTest()
    : w(5), h(5),
      img2(w, h), img3(w, h)
    {
        for(int y = 0; y < h; ++y)
        {
            for(int x = 0; x < w; ++x)
            {
                img2(x,y)[0] = x;
                img2(x,y)[1] = y;
            }
        }
        vectorToTensor(srcImageRange(img2), destImage(img3));
    }

    void vector2TensorTest()
    {
        for(int y = 0; y < h; ++y)
        {
            for(int x = 0; x < w; ++x)
            {
                shouldEqual(img3(x,y)[0], x*x);
                shouldEqual(img3(x,y)[1], -x*y);
                shouldEqual(img3(x,y)[2], y*y);
            }
        }
    }

    void tensorEigenRepresentationTest()
    {
        V3Image res(img3.size());
        tensorEigenRepresentation(srcImageRange(img3), destImage(res));

        for(int y = 0; y < h; ++y)
        {
            for(int x = 0; x < w; ++x)
            {
                shouldEqual(res(x,y)[0]+res(x,y)[1], img3(x,y)[0] + img3(x,y)[2]);
                shouldEqual(res(x,y)[0]*res(x,y)[1],
                            img3(x,y)[0]*img3(x,y)[2]-sq(img3(x,y)[1]));
                shouldEqualTolerance(res(x,y)[2],
                                    VIGRA_CSTD::atan2((double)-y, (double)x), 1e-12);
            }
        }
    }

    void tensorTraceTest()
    {
        Image res(img3.size());
        tensorTrace(srcImageRange(img3), destImage(res));

        for(int y = 0; y < h; ++y)
        {
            for(int x = 0; x < w; ++x)
            {
                shouldEqual(res(x,y), img3(x,y)[0] + img3(x,y)[2]);
            }
        }
    }

    void tensorToEdgeCornerTest()
    {
        V2Image edge(img3.size());
        Image corner(img3.size());
        tensorToEdgeCorner(srcImageRange(img3), destImage(edge), destImage(corner));
        V3Image eigen(img3.size());
        tensorEigenRepresentation(srcImageRange(img3), destImage(eigen));

        for(int y = 0; y < h; ++y)
        {
            for(int x = 0; x < w; ++x)
            {
                shouldEqual(edge(x,y)[0], eigen(x,y)[0]-eigen(x,y)[1]);
                shouldEqual(edge(x,y)[1], eigen(x,y)[2]);
                shouldEqual(corner(x,y), 2.0*eigen(x,y)[1]);
            }
        }
    }

    int w, h;
    V2Image img2;
    V3Image img3;
};

#define defImpulseResponseTest(xorder, yorder) \
    void rieszTransform##xorder##yorder##Test() \
    { \
        Image res(img1.size()), ref(img1.size()); \
        ImageImportInfo iref("riesz" #xorder #yorder ".xv"); \
        importImage(iref, destImage(ref)); \
        \
        rieszTransformOfLOG(srcImageRange(img1), destImage(res), 2.0, xorder, yorder); \
        \
        shouldEqualSequenceTolerance(res.begin(), res.end(), ref.begin(), 1e-12); \
    } \

struct EdgeJunctionTensorTest
{
    typedef vigra::DImage Image;
    typedef vigra::DVector2Image V2Image;
    typedef vigra::DVector3Image V3Image;

    EdgeJunctionTensorTest()
    : img1(17, 17)
    {
        img1.init(0.0);
        img1(8,8) = 1.0;

        ImageImportInfo i2("l2.xv");
        img2.resize(i2.size());
        importImage(i2, destImage(img2));
    }

    defImpulseResponseTest(0, 0)
    defImpulseResponseTest(1, 0)
    defImpulseResponseTest(0, 1)
    defImpulseResponseTest(2, 0)
    defImpulseResponseTest(1, 1)
    defImpulseResponseTest(0, 2)

    void boundaryTensorTest0()
    {
        V3Image res(img1.size()), ref(img1.size());
        ImageImportInfo iref("boundaryTensor.xv");
        importImage(iref, destImage(ref));

        boundaryTensor(srcImageRange(img1), destImage(res), 2.0);

        for(V3Image::iterator i = res.begin(), j = ref.begin(); i < res.end(); ++i, ++j)
        {
            shouldEqualTolerance(i->magnitude(), j->magnitude(), 1e-12);
        }
    }

    void boundaryTensorTest1()
    {
        V3Image bt(img2.size());
        Image res(img2.size()), ref(img2.size());
        ImageImportInfo iref("l2_boundary1.xv");
        importImage(iref, destImage(ref));

        boundaryTensor1(srcImageRange(img2), destImage(bt), 2.0);
        tensorTrace(srcImageRange(bt), destImage(res));

        shouldEqualSequenceTolerance(res.begin(), res.end(), ref.begin(), 1e-12);
    }

    void boundaryTensorTest2()
    {
        V3Image bt(img2.size());
        Image res(img2.size()), ref(img2.size());
        ImageImportInfo iref("l2_boundary.xv");
        importImage(iref, destImage(ref));

        boundaryTensor(srcImageRange(img2), destImage(bt), 2.0);
        tensorTrace(srcImageRange(bt), destImage(res));

        shouldEqualSequenceTolerance(res.begin(), res.end(), ref.begin(), 1e-12);
    }

    void boundaryTensorTest3()
    {
        // does not produce the correct result
        Image res(img2.size());
        {
            V3Image bt(img2.size()), bt2(img2.size());

            boundaryTensor3(srcImageRange(img2), destImage(bt), destImage(bt2), 1.0);
            combineTwoImages(srcImageRange(bt), srcImage(bt2), destImage(bt2),
                         std::plus<V3Image::value_type>());
            tensorTrace(srcImageRange(bt2), destImage(res));
        }
        Image ref(img2.size());
        ImageImportInfo iref("l2_boundary3.xv");
        importImage(iref, destImage(ref));

        shouldEqualSequenceTolerance(res.begin(), res.end(), ref.begin(), 1e-12);
    }

    void hourglassTest()
    {
        V2Image gradient(img2.size());
        V3Image tensor(img2.size()), smoothedTensor(img2.size());
        Image res(img2.size()), ref(img2.size());
        ImageImportInfo iref("l2_hourglass.xv");
        importImage(iref, destImage(ref));

        gaussianGradient(srcImageRange(img2), destImage(gradient), 0.7);
        vectorToTensor(srcImageRange(gradient), destImage(tensor));
        hourGlassFilter(srcImageRange(tensor), destImage(smoothedTensor), 2.8, 0.4);
        tensorTrace(srcImageRange(smoothedTensor), destImage(res));

        shouldEqualSequenceTolerance(res.begin(), res.end(), ref.begin(), 1e-12);
    }

    void energyTensorTest()
    {
        V3Image get(img2.size());
        Image res(img2.size()), ref(img2.size());
        ImageImportInfo iref("l2_get.xv");
        importImage(iref, destImage(ref));

        Kernel1D<double> smooth, grad;
        smooth.initGaussian(1.0);
        grad.initGaussianDerivative(1.0, 1);
        gradientEnergyTensor(srcImageRange(img2), destImage(get), grad, smooth);
        tensorTrace(srcImageRange(get), destImage(res));

        shouldEqualSequenceTolerance(res.begin(), res.end(), ref.begin(), 1e-12);
    }

    Image img1, img2;
};

struct TensorTestSuite
: public vigra::test_suite
{
    TensorTestSuite()
    : vigra::test_suite("TensorTestSuite")
    {
        add( testCase( &TensorUtilityTest::vector2TensorTest));
        add( testCase( &TensorUtilityTest::tensorEigenRepresentationTest));
        add( testCase( &TensorUtilityTest::tensorTraceTest));
        add( testCase( &TensorUtilityTest::tensorToEdgeCornerTest));

        add( testCase( &EdgeJunctionTensorTest::rieszTransform00Test));
        add( testCase( &EdgeJunctionTensorTest::rieszTransform10Test));
        add( testCase( &EdgeJunctionTensorTest::rieszTransform01Test));
        add( testCase( &EdgeJunctionTensorTest::rieszTransform20Test));
        add( testCase( &EdgeJunctionTensorTest::rieszTransform11Test));
        add( testCase( &EdgeJunctionTensorTest::rieszTransform02Test));
        add( testCase( &EdgeJunctionTensorTest::boundaryTensorTest0));
        add( testCase( &EdgeJunctionTensorTest::boundaryTensorTest1));
        add( testCase( &EdgeJunctionTensorTest::boundaryTensorTest2));
        add( testCase( &EdgeJunctionTensorTest::hourglassTest));
        add( testCase( &EdgeJunctionTensorTest::energyTensorTest));
    }
};

int main()
{
    TensorTestSuite test;

    int failed = test.run();

    std::cout << test.report() << std::endl;

    return (failed != 0);
}

<commit_msg>addjusted error bound for energy tensor test<commit_after>/* tensorimaging tests */

#include <iostream>
#include <functional>
#include <cmath>
#include "unittest.hxx"
#include "vigra/stdimage.hxx"
#include "vigra/impex.hxx"
#include "vigra/tensorutilities.hxx"
#include "vigra/orientedtensorfilters.hxx"
#include "vigra/boundarytensor.hxx"
#include "vigra/gradient_energy_tensor.hxx"

using namespace vigra;

struct TensorUtilityTest
{
    typedef vigra::DImage Image;
    typedef vigra::DVector2Image V2Image;
    typedef vigra::DVector3Image V3Image;

    TensorUtilityTest()
    : w(5), h(5),
      img2(w, h), img3(w, h)
    {
        for(int y = 0; y < h; ++y)
        {
            for(int x = 0; x < w; ++x)
            {
                img2(x,y)[0] = x;
                img2(x,y)[1] = y;
            }
        }
        vectorToTensor(srcImageRange(img2), destImage(img3));
    }

    void vector2TensorTest()
    {
        for(int y = 0; y < h; ++y)
        {
            for(int x = 0; x < w; ++x)
            {
                shouldEqual(img3(x,y)[0], x*x);
                shouldEqual(img3(x,y)[1], -x*y);
                shouldEqual(img3(x,y)[2], y*y);
            }
        }
    }

    void tensorEigenRepresentationTest()
    {
        V3Image res(img3.size());
        tensorEigenRepresentation(srcImageRange(img3), destImage(res));

        for(int y = 0; y < h; ++y)
        {
            for(int x = 0; x < w; ++x)
            {
                shouldEqual(res(x,y)[0]+res(x,y)[1], img3(x,y)[0] + img3(x,y)[2]);
                shouldEqual(res(x,y)[0]*res(x,y)[1],
                            img3(x,y)[0]*img3(x,y)[2]-sq(img3(x,y)[1]));
                shouldEqualTolerance(res(x,y)[2],
                                    VIGRA_CSTD::atan2((double)-y, (double)x), 1e-12);
            }
        }
    }

    void tensorTraceTest()
    {
        Image res(img3.size());
        tensorTrace(srcImageRange(img3), destImage(res));

        for(int y = 0; y < h; ++y)
        {
            for(int x = 0; x < w; ++x)
            {
                shouldEqual(res(x,y), img3(x,y)[0] + img3(x,y)[2]);
            }
        }
    }

    void tensorToEdgeCornerTest()
    {
        V2Image edge(img3.size());
        Image corner(img3.size());
        tensorToEdgeCorner(srcImageRange(img3), destImage(edge), destImage(corner));
        V3Image eigen(img3.size());
        tensorEigenRepresentation(srcImageRange(img3), destImage(eigen));

        for(int y = 0; y < h; ++y)
        {
            for(int x = 0; x < w; ++x)
            {
                shouldEqual(edge(x,y)[0], eigen(x,y)[0]-eigen(x,y)[1]);
                shouldEqual(edge(x,y)[1], eigen(x,y)[2]);
                shouldEqual(corner(x,y), 2.0*eigen(x,y)[1]);
            }
        }
    }

    int w, h;
    V2Image img2;
    V3Image img3;
};

#define defImpulseResponseTest(xorder, yorder) \
    void rieszTransform##xorder##yorder##Test() \
    { \
        Image res(img1.size()), ref(img1.size()); \
        ImageImportInfo iref("riesz" #xorder #yorder ".xv"); \
        importImage(iref, destImage(ref)); \
        \
        rieszTransformOfLOG(srcImageRange(img1), destImage(res), 2.0, xorder, yorder); \
        \
        shouldEqualSequenceTolerance(res.begin(), res.end(), ref.begin(), 1e-12); \
    } \

struct EdgeJunctionTensorTest
{
    typedef vigra::DImage Image;
    typedef vigra::DVector2Image V2Image;
    typedef vigra::DVector3Image V3Image;

    EdgeJunctionTensorTest()
    : img1(17, 17)
    {
        img1.init(0.0);
        img1(8,8) = 1.0;

        ImageImportInfo i2("l2.xv");
        img2.resize(i2.size());
        importImage(i2, destImage(img2));
    }

    defImpulseResponseTest(0, 0)
    defImpulseResponseTest(1, 0)
    defImpulseResponseTest(0, 1)
    defImpulseResponseTest(2, 0)
    defImpulseResponseTest(1, 1)
    defImpulseResponseTest(0, 2)

    void boundaryTensorTest0()
    {
        V3Image res(img1.size()), ref(img1.size());
        ImageImportInfo iref("boundaryTensor.xv");
        importImage(iref, destImage(ref));

        boundaryTensor(srcImageRange(img1), destImage(res), 2.0);

        for(V3Image::iterator i = res.begin(), j = ref.begin(); i < res.end(); ++i, ++j)
        {
            shouldEqualTolerance(i->magnitude(), j->magnitude(), 1e-12);
        }
    }

    void boundaryTensorTest1()
    {
        V3Image bt(img2.size());
        Image res(img2.size()), ref(img2.size());
        ImageImportInfo iref("l2_boundary1.xv");
        importImage(iref, destImage(ref));

        boundaryTensor1(srcImageRange(img2), destImage(bt), 2.0);
        tensorTrace(srcImageRange(bt), destImage(res));

        shouldEqualSequenceTolerance(res.begin(), res.end(), ref.begin(), 1e-12);
    }

    void boundaryTensorTest2()
    {
        V3Image bt(img2.size());
        Image res(img2.size()), ref(img2.size());
        ImageImportInfo iref("l2_boundary.xv");
        importImage(iref, destImage(ref));

        boundaryTensor(srcImageRange(img2), destImage(bt), 2.0);
        tensorTrace(srcImageRange(bt), destImage(res));

        shouldEqualSequenceTolerance(res.begin(), res.end(), ref.begin(), 1e-12);
    }

    void boundaryTensorTest3()
    {
        // does not produce the correct result
        Image res(img2.size());
        {
            V3Image bt(img2.size()), bt2(img2.size());

            boundaryTensor3(srcImageRange(img2), destImage(bt), destImage(bt2), 1.0);
            combineTwoImages(srcImageRange(bt), srcImage(bt2), destImage(bt2),
                         std::plus<V3Image::value_type>());
            tensorTrace(srcImageRange(bt2), destImage(res));
        }
        Image ref(img2.size());
        ImageImportInfo iref("l2_boundary3.xv");
        importImage(iref, destImage(ref));

        shouldEqualSequenceTolerance(res.begin(), res.end(), ref.begin(), 1e-12);
    }

    void hourglassTest()
    {
        V2Image gradient(img2.size());
        V3Image tensor(img2.size()), smoothedTensor(img2.size());
        Image res(img2.size()), ref(img2.size());
        ImageImportInfo iref("l2_hourglass.xv");
        importImage(iref, destImage(ref));

        gaussianGradient(srcImageRange(img2), destImage(gradient), 0.7);
        vectorToTensor(srcImageRange(gradient), destImage(tensor));
        hourGlassFilter(srcImageRange(tensor), destImage(smoothedTensor), 2.8, 0.4);
        tensorTrace(srcImageRange(smoothedTensor), destImage(res));

        shouldEqualSequenceTolerance(res.begin(), res.end(), ref.begin(), 1e-12);
    }

    void energyTensorTest()
    {
        V3Image get(img2.size());
        Image res(img2.size()), ref(img2.size());
        ImageImportInfo iref("l2_get.xv");
        importImage(iref, destImage(ref));

        Kernel1D<double> smooth, grad;
        smooth.initGaussian(1.0);
        grad.initGaussianDerivative(1.0, 1);
        gradientEnergyTensor(srcImageRange(img2), destImage(get), grad, smooth);
        tensorTrace(srcImageRange(get), destImage(res));

        shouldEqualSequenceTolerance(res.begin(), res.end(), ref.begin(), 1e-10);
    }

    Image img1, img2;
};

struct TensorTestSuite
: public vigra::test_suite
{
    TensorTestSuite()
    : vigra::test_suite("TensorTestSuite")
    {
        add( testCase( &TensorUtilityTest::vector2TensorTest));
        add( testCase( &TensorUtilityTest::tensorEigenRepresentationTest));
        add( testCase( &TensorUtilityTest::tensorTraceTest));
        add( testCase( &TensorUtilityTest::tensorToEdgeCornerTest));

        add( testCase( &EdgeJunctionTensorTest::rieszTransform00Test));
        add( testCase( &EdgeJunctionTensorTest::rieszTransform10Test));
        add( testCase( &EdgeJunctionTensorTest::rieszTransform01Test));
        add( testCase( &EdgeJunctionTensorTest::rieszTransform20Test));
        add( testCase( &EdgeJunctionTensorTest::rieszTransform11Test));
        add( testCase( &EdgeJunctionTensorTest::rieszTransform02Test));
        add( testCase( &EdgeJunctionTensorTest::boundaryTensorTest0));
        add( testCase( &EdgeJunctionTensorTest::boundaryTensorTest1));
        add( testCase( &EdgeJunctionTensorTest::boundaryTensorTest2));
        add( testCase( &EdgeJunctionTensorTest::hourglassTest));
        add( testCase( &EdgeJunctionTensorTest::energyTensorTest));
    }
};

int main()
{
    TensorTestSuite test;

    int failed = test.run();

    std::cout << test.report() << std::endl;

    return (failed != 0);
}

<|endoftext|>